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