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