NotificationManagerService.java revision e77bb36d48b6b8b5c3bb6a1195aca469bb237919
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.setCallback(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, boolean requested) { 1359 enforceSystemOrSystemUI("INotificationManager.requestZenModeConditions"); 1360 mConditionProviders.requestZenModeConditions(callback, requested); 1361 } 1362 1363 @Override 1364 public void setZenModeCondition(Uri conditionId) { 1365 enforceSystemOrSystemUI("INotificationManager.setZenModeCondition"); 1366 mConditionProviders.setZenModeCondition(conditionId); 1367 } 1368 1369 private void enforceSystemOrSystemUI(String message) { 1370 if (isCallerSystem()) return; 1371 getContext().enforceCallingPermission(android.Manifest.permission.STATUS_BAR_SERVICE, 1372 message); 1373 } 1374 1375 @Override 1376 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1377 if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP) 1378 != PackageManager.PERMISSION_GRANTED) { 1379 pw.println("Permission Denial: can't dump NotificationManager from from pid=" 1380 + Binder.getCallingPid() 1381 + ", uid=" + Binder.getCallingUid()); 1382 return; 1383 } 1384 1385 dumpImpl(pw); 1386 } 1387 }; 1388 1389 private String[] getActiveNotificationKeysFromListener(INotificationListener token) { 1390 synchronized (mNotificationList) { 1391 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token); 1392 final ArrayList<String> keys = new ArrayList<String>(); 1393 final int N = mNotificationList.size(); 1394 for (int i=0; i<N; i++) { 1395 final StatusBarNotification sbn = mNotificationList.get(i).sbn; 1396 if (info.enabledAndUserMatches(sbn.getUserId())) { 1397 keys.add(sbn.getKey()); 1398 } 1399 } 1400 return keys.toArray(new String[keys.size()]); 1401 } 1402 } 1403 1404 void dumpImpl(PrintWriter pw) { 1405 pw.println("Current Notification Manager state:"); 1406 1407 int N; 1408 1409 synchronized (mToastQueue) { 1410 N = mToastQueue.size(); 1411 if (N > 0) { 1412 pw.println(" Toast Queue:"); 1413 for (int i=0; i<N; i++) { 1414 mToastQueue.get(i).dump(pw, " "); 1415 } 1416 pw.println(" "); 1417 } 1418 1419 } 1420 1421 synchronized (mNotificationList) { 1422 N = mNotificationList.size(); 1423 if (N > 0) { 1424 pw.println(" Notification List:"); 1425 for (int i=0; i<N; i++) { 1426 mNotificationList.get(i).dump(pw, " ", getContext()); 1427 } 1428 pw.println(" "); 1429 } 1430 1431 N = mLights.size(); 1432 if (N > 0) { 1433 pw.println(" Lights List:"); 1434 for (int i=0; i<N; i++) { 1435 pw.println(" " + mLights.get(i)); 1436 } 1437 pw.println(" "); 1438 } 1439 1440 pw.println(" mSoundNotification=" + mSoundNotification); 1441 pw.println(" mVibrateNotification=" + mVibrateNotification); 1442 pw.println(" mDisableNotificationAlerts=" + mDisableNotificationAlerts); 1443 pw.println(" mSystemReady=" + mSystemReady); 1444 pw.println(" mArchive=" + mArchive.toString()); 1445 Iterator<StatusBarNotification> iter = mArchive.descendingIterator(); 1446 int i=0; 1447 while (iter.hasNext()) { 1448 pw.println(" " + iter.next()); 1449 if (++i >= 5) { 1450 if (iter.hasNext()) pw.println(" ..."); 1451 break; 1452 } 1453 } 1454 1455 pw.println("\n Usage Stats:"); 1456 mUsageStats.dump(pw, " "); 1457 1458 pw.println("\n Zen Mode:"); 1459 mZenModeHelper.dump(pw, " "); 1460 1461 pw.println("\n Notification listeners:"); 1462 mListeners.dump(pw); 1463 1464 pw.println("\n Condition providers:"); 1465 mConditionProviders.dump(pw); 1466 } 1467 } 1468 1469 /** 1470 * The private API only accessible to the system process. 1471 */ 1472 private final NotificationManagerInternal mInternalService = new NotificationManagerInternal() { 1473 @Override 1474 public void enqueueNotification(String pkg, String opPkg, int callingUid, int callingPid, 1475 String tag, int id, Notification notification, int[] idReceived, int userId) { 1476 enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, notification, 1477 idReceived, userId); 1478 } 1479 }; 1480 1481 void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid, 1482 final int callingPid, final String tag, final int id, final Notification notification, 1483 int[] idOut, int incomingUserId) { 1484 if (DBG) { 1485 Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id 1486 + " notification=" + notification); 1487 } 1488 checkCallerIsSystemOrSameApp(pkg); 1489 final boolean isSystemNotification = isUidSystem(callingUid) || ("android".equals(pkg)); 1490 1491 final int userId = ActivityManager.handleIncomingUser(callingPid, 1492 callingUid, incomingUserId, true, false, "enqueueNotification", pkg); 1493 final UserHandle user = new UserHandle(userId); 1494 1495 // Limit the number of notifications that any given package except the android 1496 // package can enqueue. Prevents DOS attacks and deals with leaks. 1497 if (!isSystemNotification) { 1498 synchronized (mNotificationList) { 1499 int count = 0; 1500 final int N = mNotificationList.size(); 1501 for (int i=0; i<N; i++) { 1502 final NotificationRecord r = mNotificationList.get(i); 1503 if (r.sbn.getPackageName().equals(pkg) && r.sbn.getUserId() == userId) { 1504 count++; 1505 if (count >= MAX_PACKAGE_NOTIFICATIONS) { 1506 Slog.e(TAG, "Package has already posted " + count 1507 + " notifications. Not showing more. package=" + pkg); 1508 return; 1509 } 1510 } 1511 } 1512 } 1513 } 1514 1515 // This conditional is a dirty hack to limit the logging done on 1516 // behalf of the download manager without affecting other apps. 1517 if (!pkg.equals("com.android.providers.downloads") 1518 || Log.isLoggable("DownloadManager", Log.VERBOSE)) { 1519 EventLogTags.writeNotificationEnqueue(callingUid, callingPid, 1520 pkg, id, tag, userId, notification.toString()); 1521 } 1522 1523 if (pkg == null || notification == null) { 1524 throw new IllegalArgumentException("null not allowed: pkg=" + pkg 1525 + " id=" + id + " notification=" + notification); 1526 } 1527 if (notification.icon != 0) { 1528 if (notification.contentView == null) { 1529 throw new IllegalArgumentException("contentView required: pkg=" + pkg 1530 + " id=" + id + " notification=" + notification); 1531 } 1532 } 1533 1534 mHandler.post(new Runnable() { 1535 @Override 1536 public void run() { 1537 1538 // === Scoring === 1539 1540 // 0. Sanitize inputs 1541 notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN, 1542 Notification.PRIORITY_MAX); 1543 // Migrate notification flags to scores 1544 if (0 != (notification.flags & Notification.FLAG_HIGH_PRIORITY)) { 1545 if (notification.priority < Notification.PRIORITY_MAX) { 1546 notification.priority = Notification.PRIORITY_MAX; 1547 } 1548 } else if (SCORE_ONGOING_HIGHER && 1549 0 != (notification.flags & Notification.FLAG_ONGOING_EVENT)) { 1550 if (notification.priority < Notification.PRIORITY_HIGH) { 1551 notification.priority = Notification.PRIORITY_HIGH; 1552 } 1553 } 1554 1555 // 1. initial score: buckets of 10, around the app 1556 int score = notification.priority * NOTIFICATION_PRIORITY_MULTIPLIER; //[-20..20] 1557 1558 // 2. Consult external heuristics (TBD) 1559 1560 // 3. Apply local rules 1561 1562 int initialScore = score; 1563 if (!mScorers.isEmpty()) { 1564 if (DBG) Slog.v(TAG, "Initial score is " + score + "."); 1565 for (NotificationScorer scorer : mScorers) { 1566 try { 1567 score = scorer.getScore(notification, score); 1568 } catch (Throwable t) { 1569 Slog.w(TAG, "Scorer threw on .getScore.", t); 1570 } 1571 } 1572 if (DBG) Slog.v(TAG, "Final score is " + score + "."); 1573 } 1574 1575 // add extra to indicate score modified by NotificationScorer 1576 notification.extras.putBoolean(Notification.EXTRA_SCORE_MODIFIED, 1577 score != initialScore); 1578 1579 // blocked apps 1580 if (ENABLE_BLOCKED_NOTIFICATIONS && !noteNotificationOp(pkg, callingUid)) { 1581 if (!isSystemNotification) { 1582 score = JUNK_SCORE; 1583 Slog.e(TAG, "Suppressing notification from package " + pkg 1584 + " by user request."); 1585 } 1586 } 1587 1588 if (DBG) { 1589 Slog.v(TAG, "Assigned score=" + score + " to " + notification); 1590 } 1591 1592 if (score < SCORE_DISPLAY_THRESHOLD) { 1593 // Notification will be blocked because the score is too low. 1594 return; 1595 } 1596 1597 // Is this notification intercepted by zen mode? 1598 final boolean intercept = mZenModeHelper.shouldIntercept(pkg, notification); 1599 notification.extras.putBoolean(EXTRA_INTERCEPT, intercept); 1600 1601 // Should this notification make noise, vibe, or use the LED? 1602 final boolean canInterrupt = (score >= SCORE_INTERRUPTION_THRESHOLD) && !intercept; 1603 if (DBG) Slog.v(TAG, "canInterrupt=" + canInterrupt + " intercept=" + intercept); 1604 synchronized (mNotificationList) { 1605 final StatusBarNotification n = new StatusBarNotification( 1606 pkg, opPkg, id, tag, callingUid, callingPid, score, notification, 1607 user); 1608 NotificationRecord r = new NotificationRecord(n); 1609 NotificationRecord old = null; 1610 1611 int index = indexOfNotificationLocked(pkg, tag, id, userId); 1612 if (index < 0) { 1613 mNotificationList.add(r); 1614 mUsageStats.registerPostedByApp(r); 1615 } else { 1616 old = mNotificationList.get(index); 1617 mNotificationList.set(index, r); 1618 mUsageStats.registerUpdatedByApp(r); 1619 // Make sure we don't lose the foreground service state. 1620 if (old != null) { 1621 notification.flags |= 1622 old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE; 1623 } 1624 } 1625 if (old != null) { 1626 mNotificationsByKey.remove(old.sbn.getKey()); 1627 } 1628 mNotificationsByKey.put(n.getKey(), r); 1629 1630 // Ensure if this is a foreground service that the proper additional 1631 // flags are set. 1632 if ((notification.flags&Notification.FLAG_FOREGROUND_SERVICE) != 0) { 1633 notification.flags |= Notification.FLAG_ONGOING_EVENT 1634 | Notification.FLAG_NO_CLEAR; 1635 } 1636 1637 final int currentUser; 1638 final long token = Binder.clearCallingIdentity(); 1639 try { 1640 currentUser = ActivityManager.getCurrentUser(); 1641 } finally { 1642 Binder.restoreCallingIdentity(token); 1643 } 1644 1645 if (notification.icon != 0) { 1646 if (old != null && old.statusBarKey != null) { 1647 r.statusBarKey = old.statusBarKey; 1648 final long identity = Binder.clearCallingIdentity(); 1649 try { 1650 mStatusBar.updateNotification(r.statusBarKey, n); 1651 } finally { 1652 Binder.restoreCallingIdentity(identity); 1653 } 1654 } else { 1655 final long identity = Binder.clearCallingIdentity(); 1656 try { 1657 r.statusBarKey = mStatusBar.addNotification(n); 1658 if ((n.getNotification().flags & Notification.FLAG_SHOW_LIGHTS) != 0 1659 && canInterrupt) { 1660 mAttentionLight.pulse(); 1661 } 1662 } finally { 1663 Binder.restoreCallingIdentity(identity); 1664 } 1665 } 1666 // Send accessibility events only for the current user. 1667 if (currentUser == userId) { 1668 sendAccessibilityEvent(notification, pkg); 1669 } 1670 1671 mListeners.notifyPostedLocked(r.sbn); 1672 } else { 1673 Slog.e(TAG, "Not posting notification with icon==0: " + notification); 1674 if (old != null && old.statusBarKey != null) { 1675 final long identity = Binder.clearCallingIdentity(); 1676 try { 1677 mStatusBar.removeNotification(old.statusBarKey); 1678 } finally { 1679 Binder.restoreCallingIdentity(identity); 1680 } 1681 1682 mListeners.notifyRemovedLocked(r.sbn); 1683 } 1684 // ATTENTION: in a future release we will bail out here 1685 // so that we do not play sounds, show lights, etc. for invalid 1686 // notifications 1687 Slog.e(TAG, "WARNING: In a future release this will crash the app: " 1688 + n.getPackageName()); 1689 } 1690 1691 // If we're not supposed to beep, vibrate, etc. then don't. 1692 if (!mDisableNotificationAlerts 1693 && (!(old != null 1694 && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 )) 1695 && (r.getUserId() == UserHandle.USER_ALL || 1696 (r.getUserId() == userId && r.getUserId() == currentUser) || 1697 mUserProfiles.isCurrentProfile(r.getUserId())) 1698 && canInterrupt 1699 && mSystemReady 1700 && mAudioManager != null) { 1701 if (DBG) Slog.v(TAG, "Interrupting!"); 1702 // sound 1703 1704 // should we use the default notification sound? (indicated either by 1705 // DEFAULT_SOUND or because notification.sound is pointing at 1706 // Settings.System.NOTIFICATION_SOUND) 1707 final boolean useDefaultSound = 1708 (notification.defaults & Notification.DEFAULT_SOUND) != 0 || 1709 Settings.System.DEFAULT_NOTIFICATION_URI 1710 .equals(notification.sound); 1711 1712 Uri soundUri = null; 1713 boolean hasValidSound = false; 1714 1715 if (useDefaultSound) { 1716 soundUri = Settings.System.DEFAULT_NOTIFICATION_URI; 1717 1718 // check to see if the default notification sound is silent 1719 ContentResolver resolver = getContext().getContentResolver(); 1720 hasValidSound = Settings.System.getString(resolver, 1721 Settings.System.NOTIFICATION_SOUND) != null; 1722 } else if (notification.sound != null) { 1723 soundUri = notification.sound; 1724 hasValidSound = (soundUri != null); 1725 } 1726 1727 if (hasValidSound) { 1728 boolean looping = 1729 (notification.flags & Notification.FLAG_INSISTENT) != 0; 1730 int audioStreamType; 1731 if (notification.audioStreamType >= 0) { 1732 audioStreamType = notification.audioStreamType; 1733 } else { 1734 audioStreamType = DEFAULT_STREAM_TYPE; 1735 } 1736 mSoundNotification = r; 1737 // do not play notifications if stream volume is 0 (typically because 1738 // ringer mode is silent) or if there is a user of exclusive audio focus 1739 if ((mAudioManager.getStreamVolume(audioStreamType) != 0) 1740 && !mAudioManager.isAudioFocusExclusive()) { 1741 final long identity = Binder.clearCallingIdentity(); 1742 try { 1743 final IRingtonePlayer player = 1744 mAudioManager.getRingtonePlayer(); 1745 if (player != null) { 1746 if (DBG) Slog.v(TAG, "Playing sound " + soundUri 1747 + " on stream " + audioStreamType); 1748 player.playAsync(soundUri, user, looping, audioStreamType); 1749 } 1750 } catch (RemoteException e) { 1751 } finally { 1752 Binder.restoreCallingIdentity(identity); 1753 } 1754 } 1755 } 1756 1757 // vibrate 1758 // Does the notification want to specify its own vibration? 1759 final boolean hasCustomVibrate = notification.vibrate != null; 1760 1761 // new in 4.2: if there was supposed to be a sound and we're in vibrate 1762 // mode, and no other vibration is specified, we fall back to vibration 1763 final boolean convertSoundToVibration = 1764 !hasCustomVibrate 1765 && hasValidSound 1766 && (mAudioManager.getRingerMode() 1767 == AudioManager.RINGER_MODE_VIBRATE); 1768 1769 // The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback. 1770 final boolean useDefaultVibrate = 1771 (notification.defaults & Notification.DEFAULT_VIBRATE) != 0; 1772 1773 if ((useDefaultVibrate || convertSoundToVibration || hasCustomVibrate) 1774 && !(mAudioManager.getRingerMode() 1775 == AudioManager.RINGER_MODE_SILENT)) { 1776 mVibrateNotification = r; 1777 1778 if (useDefaultVibrate || convertSoundToVibration) { 1779 // Escalate privileges so we can use the vibrator even if the 1780 // notifying app does not have the VIBRATE permission. 1781 long identity = Binder.clearCallingIdentity(); 1782 try { 1783 mVibrator.vibrate(r.sbn.getUid(), r.sbn.getOpPkg(), 1784 useDefaultVibrate ? mDefaultVibrationPattern 1785 : mFallbackVibrationPattern, 1786 ((notification.flags & Notification.FLAG_INSISTENT) != 0) 1787 ? 0: -1, notification.audioStreamType); 1788 } finally { 1789 Binder.restoreCallingIdentity(identity); 1790 } 1791 } else if (notification.vibrate.length > 1) { 1792 // If you want your own vibration pattern, you need the VIBRATE 1793 // permission 1794 mVibrator.vibrate(r.sbn.getUid(), r.sbn.getOpPkg(), 1795 notification.vibrate, 1796 ((notification.flags & Notification.FLAG_INSISTENT) != 0) 1797 ? 0: -1, notification.audioStreamType); 1798 } 1799 } 1800 } 1801 1802 // light 1803 // the most recent thing gets the light 1804 mLights.remove(old); 1805 if (mLedNotification == old) { 1806 mLedNotification = null; 1807 } 1808 //Slog.i(TAG, "notification.lights=" 1809 // + ((old.notification.lights.flags & Notification.FLAG_SHOW_LIGHTS) 1810 // != 0)); 1811 if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0 1812 && canInterrupt) { 1813 mLights.add(r); 1814 updateLightsLocked(); 1815 } else { 1816 if (old != null 1817 && ((old.getFlags() & Notification.FLAG_SHOW_LIGHTS) != 0)) { 1818 updateLightsLocked(); 1819 } 1820 } 1821 } 1822 } 1823 }); 1824 1825 idOut[0] = id; 1826 } 1827 1828 void showNextToastLocked() { 1829 ToastRecord record = mToastQueue.get(0); 1830 while (record != null) { 1831 if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback); 1832 try { 1833 record.callback.show(); 1834 scheduleTimeoutLocked(record); 1835 return; 1836 } catch (RemoteException e) { 1837 Slog.w(TAG, "Object died trying to show notification " + record.callback 1838 + " in package " + record.pkg); 1839 // remove it from the list and let the process die 1840 int index = mToastQueue.indexOf(record); 1841 if (index >= 0) { 1842 mToastQueue.remove(index); 1843 } 1844 keepProcessAliveLocked(record.pid); 1845 if (mToastQueue.size() > 0) { 1846 record = mToastQueue.get(0); 1847 } else { 1848 record = null; 1849 } 1850 } 1851 } 1852 } 1853 1854 void cancelToastLocked(int index) { 1855 ToastRecord record = mToastQueue.get(index); 1856 try { 1857 record.callback.hide(); 1858 } catch (RemoteException e) { 1859 Slog.w(TAG, "Object died trying to hide notification " + record.callback 1860 + " in package " + record.pkg); 1861 // don't worry about this, we're about to remove it from 1862 // the list anyway 1863 } 1864 mToastQueue.remove(index); 1865 keepProcessAliveLocked(record.pid); 1866 if (mToastQueue.size() > 0) { 1867 // Show the next one. If the callback fails, this will remove 1868 // it from the list, so don't assume that the list hasn't changed 1869 // after this point. 1870 showNextToastLocked(); 1871 } 1872 } 1873 1874 private void scheduleTimeoutLocked(ToastRecord r) 1875 { 1876 mHandler.removeCallbacksAndMessages(r); 1877 Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r); 1878 long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY; 1879 mHandler.sendMessageDelayed(m, delay); 1880 } 1881 1882 private void handleTimeout(ToastRecord record) 1883 { 1884 if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback); 1885 synchronized (mToastQueue) { 1886 int index = indexOfToastLocked(record.pkg, record.callback); 1887 if (index >= 0) { 1888 cancelToastLocked(index); 1889 } 1890 } 1891 } 1892 1893 // lock on mToastQueue 1894 int indexOfToastLocked(String pkg, ITransientNotification callback) 1895 { 1896 IBinder cbak = callback.asBinder(); 1897 ArrayList<ToastRecord> list = mToastQueue; 1898 int len = list.size(); 1899 for (int i=0; i<len; i++) { 1900 ToastRecord r = list.get(i); 1901 if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) { 1902 return i; 1903 } 1904 } 1905 return -1; 1906 } 1907 1908 // lock on mToastQueue 1909 void keepProcessAliveLocked(int pid) 1910 { 1911 int toastCount = 0; // toasts from this pid 1912 ArrayList<ToastRecord> list = mToastQueue; 1913 int N = list.size(); 1914 for (int i=0; i<N; i++) { 1915 ToastRecord r = list.get(i); 1916 if (r.pid == pid) { 1917 toastCount++; 1918 } 1919 } 1920 try { 1921 mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0); 1922 } catch (RemoteException e) { 1923 // Shouldn't happen. 1924 } 1925 } 1926 1927 private final class WorkerHandler extends Handler 1928 { 1929 @Override 1930 public void handleMessage(Message msg) 1931 { 1932 switch (msg.what) 1933 { 1934 case MESSAGE_TIMEOUT: 1935 handleTimeout((ToastRecord)msg.obj); 1936 break; 1937 case MESSAGE_SAVE_POLICY_FILE: 1938 handleSavePolicyFile(); 1939 break; 1940 } 1941 } 1942 } 1943 1944 1945 // Notifications 1946 // ============================================================================ 1947 static int clamp(int x, int low, int high) { 1948 return (x < low) ? low : ((x > high) ? high : x); 1949 } 1950 1951 void sendAccessibilityEvent(Notification notification, CharSequence packageName) { 1952 AccessibilityManager manager = AccessibilityManager.getInstance(getContext()); 1953 if (!manager.isEnabled()) { 1954 return; 1955 } 1956 1957 AccessibilityEvent event = 1958 AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED); 1959 event.setPackageName(packageName); 1960 event.setClassName(Notification.class.getName()); 1961 event.setParcelableData(notification); 1962 CharSequence tickerText = notification.tickerText; 1963 if (!TextUtils.isEmpty(tickerText)) { 1964 event.getText().add(tickerText); 1965 } 1966 1967 manager.sendAccessibilityEvent(event); 1968 } 1969 1970 private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason) { 1971 // tell the app 1972 if (sendDelete) { 1973 if (r.getNotification().deleteIntent != null) { 1974 try { 1975 r.getNotification().deleteIntent.send(); 1976 } catch (PendingIntent.CanceledException ex) { 1977 // do nothing - there's no relevant way to recover, and 1978 // no reason to let this propagate 1979 Slog.w(TAG, "canceled PendingIntent for " + r.sbn.getPackageName(), ex); 1980 } 1981 } 1982 } 1983 1984 // status bar 1985 if (r.getNotification().icon != 0) { 1986 final long identity = Binder.clearCallingIdentity(); 1987 try { 1988 mStatusBar.removeNotification(r.statusBarKey); 1989 } finally { 1990 Binder.restoreCallingIdentity(identity); 1991 } 1992 r.statusBarKey = null; 1993 mListeners.notifyRemovedLocked(r.sbn); 1994 } 1995 1996 // sound 1997 if (mSoundNotification == r) { 1998 mSoundNotification = null; 1999 final long identity = Binder.clearCallingIdentity(); 2000 try { 2001 final IRingtonePlayer player = mAudioManager.getRingtonePlayer(); 2002 if (player != null) { 2003 player.stopAsync(); 2004 } 2005 } catch (RemoteException e) { 2006 } finally { 2007 Binder.restoreCallingIdentity(identity); 2008 } 2009 } 2010 2011 // vibrate 2012 if (mVibrateNotification == r) { 2013 mVibrateNotification = null; 2014 long identity = Binder.clearCallingIdentity(); 2015 try { 2016 mVibrator.cancel(); 2017 } 2018 finally { 2019 Binder.restoreCallingIdentity(identity); 2020 } 2021 } 2022 2023 // light 2024 mLights.remove(r); 2025 if (mLedNotification == r) { 2026 mLedNotification = null; 2027 } 2028 2029 // Record usage stats 2030 switch (reason) { 2031 case REASON_DELEGATE_CANCEL: 2032 case REASON_DELEGATE_CANCEL_ALL: 2033 case REASON_LISTENER_CANCEL: 2034 case REASON_LISTENER_CANCEL_ALL: 2035 mUsageStats.registerDismissedByUser(r); 2036 break; 2037 case REASON_NOMAN_CANCEL: 2038 case REASON_NOMAN_CANCEL_ALL: 2039 mUsageStats.registerRemovedByApp(r); 2040 break; 2041 case REASON_DELEGATE_CLICK: 2042 mUsageStats.registerCancelDueToClick(r); 2043 break; 2044 default: 2045 mUsageStats.registerCancelUnknown(r); 2046 break; 2047 } 2048 2049 // Save it for users of getHistoricalNotifications() 2050 mArchive.record(r.sbn); 2051 } 2052 2053 /** 2054 * Cancels a notification ONLY if it has all of the {@code mustHaveFlags} 2055 * and none of the {@code mustNotHaveFlags}. 2056 */ 2057 void cancelNotification(final int callingUid, final int callingPid, 2058 final String pkg, final String tag, final int id, 2059 final int mustHaveFlags, final int mustNotHaveFlags, final boolean sendDelete, 2060 final int userId, final int reason, final ManagedServiceInfo listener) { 2061 // In enqueueNotificationInternal notifications are added by scheduling the 2062 // work on the worker handler. Hence, we also schedule the cancel on this 2063 // handler to avoid a scenario where an add notification call followed by a 2064 // remove notification call ends up in not removing the notification. 2065 mHandler.post(new Runnable() { 2066 @Override 2067 public void run() { 2068 EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, id, tag, userId, 2069 mustHaveFlags, mustNotHaveFlags, reason, 2070 listener == null ? null : listener.component.toShortString()); 2071 2072 synchronized (mNotificationList) { 2073 int index = indexOfNotificationLocked(pkg, tag, id, userId); 2074 if (index >= 0) { 2075 NotificationRecord r = mNotificationList.get(index); 2076 2077 // Ideally we'd do this in the caller of this method. However, that would 2078 // require the caller to also find the notification. 2079 if (reason == REASON_DELEGATE_CLICK) { 2080 mUsageStats.registerClickedByUser(r); 2081 } 2082 2083 if ((r.getNotification().flags & mustHaveFlags) != mustHaveFlags) { 2084 return; 2085 } 2086 if ((r.getNotification().flags & mustNotHaveFlags) != 0) { 2087 return; 2088 } 2089 2090 mNotificationList.remove(index); 2091 mNotificationsByKey.remove(r.sbn.getKey()); 2092 2093 cancelNotificationLocked(r, sendDelete, reason); 2094 updateLightsLocked(); 2095 } 2096 } 2097 } 2098 }); 2099 } 2100 2101 /** 2102 * Determine whether the userId applies to the notification in question, either because 2103 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard). 2104 */ 2105 private boolean notificationMatchesUserId(NotificationRecord r, int userId) { 2106 return 2107 // looking for USER_ALL notifications? match everything 2108 userId == UserHandle.USER_ALL 2109 // a notification sent to USER_ALL matches any query 2110 || r.getUserId() == UserHandle.USER_ALL 2111 // an exact user match 2112 || r.getUserId() == userId; 2113 } 2114 2115 /** 2116 * Determine whether the userId applies to the notification in question, either because 2117 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard) or 2118 * because it matches one of the users profiles. 2119 */ 2120 private boolean notificationMatchesCurrentProfiles(NotificationRecord r, int userId) { 2121 return notificationMatchesUserId(r, userId) 2122 || mUserProfiles.isCurrentProfile(r.getUserId()); 2123 } 2124 2125 /** 2126 * Cancels all notifications from a given package that have all of the 2127 * {@code mustHaveFlags}. 2128 */ 2129 boolean cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, int mustHaveFlags, 2130 int mustNotHaveFlags, boolean doit, int userId, int reason, 2131 ManagedServiceInfo listener) { 2132 EventLogTags.writeNotificationCancelAll(callingUid, callingPid, 2133 pkg, userId, mustHaveFlags, mustNotHaveFlags, reason, 2134 listener == null ? null : listener.component.toShortString()); 2135 2136 synchronized (mNotificationList) { 2137 final int N = mNotificationList.size(); 2138 boolean canceledSomething = false; 2139 for (int i = N-1; i >= 0; --i) { 2140 NotificationRecord r = mNotificationList.get(i); 2141 if (!notificationMatchesUserId(r, userId)) { 2142 continue; 2143 } 2144 // Don't remove notifications to all, if there's no package name specified 2145 if (r.getUserId() == UserHandle.USER_ALL && pkg == null) { 2146 continue; 2147 } 2148 if ((r.getFlags() & mustHaveFlags) != mustHaveFlags) { 2149 continue; 2150 } 2151 if ((r.getFlags() & mustNotHaveFlags) != 0) { 2152 continue; 2153 } 2154 if (pkg != null && !r.sbn.getPackageName().equals(pkg)) { 2155 continue; 2156 } 2157 canceledSomething = true; 2158 if (!doit) { 2159 return true; 2160 } 2161 mNotificationList.remove(i); 2162 mNotificationsByKey.remove(r.sbn.getKey()); 2163 cancelNotificationLocked(r, false, reason); 2164 } 2165 if (canceledSomething) { 2166 updateLightsLocked(); 2167 } 2168 return canceledSomething; 2169 } 2170 } 2171 2172 void cancelAllLocked(int callingUid, int callingPid, int userId, int reason, 2173 ManagedServiceInfo listener, boolean includeCurrentProfiles) { 2174 EventLogTags.writeNotificationCancelAll(callingUid, callingPid, 2175 null, userId, 0, 0, reason, 2176 listener == null ? null : listener.component.toShortString()); 2177 2178 final int N = mNotificationList.size(); 2179 for (int i=N-1; i>=0; i--) { 2180 NotificationRecord r = mNotificationList.get(i); 2181 if (includeCurrentProfiles) { 2182 if (!notificationMatchesCurrentProfiles(r, userId)) { 2183 continue; 2184 } 2185 } else { 2186 if (!notificationMatchesUserId(r, userId)) { 2187 continue; 2188 } 2189 } 2190 2191 if ((r.getFlags() & (Notification.FLAG_ONGOING_EVENT 2192 | Notification.FLAG_NO_CLEAR)) == 0) { 2193 mNotificationList.remove(i); 2194 mNotificationsByKey.remove(r.sbn.getKey()); 2195 cancelNotificationLocked(r, true, reason); 2196 } 2197 } 2198 updateLightsLocked(); 2199 } 2200 2201 // lock on mNotificationList 2202 void updateLightsLocked() 2203 { 2204 // handle notification lights 2205 if (mLedNotification == null) { 2206 // get next notification, if any 2207 int n = mLights.size(); 2208 if (n > 0) { 2209 mLedNotification = mLights.get(n-1); 2210 } 2211 } 2212 2213 // Don't flash while we are in a call or screen is on 2214 if (mLedNotification == null || mInCall || mScreenOn) { 2215 mNotificationLight.turnOff(); 2216 } else { 2217 final Notification ledno = mLedNotification.sbn.getNotification(); 2218 int ledARGB = ledno.ledARGB; 2219 int ledOnMS = ledno.ledOnMS; 2220 int ledOffMS = ledno.ledOffMS; 2221 if ((ledno.defaults & Notification.DEFAULT_LIGHTS) != 0) { 2222 ledARGB = mDefaultNotificationColor; 2223 ledOnMS = mDefaultNotificationLedOn; 2224 ledOffMS = mDefaultNotificationLedOff; 2225 } 2226 if (mNotificationPulseEnabled) { 2227 // pulse repeatedly 2228 mNotificationLight.setFlashing(ledARGB, Light.LIGHT_FLASH_TIMED, 2229 ledOnMS, ledOffMS); 2230 } 2231 } 2232 } 2233 2234 // lock on mNotificationList 2235 int indexOfNotificationLocked(String pkg, String tag, int id, int userId) 2236 { 2237 ArrayList<NotificationRecord> list = mNotificationList; 2238 final int len = list.size(); 2239 for (int i=0; i<len; i++) { 2240 NotificationRecord r = list.get(i); 2241 if (!notificationMatchesUserId(r, userId) || r.sbn.getId() != id) { 2242 continue; 2243 } 2244 if (tag == null) { 2245 if (r.sbn.getTag() != null) { 2246 continue; 2247 } 2248 } else { 2249 if (!tag.equals(r.sbn.getTag())) { 2250 continue; 2251 } 2252 } 2253 if (r.sbn.getPackageName().equals(pkg)) { 2254 return i; 2255 } 2256 } 2257 return -1; 2258 } 2259 2260 private void updateNotificationPulse() { 2261 synchronized (mNotificationList) { 2262 updateLightsLocked(); 2263 } 2264 } 2265 2266 private static boolean isUidSystem(int uid) { 2267 final int appid = UserHandle.getAppId(uid); 2268 return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0); 2269 } 2270 2271 private static boolean isCallerSystem() { 2272 return isUidSystem(Binder.getCallingUid()); 2273 } 2274 2275 private static void checkCallerIsSystem() { 2276 if (isCallerSystem()) { 2277 return; 2278 } 2279 throw new SecurityException("Disallowed call for uid " + Binder.getCallingUid()); 2280 } 2281 2282 private static void checkCallerIsSystemOrSameApp(String pkg) { 2283 if (isCallerSystem()) { 2284 return; 2285 } 2286 final int uid = Binder.getCallingUid(); 2287 try { 2288 ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo( 2289 pkg, 0, UserHandle.getCallingUserId()); 2290 if (!UserHandle.isSameApp(ai.uid, uid)) { 2291 throw new SecurityException("Calling uid " + uid + " gave package" 2292 + pkg + " which is owned by uid " + ai.uid); 2293 } 2294 } catch (RemoteException re) { 2295 throw new SecurityException("Unknown package " + pkg + "\n" + re); 2296 } 2297 } 2298 2299 public class NotificationListeners extends ManagedServices { 2300 2301 public NotificationListeners() { 2302 super(getContext(), mHandler, mNotificationList, mUserProfiles); 2303 } 2304 2305 @Override 2306 protected Config getConfig() { 2307 Config c = new Config(); 2308 c.caption = "notification listener"; 2309 c.serviceInterface = NotificationListenerService.SERVICE_INTERFACE; 2310 c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_LISTENERS; 2311 c.bindPermission = android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE; 2312 c.settingsAction = Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS; 2313 c.clientLabel = R.string.notification_listener_binding_label; 2314 return c; 2315 } 2316 2317 @Override 2318 protected IInterface asInterface(IBinder binder) { 2319 return INotificationListener.Stub.asInterface(binder); 2320 } 2321 2322 @Override 2323 public void onServiceAdded(IInterface service) { 2324 final INotificationListener listener = (INotificationListener) service; 2325 final String[] keys = getActiveNotificationKeysFromListener(listener); 2326 try { 2327 listener.onListenerConnected(keys); 2328 } catch (RemoteException e) { 2329 // we tried 2330 } 2331 } 2332 2333 /** 2334 * asynchronously notify all listeners about a new notification 2335 */ 2336 public void notifyPostedLocked(StatusBarNotification sbn) { 2337 // make a copy in case changes are made to the underlying Notification object 2338 final StatusBarNotification sbnClone = sbn.clone(); 2339 for (final ManagedServiceInfo info : mServices) { 2340 mHandler.post(new Runnable() { 2341 @Override 2342 public void run() { 2343 notifyPostedIfUserMatch(info, sbnClone); 2344 } 2345 }); 2346 } 2347 } 2348 2349 /** 2350 * asynchronously notify all listeners about a removed notification 2351 */ 2352 public void notifyRemovedLocked(StatusBarNotification sbn) { 2353 // make a copy in case changes are made to the underlying Notification object 2354 // NOTE: this copy is lightweight: it doesn't include heavyweight parts of the 2355 // notification 2356 final StatusBarNotification sbnLight = sbn.cloneLight(); 2357 for (ManagedServiceInfo serviceInfo : mServices) { 2358 final ManagedServiceInfo info = (ManagedServiceInfo) serviceInfo; 2359 mHandler.post(new Runnable() { 2360 @Override 2361 public void run() { 2362 notifyRemovedIfUserMatch(info, sbnLight); 2363 } 2364 }); 2365 } 2366 } 2367 2368 private void notifyPostedIfUserMatch(ManagedServiceInfo info, StatusBarNotification sbn) { 2369 if (!info.enabledAndUserMatches(sbn.getUserId())) { 2370 return; 2371 } 2372 final INotificationListener listener = (INotificationListener)info.service; 2373 try { 2374 listener.onNotificationPosted(sbn); 2375 } catch (RemoteException ex) { 2376 Log.e(TAG, "unable to notify listener (posted): " + listener, ex); 2377 } 2378 } 2379 2380 private void notifyRemovedIfUserMatch(ManagedServiceInfo info, StatusBarNotification sbn) { 2381 if (!info.enabledAndUserMatches(sbn.getUserId())) { 2382 return; 2383 } 2384 final INotificationListener listener = (INotificationListener)info.service; 2385 try { 2386 listener.onNotificationRemoved(sbn); 2387 } catch (RemoteException ex) { 2388 Log.e(TAG, "unable to notify listener (removed): " + listener, ex); 2389 } 2390 } 2391 } 2392} 2393