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