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