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