NotificationManagerService.java revision c2346134bb519a54d50655cbef940fc3fdec60a9
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; 18 19import com.android.internal.statusbar.StatusBarNotification; 20 21import android.app.ActivityManagerNative; 22import android.app.IActivityManager; 23import android.app.INotificationManager; 24import android.app.ITransientNotification; 25import android.app.Notification; 26import android.app.NotificationManager; 27import android.app.PendingIntent; 28import android.app.StatusBarManager; 29import android.content.BroadcastReceiver; 30import android.content.ContentResolver; 31import android.content.Context; 32import android.content.Intent; 33import android.content.IntentFilter; 34import android.content.pm.ApplicationInfo; 35import android.content.pm.PackageManager; 36import android.content.pm.PackageManager.NameNotFoundException; 37import android.content.res.Resources; 38import android.database.ContentObserver; 39import android.media.AudioManager; 40import android.net.Uri; 41import android.os.Binder; 42import android.os.Bundle; 43import android.os.Handler; 44import android.os.IBinder; 45import android.os.Message; 46import android.os.Process; 47import android.os.RemoteException; 48import android.os.UserId; 49import android.os.Vibrator; 50import android.provider.Settings; 51import android.telephony.TelephonyManager; 52import android.text.TextUtils; 53import android.util.EventLog; 54import android.util.Log; 55import android.util.Slog; 56import android.view.accessibility.AccessibilityEvent; 57import android.view.accessibility.AccessibilityManager; 58import android.widget.Toast; 59 60import java.io.FileDescriptor; 61import java.io.PrintWriter; 62import java.util.ArrayList; 63import java.util.Arrays; 64 65/** {@hide} */ 66public class NotificationManagerService extends INotificationManager.Stub 67{ 68 private static final String TAG = "NotificationService"; 69 private static final boolean DBG = false; 70 71 private static final int MAX_PACKAGE_NOTIFICATIONS = 50; 72 73 // message codes 74 private static final int MESSAGE_TIMEOUT = 2; 75 76 private static final int LONG_DELAY = 3500; // 3.5 seconds 77 private static final int SHORT_DELAY = 2000; // 2 seconds 78 79 private static final long[] DEFAULT_VIBRATE_PATTERN = {0, 250, 250, 250}; 80 81 private static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_NOTIFICATION; 82 private static final boolean SCORE_ONGOING_HIGHER = false; 83 84 final Context mContext; 85 final IActivityManager mAm; 86 final IBinder mForegroundToken = new Binder(); 87 88 private WorkerHandler mHandler; 89 private StatusBarManagerService mStatusBar; 90 private LightsService.Light mNotificationLight; 91 private LightsService.Light mAttentionLight; 92 93 private int mDefaultNotificationColor; 94 private int mDefaultNotificationLedOn; 95 private int mDefaultNotificationLedOff; 96 97 private NotificationRecord mSoundNotification; 98 private NotificationPlayer mSound; 99 private boolean mSystemReady; 100 private int mDisabledNotifications; 101 102 private NotificationRecord mVibrateNotification; 103 private Vibrator mVibrator; 104 105 // for enabling and disabling notification pulse behavior 106 private boolean mScreenOn = true; 107 private boolean mInCall = false; 108 private boolean mNotificationPulseEnabled; 109 110 private final ArrayList<NotificationRecord> mNotificationList = 111 new ArrayList<NotificationRecord>(); 112 113 private ArrayList<ToastRecord> mToastQueue; 114 115 private ArrayList<NotificationRecord> mLights = new ArrayList<NotificationRecord>(); 116 private NotificationRecord mLedNotification; 117 118 private static String idDebugString(Context baseContext, String packageName, int id) { 119 Context c = null; 120 121 if (packageName != null) { 122 try { 123 c = baseContext.createPackageContext(packageName, 0); 124 } catch (NameNotFoundException e) { 125 c = baseContext; 126 } 127 } else { 128 c = baseContext; 129 } 130 131 String pkg; 132 String type; 133 String name; 134 135 Resources r = c.getResources(); 136 try { 137 return r.getResourceName(id); 138 } catch (Resources.NotFoundException e) { 139 return "<name unknown>"; 140 } 141 } 142 143 private static final class NotificationRecord 144 { 145 final String pkg; 146 final String tag; 147 final int id; 148 final int uid; 149 final int initialPid; 150 final Notification notification; 151 final int score; 152 IBinder statusBarKey; 153 154 NotificationRecord(String pkg, String tag, int id, int uid, int initialPid, int score, Notification notification) 155 { 156 this.pkg = pkg; 157 this.tag = tag; 158 this.id = id; 159 this.uid = uid; 160 this.initialPid = initialPid; 161 this.score = score; 162 this.notification = notification; 163 } 164 165 void dump(PrintWriter pw, String prefix, Context baseContext) { 166 pw.println(prefix + this); 167 pw.println(prefix + " icon=0x" + Integer.toHexString(notification.icon) 168 + " / " + idDebugString(baseContext, this.pkg, notification.icon)); 169 pw.println(prefix + " pri=" + notification.priority); 170 pw.println(prefix + " score=" + this.score); 171 pw.println(prefix + " contentIntent=" + notification.contentIntent); 172 pw.println(prefix + " deleteIntent=" + notification.deleteIntent); 173 pw.println(prefix + " tickerText=" + notification.tickerText); 174 pw.println(prefix + " contentView=" + notification.contentView); 175 pw.println(prefix + " uid=" + uid); 176 pw.println(prefix + " defaults=0x" + Integer.toHexString(notification.defaults)); 177 pw.println(prefix + " flags=0x" + Integer.toHexString(notification.flags)); 178 pw.println(prefix + " sound=" + notification.sound); 179 pw.println(prefix + " vibrate=" + Arrays.toString(notification.vibrate)); 180 pw.println(prefix + " ledARGB=0x" + Integer.toHexString(notification.ledARGB) 181 + " ledOnMS=" + notification.ledOnMS 182 + " ledOffMS=" + notification.ledOffMS); 183 } 184 185 @Override 186 public final String toString() 187 { 188 return "NotificationRecord{" 189 + Integer.toHexString(System.identityHashCode(this)) 190 + " pkg=" + pkg 191 + " id=" + Integer.toHexString(id) 192 + " tag=" + tag 193 + " score=" + score 194 + "}"; 195 } 196 } 197 198 private static final class ToastRecord 199 { 200 final int pid; 201 final String pkg; 202 final ITransientNotification callback; 203 int duration; 204 205 ToastRecord(int pid, String pkg, ITransientNotification callback, int duration) 206 { 207 this.pid = pid; 208 this.pkg = pkg; 209 this.callback = callback; 210 this.duration = duration; 211 } 212 213 void update(int duration) { 214 this.duration = duration; 215 } 216 217 void dump(PrintWriter pw, String prefix) { 218 pw.println(prefix + this); 219 } 220 221 @Override 222 public final String toString() 223 { 224 return "ToastRecord{" 225 + Integer.toHexString(System.identityHashCode(this)) 226 + " pkg=" + pkg 227 + " callback=" + callback 228 + " duration=" + duration; 229 } 230 } 231 232 private StatusBarManagerService.NotificationCallbacks mNotificationCallbacks 233 = new StatusBarManagerService.NotificationCallbacks() { 234 235 public void onSetDisabled(int status) { 236 synchronized (mNotificationList) { 237 mDisabledNotifications = status; 238 if ((mDisabledNotifications & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) { 239 // cancel whatever's going on 240 long identity = Binder.clearCallingIdentity(); 241 try { 242 mSound.stop(); 243 } 244 finally { 245 Binder.restoreCallingIdentity(identity); 246 } 247 248 identity = Binder.clearCallingIdentity(); 249 try { 250 mVibrator.cancel(); 251 } 252 finally { 253 Binder.restoreCallingIdentity(identity); 254 } 255 } 256 } 257 } 258 259 public void onClearAll() { 260 cancelAll(); 261 } 262 263 public void onNotificationClick(String pkg, String tag, int id) { 264 cancelNotification(pkg, tag, id, Notification.FLAG_AUTO_CANCEL, 265 Notification.FLAG_FOREGROUND_SERVICE, false); 266 } 267 268 public void onNotificationClear(String pkg, String tag, int id) { 269 cancelNotification(pkg, tag, id, 0, 270 Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE, 271 true); 272 } 273 274 public void onPanelRevealed() { 275 synchronized (mNotificationList) { 276 // sound 277 mSoundNotification = null; 278 long identity = Binder.clearCallingIdentity(); 279 try { 280 mSound.stop(); 281 } 282 finally { 283 Binder.restoreCallingIdentity(identity); 284 } 285 286 // vibrate 287 mVibrateNotification = null; 288 identity = Binder.clearCallingIdentity(); 289 try { 290 mVibrator.cancel(); 291 } 292 finally { 293 Binder.restoreCallingIdentity(identity); 294 } 295 296 // light 297 mLights.clear(); 298 mLedNotification = null; 299 updateLightsLocked(); 300 } 301 } 302 303 public void onNotificationError(String pkg, String tag, int id, 304 int uid, int initialPid, String message) { 305 Slog.d(TAG, "onNotification error pkg=" + pkg + " tag=" + tag + " id=" + id 306 + "; will crashApplication(uid=" + uid + ", pid=" + initialPid + ")"); 307 cancelNotification(pkg, tag, id, 0, 0, false); 308 long ident = Binder.clearCallingIdentity(); 309 try { 310 ActivityManagerNative.getDefault().crashApplication(uid, initialPid, pkg, 311 "Bad notification posted from package " + pkg 312 + ": " + message); 313 } catch (RemoteException e) { 314 } 315 Binder.restoreCallingIdentity(ident); 316 } 317 }; 318 319 private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { 320 @Override 321 public void onReceive(Context context, Intent intent) { 322 String action = intent.getAction(); 323 324 boolean queryRestart = false; 325 326 if (action.equals(Intent.ACTION_PACKAGE_REMOVED) 327 || action.equals(Intent.ACTION_PACKAGE_RESTARTED) 328 || action.equals(Intent.ACTION_PACKAGE_CHANGED) 329 || (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART)) 330 || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) { 331 String pkgList[] = null; 332 if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) { 333 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); 334 } else if (queryRestart) { 335 pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES); 336 } else { 337 Uri uri = intent.getData(); 338 if (uri == null) { 339 return; 340 } 341 String pkgName = uri.getSchemeSpecificPart(); 342 if (pkgName == null) { 343 return; 344 } 345 pkgList = new String[]{pkgName}; 346 } 347 if (pkgList != null && (pkgList.length > 0)) { 348 for (String pkgName : pkgList) { 349 cancelAllNotificationsInt(pkgName, 0, 0, !queryRestart); 350 } 351 } 352 } else if (action.equals(Intent.ACTION_SCREEN_ON)) { 353 // Keep track of screen on/off state, but do not turn off the notification light 354 // until user passes through the lock screen or views the notification. 355 mScreenOn = true; 356 } else if (action.equals(Intent.ACTION_SCREEN_OFF)) { 357 mScreenOn = false; 358 } else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) { 359 mInCall = (intent.getStringExtra(TelephonyManager.EXTRA_STATE).equals( 360 TelephonyManager.EXTRA_STATE_OFFHOOK)); 361 updateNotificationPulse(); 362 } else if (action.equals(Intent.ACTION_USER_PRESENT)) { 363 // turn off LED when user passes through lock screen 364 mNotificationLight.turnOff(); 365 } 366 } 367 }; 368 369 class SettingsObserver extends ContentObserver { 370 SettingsObserver(Handler handler) { 371 super(handler); 372 } 373 374 void observe() { 375 ContentResolver resolver = mContext.getContentResolver(); 376 resolver.registerContentObserver(Settings.System.getUriFor( 377 Settings.System.NOTIFICATION_LIGHT_PULSE), false, this); 378 update(); 379 } 380 381 @Override public void onChange(boolean selfChange) { 382 update(); 383 } 384 385 public void update() { 386 ContentResolver resolver = mContext.getContentResolver(); 387 boolean pulseEnabled = Settings.System.getInt(resolver, 388 Settings.System.NOTIFICATION_LIGHT_PULSE, 0) != 0; 389 if (mNotificationPulseEnabled != pulseEnabled) { 390 mNotificationPulseEnabled = pulseEnabled; 391 updateNotificationPulse(); 392 } 393 } 394 } 395 396 NotificationManagerService(Context context, StatusBarManagerService statusBar, 397 LightsService lights) 398 { 399 super(); 400 mContext = context; 401 mVibrator = (Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE); 402 mAm = ActivityManagerNative.getDefault(); 403 mSound = new NotificationPlayer(TAG); 404 mSound.setUsesWakeLock(context); 405 mToastQueue = new ArrayList<ToastRecord>(); 406 mHandler = new WorkerHandler(); 407 408 mStatusBar = statusBar; 409 statusBar.setNotificationCallbacks(mNotificationCallbacks); 410 411 mNotificationLight = lights.getLight(LightsService.LIGHT_ID_NOTIFICATIONS); 412 mAttentionLight = lights.getLight(LightsService.LIGHT_ID_ATTENTION); 413 414 Resources resources = mContext.getResources(); 415 mDefaultNotificationColor = resources.getColor( 416 com.android.internal.R.color.config_defaultNotificationColor); 417 mDefaultNotificationLedOn = resources.getInteger( 418 com.android.internal.R.integer.config_defaultNotificationLedOn); 419 mDefaultNotificationLedOff = resources.getInteger( 420 com.android.internal.R.integer.config_defaultNotificationLedOff); 421 422 // Don't start allowing notifications until the setup wizard has run once. 423 // After that, including subsequent boots, init with notifications turned on. 424 // This works on the first boot because the setup wizard will toggle this 425 // flag at least once and we'll go back to 0 after that. 426 if (0 == Settings.Secure.getInt(mContext.getContentResolver(), 427 Settings.Secure.DEVICE_PROVISIONED, 0)) { 428 mDisabledNotifications = StatusBarManager.DISABLE_NOTIFICATION_ALERTS; 429 } 430 431 // register for various Intents 432 IntentFilter filter = new IntentFilter(); 433 filter.addAction(Intent.ACTION_SCREEN_ON); 434 filter.addAction(Intent.ACTION_SCREEN_OFF); 435 filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED); 436 filter.addAction(Intent.ACTION_USER_PRESENT); 437 mContext.registerReceiver(mIntentReceiver, filter); 438 IntentFilter pkgFilter = new IntentFilter(); 439 pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); 440 pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED); 441 pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED); 442 pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART); 443 pkgFilter.addDataScheme("package"); 444 mContext.registerReceiver(mIntentReceiver, pkgFilter); 445 IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); 446 mContext.registerReceiver(mIntentReceiver, sdFilter); 447 448 SettingsObserver observer = new SettingsObserver(mHandler); 449 observer.observe(); 450 } 451 452 void systemReady() { 453 // no beeping until we're basically done booting 454 mSystemReady = true; 455 } 456 457 // Toasts 458 // ============================================================================ 459 public void enqueueToast(String pkg, ITransientNotification callback, int duration) 460 { 461 if (DBG) Slog.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback + " duration=" + duration); 462 463 if (pkg == null || callback == null) { 464 Slog.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback); 465 return ; 466 } 467 468 synchronized (mToastQueue) { 469 int callingPid = Binder.getCallingPid(); 470 long callingId = Binder.clearCallingIdentity(); 471 try { 472 ToastRecord record; 473 int index = indexOfToastLocked(pkg, callback); 474 // If it's already in the queue, we update it in place, we don't 475 // move it to the end of the queue. 476 if (index >= 0) { 477 record = mToastQueue.get(index); 478 record.update(duration); 479 } else { 480 // Limit the number of toasts that any given package except the android 481 // package can enqueue. Prevents DOS attacks and deals with leaks. 482 if (!"android".equals(pkg)) { 483 int count = 0; 484 final int N = mToastQueue.size(); 485 for (int i=0; i<N; i++) { 486 final ToastRecord r = mToastQueue.get(i); 487 if (r.pkg.equals(pkg)) { 488 count++; 489 if (count >= MAX_PACKAGE_NOTIFICATIONS) { 490 Slog.e(TAG, "Package has already posted " + count 491 + " toasts. Not showing more. Package=" + pkg); 492 return; 493 } 494 } 495 } 496 } 497 498 record = new ToastRecord(callingPid, pkg, callback, duration); 499 mToastQueue.add(record); 500 index = mToastQueue.size() - 1; 501 keepProcessAliveLocked(callingPid); 502 } 503 // If it's at index 0, it's the current toast. It doesn't matter if it's 504 // new or just been updated. Call back and tell it to show itself. 505 // If the callback fails, this will remove it from the list, so don't 506 // assume that it's valid after this. 507 if (index == 0) { 508 showNextToastLocked(); 509 } 510 } finally { 511 Binder.restoreCallingIdentity(callingId); 512 } 513 } 514 } 515 516 public void cancelToast(String pkg, ITransientNotification callback) { 517 Slog.i(TAG, "cancelToast pkg=" + pkg + " callback=" + callback); 518 519 if (pkg == null || callback == null) { 520 Slog.e(TAG, "Not cancelling notification. pkg=" + pkg + " callback=" + callback); 521 return ; 522 } 523 524 synchronized (mToastQueue) { 525 long callingId = Binder.clearCallingIdentity(); 526 try { 527 int index = indexOfToastLocked(pkg, callback); 528 if (index >= 0) { 529 cancelToastLocked(index); 530 } else { 531 Slog.w(TAG, "Toast already cancelled. pkg=" + pkg + " callback=" + callback); 532 } 533 } finally { 534 Binder.restoreCallingIdentity(callingId); 535 } 536 } 537 } 538 539 private void showNextToastLocked() { 540 ToastRecord record = mToastQueue.get(0); 541 while (record != null) { 542 if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback); 543 try { 544 record.callback.show(); 545 scheduleTimeoutLocked(record, false); 546 return; 547 } catch (RemoteException e) { 548 Slog.w(TAG, "Object died trying to show notification " + record.callback 549 + " in package " + record.pkg); 550 // remove it from the list and let the process die 551 int index = mToastQueue.indexOf(record); 552 if (index >= 0) { 553 mToastQueue.remove(index); 554 } 555 keepProcessAliveLocked(record.pid); 556 if (mToastQueue.size() > 0) { 557 record = mToastQueue.get(0); 558 } else { 559 record = null; 560 } 561 } 562 } 563 } 564 565 private void cancelToastLocked(int index) { 566 ToastRecord record = mToastQueue.get(index); 567 try { 568 record.callback.hide(); 569 } catch (RemoteException e) { 570 Slog.w(TAG, "Object died trying to hide notification " + record.callback 571 + " in package " + record.pkg); 572 // don't worry about this, we're about to remove it from 573 // the list anyway 574 } 575 mToastQueue.remove(index); 576 keepProcessAliveLocked(record.pid); 577 if (mToastQueue.size() > 0) { 578 // Show the next one. If the callback fails, this will remove 579 // it from the list, so don't assume that the list hasn't changed 580 // after this point. 581 showNextToastLocked(); 582 } 583 } 584 585 private void scheduleTimeoutLocked(ToastRecord r, boolean immediate) 586 { 587 Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r); 588 long delay = immediate ? 0 : (r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY); 589 mHandler.removeCallbacksAndMessages(r); 590 mHandler.sendMessageDelayed(m, delay); 591 } 592 593 private void handleTimeout(ToastRecord record) 594 { 595 if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback); 596 synchronized (mToastQueue) { 597 int index = indexOfToastLocked(record.pkg, record.callback); 598 if (index >= 0) { 599 cancelToastLocked(index); 600 } 601 } 602 } 603 604 // lock on mToastQueue 605 private int indexOfToastLocked(String pkg, ITransientNotification callback) 606 { 607 IBinder cbak = callback.asBinder(); 608 ArrayList<ToastRecord> list = mToastQueue; 609 int len = list.size(); 610 for (int i=0; i<len; i++) { 611 ToastRecord r = list.get(i); 612 if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) { 613 return i; 614 } 615 } 616 return -1; 617 } 618 619 // lock on mToastQueue 620 private void keepProcessAliveLocked(int pid) 621 { 622 int toastCount = 0; // toasts from this pid 623 ArrayList<ToastRecord> list = mToastQueue; 624 int N = list.size(); 625 for (int i=0; i<N; i++) { 626 ToastRecord r = list.get(i); 627 if (r.pid == pid) { 628 toastCount++; 629 } 630 } 631 try { 632 mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0); 633 } catch (RemoteException e) { 634 // Shouldn't happen. 635 } 636 } 637 638 private final class WorkerHandler extends Handler 639 { 640 @Override 641 public void handleMessage(Message msg) 642 { 643 switch (msg.what) 644 { 645 case MESSAGE_TIMEOUT: 646 handleTimeout((ToastRecord)msg.obj); 647 break; 648 } 649 } 650 } 651 652 653 // Notifications 654 // ============================================================================ 655 @Deprecated 656 public void enqueueNotification(String pkg, int id, Notification notification, int[] idOut) 657 { 658 enqueueNotificationWithTag(pkg, null /* tag */, id, notification, idOut); 659 } 660 661 public void enqueueNotificationWithTag(String pkg, String tag, int id, Notification notification, 662 int[] idOut) 663 { 664 enqueueNotificationInternal(pkg, Binder.getCallingUid(), Binder.getCallingPid(), 665 tag, id, notification, idOut); 666 } 667 668 private final static int clamp(int x, int low, int high) { 669 return (x < low) ? low : ((x > high) ? high : x); 670 } 671 672 673 // Not exposed via Binder; for system use only (otherwise malicious apps could spoof the 674 // uid/pid of another application) 675 public void enqueueNotificationInternal(String pkg, int callingUid, int callingPid, 676 String tag, int id, Notification notification, int[] idOut) 677 { 678 checkIncomingCall(pkg); 679 680 // Limit the number of notifications that any given package except the android 681 // package can enqueue. Prevents DOS attacks and deals with leaks. 682 if (!"android".equals(pkg)) { 683 synchronized (mNotificationList) { 684 int count = 0; 685 final int N = mNotificationList.size(); 686 for (int i=0; i<N; i++) { 687 final NotificationRecord r = mNotificationList.get(i); 688 if (r.pkg.equals(pkg)) { 689 count++; 690 if (count >= MAX_PACKAGE_NOTIFICATIONS) { 691 Slog.e(TAG, "Package has already posted " + count 692 + " notifications. Not showing more. package=" + pkg); 693 return; 694 } 695 } 696 } 697 } 698 } 699 700 // This conditional is a dirty hack to limit the logging done on 701 // behalf of the download manager without affecting other apps. 702 if (!pkg.equals("com.android.providers.downloads") 703 || Log.isLoggable("DownloadManager", Log.VERBOSE)) { 704 EventLog.writeEvent(EventLogTags.NOTIFICATION_ENQUEUE, pkg, id, tag, 705 notification.toString()); 706 } 707 708 if (pkg == null || notification == null) { 709 throw new IllegalArgumentException("null not allowed: pkg=" + pkg 710 + " id=" + id + " notification=" + notification); 711 } 712 if (notification.icon != 0) { 713 if (notification.contentView == null) { 714 throw new IllegalArgumentException("contentView required: pkg=" + pkg 715 + " id=" + id + " notification=" + notification); 716 } 717 } 718 719 // === Scoring === 720 721 // 0. Sanitize inputs 722 notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN, Notification.PRIORITY_MAX); 723 // Migrate notification flags to scores 724 if (0 != (notification.flags & Notification.FLAG_HIGH_PRIORITY)) { 725 if (notification.priority < Notification.PRIORITY_MAX) notification.priority = Notification.PRIORITY_MAX; 726 } else if (SCORE_ONGOING_HIGHER && 0 != (notification.flags & Notification.FLAG_ONGOING_EVENT)) { 727 if (notification.priority < Notification.PRIORITY_HIGH) notification.priority = Notification.PRIORITY_HIGH; 728 } 729 730 // 1. initial score: buckets of 10, around the app 731 int score = notification.priority * 10; //[-20..20] 732 733 // 2. Consult oracles (external heuristics) 734 // TODO(dsandler): oracles 735 736 // 3. Apply local heuristics & overrides 737 738 // blocked apps 739 // TODO(dsandler): add block db 740 if (pkg.startsWith("com.test.spammer.")) { 741 score = -1000; 742 } 743 744 synchronized (mNotificationList) { 745 NotificationRecord r = new NotificationRecord(pkg, tag, id, 746 callingUid, callingPid, 747 score, 748 notification); 749 NotificationRecord old = null; 750 751 int index = indexOfNotificationLocked(pkg, tag, id); 752 if (index < 0) { 753 mNotificationList.add(r); 754 } else { 755 old = mNotificationList.remove(index); 756 mNotificationList.add(index, r); 757 // Make sure we don't lose the foreground service state. 758 if (old != null) { 759 notification.flags |= 760 old.notification.flags&Notification.FLAG_FOREGROUND_SERVICE; 761 } 762 } 763 764 // Ensure if this is a foreground service that the proper additional 765 // flags are set. 766 if ((notification.flags&Notification.FLAG_FOREGROUND_SERVICE) != 0) { 767 notification.flags |= Notification.FLAG_ONGOING_EVENT 768 | Notification.FLAG_NO_CLEAR; 769 } 770 771 if (notification.icon != 0) { 772 StatusBarNotification n = new StatusBarNotification(pkg, id, tag, 773 r.uid, r.initialPid, score, notification); 774 if (old != null && old.statusBarKey != null) { 775 r.statusBarKey = old.statusBarKey; 776 long identity = Binder.clearCallingIdentity(); 777 try { 778 mStatusBar.updateNotification(r.statusBarKey, n); 779 } 780 finally { 781 Binder.restoreCallingIdentity(identity); 782 } 783 } else { 784 long identity = Binder.clearCallingIdentity(); 785 try { 786 r.statusBarKey = mStatusBar.addNotification(n); 787 if ((n.notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0) { 788 mAttentionLight.pulse(); 789 } 790 } 791 finally { 792 Binder.restoreCallingIdentity(identity); 793 } 794 } 795 sendAccessibilityEvent(notification, pkg); 796 } else { 797 Slog.e(TAG, "Ignoring notification with icon==0: " + notification); 798 if (old != null && old.statusBarKey != null) { 799 long identity = Binder.clearCallingIdentity(); 800 try { 801 mStatusBar.removeNotification(old.statusBarKey); 802 } 803 finally { 804 Binder.restoreCallingIdentity(identity); 805 } 806 } 807 } 808 809 // If we're not supposed to beep, vibrate, etc. then don't. 810 if (((mDisabledNotifications & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) == 0) 811 && (!(old != null 812 && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 )) 813 && mSystemReady) { 814 815 final AudioManager audioManager = (AudioManager) mContext 816 .getSystemService(Context.AUDIO_SERVICE); 817 // sound 818 final boolean useDefaultSound = 819 (notification.defaults & Notification.DEFAULT_SOUND) != 0; 820 if (useDefaultSound || notification.sound != null) { 821 Uri uri; 822 if (useDefaultSound) { 823 uri = Settings.System.DEFAULT_NOTIFICATION_URI; 824 } else { 825 uri = notification.sound; 826 } 827 boolean looping = (notification.flags & Notification.FLAG_INSISTENT) != 0; 828 int audioStreamType; 829 if (notification.audioStreamType >= 0) { 830 audioStreamType = notification.audioStreamType; 831 } else { 832 audioStreamType = DEFAULT_STREAM_TYPE; 833 } 834 mSoundNotification = r; 835 // do not play notifications if stream volume is 0 836 // (typically because ringer mode is silent). 837 if (audioManager.getStreamVolume(audioStreamType) != 0) { 838 long identity = Binder.clearCallingIdentity(); 839 try { 840 mSound.play(mContext, uri, looping, audioStreamType); 841 } 842 finally { 843 Binder.restoreCallingIdentity(identity); 844 } 845 } 846 } 847 848 // vibrate 849 final boolean useDefaultVibrate = 850 (notification.defaults & Notification.DEFAULT_VIBRATE) != 0; 851 if ((useDefaultVibrate || notification.vibrate != null) 852 && audioManager.shouldVibrate(AudioManager.VIBRATE_TYPE_NOTIFICATION)) { 853 mVibrateNotification = r; 854 855 mVibrator.vibrate(useDefaultVibrate ? DEFAULT_VIBRATE_PATTERN 856 : notification.vibrate, 857 ((notification.flags & Notification.FLAG_INSISTENT) != 0) ? 0: -1); 858 } 859 } 860 861 // this option doesn't shut off the lights 862 863 // light 864 // the most recent thing gets the light 865 mLights.remove(old); 866 if (mLedNotification == old) { 867 mLedNotification = null; 868 } 869 //Slog.i(TAG, "notification.lights=" 870 // + ((old.notification.lights.flags & Notification.FLAG_SHOW_LIGHTS) != 0)); 871 if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0) { 872 mLights.add(r); 873 updateLightsLocked(); 874 } else { 875 if (old != null 876 && ((old.notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0)) { 877 updateLightsLocked(); 878 } 879 } 880 } 881 882 idOut[0] = id; 883 } 884 885 private void sendAccessibilityEvent(Notification notification, CharSequence packageName) { 886 AccessibilityManager manager = AccessibilityManager.getInstance(mContext); 887 if (!manager.isEnabled()) { 888 return; 889 } 890 891 AccessibilityEvent event = 892 AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED); 893 event.setPackageName(packageName); 894 event.setClassName(Notification.class.getName()); 895 event.setParcelableData(notification); 896 CharSequence tickerText = notification.tickerText; 897 if (!TextUtils.isEmpty(tickerText)) { 898 event.getText().add(tickerText); 899 } 900 901 manager.sendAccessibilityEvent(event); 902 } 903 904 private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete) { 905 // tell the app 906 if (sendDelete) { 907 if (r.notification.deleteIntent != null) { 908 try { 909 r.notification.deleteIntent.send(); 910 } catch (PendingIntent.CanceledException ex) { 911 // do nothing - there's no relevant way to recover, and 912 // no reason to let this propagate 913 Slog.w(TAG, "canceled PendingIntent for " + r.pkg, ex); 914 } 915 } 916 } 917 918 // status bar 919 if (r.notification.icon != 0) { 920 long identity = Binder.clearCallingIdentity(); 921 try { 922 mStatusBar.removeNotification(r.statusBarKey); 923 } 924 finally { 925 Binder.restoreCallingIdentity(identity); 926 } 927 r.statusBarKey = null; 928 } 929 930 // sound 931 if (mSoundNotification == r) { 932 mSoundNotification = null; 933 long identity = Binder.clearCallingIdentity(); 934 try { 935 mSound.stop(); 936 } 937 finally { 938 Binder.restoreCallingIdentity(identity); 939 } 940 } 941 942 // vibrate 943 if (mVibrateNotification == r) { 944 mVibrateNotification = null; 945 long identity = Binder.clearCallingIdentity(); 946 try { 947 mVibrator.cancel(); 948 } 949 finally { 950 Binder.restoreCallingIdentity(identity); 951 } 952 } 953 954 // light 955 mLights.remove(r); 956 if (mLedNotification == r) { 957 mLedNotification = null; 958 } 959 } 960 961 /** 962 * Cancels a notification ONLY if it has all of the {@code mustHaveFlags} 963 * and none of the {@code mustNotHaveFlags}. 964 */ 965 private void cancelNotification(String pkg, String tag, int id, int mustHaveFlags, 966 int mustNotHaveFlags, boolean sendDelete) { 967 EventLog.writeEvent(EventLogTags.NOTIFICATION_CANCEL, pkg, id, tag, 968 mustHaveFlags, mustNotHaveFlags); 969 970 synchronized (mNotificationList) { 971 int index = indexOfNotificationLocked(pkg, tag, id); 972 if (index >= 0) { 973 NotificationRecord r = mNotificationList.get(index); 974 975 if ((r.notification.flags & mustHaveFlags) != mustHaveFlags) { 976 return; 977 } 978 if ((r.notification.flags & mustNotHaveFlags) != 0) { 979 return; 980 } 981 982 mNotificationList.remove(index); 983 984 cancelNotificationLocked(r, sendDelete); 985 updateLightsLocked(); 986 } 987 } 988 } 989 990 /** 991 * Cancels all notifications from a given package that have all of the 992 * {@code mustHaveFlags}. 993 */ 994 boolean cancelAllNotificationsInt(String pkg, int mustHaveFlags, 995 int mustNotHaveFlags, boolean doit) { 996 EventLog.writeEvent(EventLogTags.NOTIFICATION_CANCEL_ALL, pkg, mustHaveFlags, 997 mustNotHaveFlags); 998 999 synchronized (mNotificationList) { 1000 final int N = mNotificationList.size(); 1001 boolean canceledSomething = false; 1002 for (int i = N-1; i >= 0; --i) { 1003 NotificationRecord r = mNotificationList.get(i); 1004 if ((r.notification.flags & mustHaveFlags) != mustHaveFlags) { 1005 continue; 1006 } 1007 if ((r.notification.flags & mustNotHaveFlags) != 0) { 1008 continue; 1009 } 1010 if (!r.pkg.equals(pkg)) { 1011 continue; 1012 } 1013 canceledSomething = true; 1014 if (!doit) { 1015 return true; 1016 } 1017 mNotificationList.remove(i); 1018 cancelNotificationLocked(r, false); 1019 } 1020 if (canceledSomething) { 1021 updateLightsLocked(); 1022 } 1023 return canceledSomething; 1024 } 1025 } 1026 1027 @Deprecated 1028 public void cancelNotification(String pkg, int id) { 1029 cancelNotificationWithTag(pkg, null /* tag */, id); 1030 } 1031 1032 public void cancelNotificationWithTag(String pkg, String tag, int id) { 1033 checkIncomingCall(pkg); 1034 // Don't allow client applications to cancel foreground service notis. 1035 cancelNotification(pkg, tag, id, 0, 1036 Binder.getCallingUid() == Process.SYSTEM_UID 1037 ? 0 : Notification.FLAG_FOREGROUND_SERVICE, false); 1038 } 1039 1040 public void cancelAllNotifications(String pkg) { 1041 checkIncomingCall(pkg); 1042 1043 // Calling from user space, don't allow the canceling of actively 1044 // running foreground services. 1045 cancelAllNotificationsInt(pkg, 0, Notification.FLAG_FOREGROUND_SERVICE, true); 1046 } 1047 1048 void checkIncomingCall(String pkg) { 1049 int uid = Binder.getCallingUid(); 1050 if (uid == Process.SYSTEM_UID || uid == 0) { 1051 return; 1052 } 1053 try { 1054 ApplicationInfo ai = mContext.getPackageManager().getApplicationInfo( 1055 pkg, 0); 1056 if (!UserId.isSameApp(ai.uid, uid)) { 1057 throw new SecurityException("Calling uid " + uid + " gave package" 1058 + pkg + " which is owned by uid " + ai.uid); 1059 } 1060 } catch (PackageManager.NameNotFoundException e) { 1061 throw new SecurityException("Unknown package " + pkg); 1062 } 1063 } 1064 1065 void cancelAll() { 1066 synchronized (mNotificationList) { 1067 final int N = mNotificationList.size(); 1068 for (int i=N-1; i>=0; i--) { 1069 NotificationRecord r = mNotificationList.get(i); 1070 1071 if ((r.notification.flags & (Notification.FLAG_ONGOING_EVENT 1072 | Notification.FLAG_NO_CLEAR)) == 0) { 1073 mNotificationList.remove(i); 1074 cancelNotificationLocked(r, true); 1075 } 1076 } 1077 1078 updateLightsLocked(); 1079 } 1080 } 1081 1082 // lock on mNotificationList 1083 private void updateLightsLocked() 1084 { 1085 // handle notification lights 1086 if (mLedNotification == null) { 1087 // get next notification, if any 1088 int n = mLights.size(); 1089 if (n > 0) { 1090 mLedNotification = mLights.get(n-1); 1091 } 1092 } 1093 1094 // Don't flash while we are in a call or screen is on 1095 if (mLedNotification == null || mInCall || mScreenOn) { 1096 mNotificationLight.turnOff(); 1097 } else { 1098 int ledARGB = mLedNotification.notification.ledARGB; 1099 int ledOnMS = mLedNotification.notification.ledOnMS; 1100 int ledOffMS = mLedNotification.notification.ledOffMS; 1101 if ((mLedNotification.notification.defaults & Notification.DEFAULT_LIGHTS) != 0) { 1102 ledARGB = mDefaultNotificationColor; 1103 ledOnMS = mDefaultNotificationLedOn; 1104 ledOffMS = mDefaultNotificationLedOff; 1105 } 1106 if (mNotificationPulseEnabled) { 1107 // pulse repeatedly 1108 mNotificationLight.setFlashing(ledARGB, LightsService.LIGHT_FLASH_TIMED, 1109 ledOnMS, ledOffMS); 1110 } 1111 } 1112 } 1113 1114 // lock on mNotificationList 1115 private int indexOfNotificationLocked(String pkg, String tag, int id) 1116 { 1117 ArrayList<NotificationRecord> list = mNotificationList; 1118 final int len = list.size(); 1119 for (int i=0; i<len; i++) { 1120 NotificationRecord r = list.get(i); 1121 if (tag == null) { 1122 if (r.tag != null) { 1123 continue; 1124 } 1125 } else { 1126 if (!tag.equals(r.tag)) { 1127 continue; 1128 } 1129 } 1130 if (r.id == id && r.pkg.equals(pkg)) { 1131 return i; 1132 } 1133 } 1134 return -1; 1135 } 1136 1137 private void updateNotificationPulse() { 1138 synchronized (mNotificationList) { 1139 updateLightsLocked(); 1140 } 1141 } 1142 1143 // ====================================================================== 1144 @Override 1145 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1146 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) 1147 != PackageManager.PERMISSION_GRANTED) { 1148 pw.println("Permission Denial: can't dump NotificationManager from from pid=" 1149 + Binder.getCallingPid() 1150 + ", uid=" + Binder.getCallingUid()); 1151 return; 1152 } 1153 1154 pw.println("Current Notification Manager state:"); 1155 1156 int N; 1157 1158 synchronized (mToastQueue) { 1159 N = mToastQueue.size(); 1160 if (N > 0) { 1161 pw.println(" Toast Queue:"); 1162 for (int i=0; i<N; i++) { 1163 mToastQueue.get(i).dump(pw, " "); 1164 } 1165 pw.println(" "); 1166 } 1167 1168 } 1169 1170 synchronized (mNotificationList) { 1171 N = mNotificationList.size(); 1172 if (N > 0) { 1173 pw.println(" Notification List:"); 1174 for (int i=0; i<N; i++) { 1175 mNotificationList.get(i).dump(pw, " ", mContext); 1176 } 1177 pw.println(" "); 1178 } 1179 1180 N = mLights.size(); 1181 if (N > 0) { 1182 pw.println(" Lights List:"); 1183 for (int i=0; i<N; i++) { 1184 mLights.get(i).dump(pw, " ", mContext); 1185 } 1186 pw.println(" "); 1187 } 1188 1189 pw.println(" mSoundNotification=" + mSoundNotification); 1190 pw.println(" mSound=" + mSound); 1191 pw.println(" mVibrateNotification=" + mVibrateNotification); 1192 pw.println(" mDisabledNotifications=0x" + Integer.toHexString(mDisabledNotifications)); 1193 pw.println(" mSystemReady=" + mSystemReady); 1194 } 1195 } 1196} 1197