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