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