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