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