NotificationManagerService.java revision b5c22f723628c31f82fa4b77fdb4451cfadd58ec
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 final int BATTERY_LOW_ARGB = 0xFFFF0000; // Charging Low - red solid on 134 private static final int BATTERY_MEDIUM_ARGB = 0xFFFFFF00; // Charging - orange solid on 135 private static final int BATTERY_FULL_ARGB = 0xFF00FF00; // Charging Full - green solid on 136 private static final int BATTERY_BLINK_ON = 125; 137 private static final int BATTERY_BLINK_OFF = 2875; 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 // Don't start allowing notifications until the setup wizard has run once. 457 // After that, including subsequent boots, init with notifications turned on. 458 // This works on the first boot because the setup wizard will toggle this 459 // flag at least once and we'll go back to 0 after that. 460 if (0 == Settings.Secure.getInt(mContext.getContentResolver(), 461 Settings.Secure.DEVICE_PROVISIONED, 0)) { 462 mDisabledNotifications = StatusBarManager.DISABLE_NOTIFICATION_ALERTS; 463 } 464 465 // register for battery changed notifications 466 IntentFilter filter = new IntentFilter(); 467 filter.addAction(Intent.ACTION_BATTERY_CHANGED); 468 filter.addAction(Usb.ACTION_USB_STATE); 469 filter.addAction(Intent.ACTION_SCREEN_ON); 470 filter.addAction(Intent.ACTION_SCREEN_OFF); 471 filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED); 472 mContext.registerReceiver(mIntentReceiver, filter); 473 IntentFilter pkgFilter = new IntentFilter(); 474 pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); 475 pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED); 476 pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART); 477 pkgFilter.addDataScheme("package"); 478 mContext.registerReceiver(mIntentReceiver, pkgFilter); 479 IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); 480 mContext.registerReceiver(mIntentReceiver, sdFilter); 481 482 SettingsObserver observer = new SettingsObserver(mHandler); 483 observer.observe(); 484 } 485 486 void systemReady() { 487 // no beeping until we're basically done booting 488 mSystemReady = true; 489 } 490 491 // Toasts 492 // ============================================================================ 493 public void enqueueToast(String pkg, ITransientNotification callback, int duration) 494 { 495 if (DBG) Slog.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback + " duration=" + duration); 496 497 if (pkg == null || callback == null) { 498 Slog.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback); 499 return ; 500 } 501 502 synchronized (mToastQueue) { 503 int callingPid = Binder.getCallingPid(); 504 long callingId = Binder.clearCallingIdentity(); 505 try { 506 ToastRecord record; 507 int index = indexOfToastLocked(pkg, callback); 508 // If it's already in the queue, we update it in place, we don't 509 // move it to the end of the queue. 510 if (index >= 0) { 511 record = mToastQueue.get(index); 512 record.update(duration); 513 } else { 514 record = new ToastRecord(callingPid, pkg, callback, duration); 515 mToastQueue.add(record); 516 index = mToastQueue.size() - 1; 517 keepProcessAliveLocked(callingPid); 518 } 519 // If it's at index 0, it's the current toast. It doesn't matter if it's 520 // new or just been updated. Call back and tell it to show itself. 521 // If the callback fails, this will remove it from the list, so don't 522 // assume that it's valid after this. 523 if (index == 0) { 524 showNextToastLocked(); 525 } 526 } finally { 527 Binder.restoreCallingIdentity(callingId); 528 } 529 } 530 } 531 532 public void cancelToast(String pkg, ITransientNotification callback) { 533 Slog.i(TAG, "cancelToast pkg=" + pkg + " callback=" + callback); 534 535 if (pkg == null || callback == null) { 536 Slog.e(TAG, "Not cancelling notification. pkg=" + pkg + " callback=" + callback); 537 return ; 538 } 539 540 synchronized (mToastQueue) { 541 long callingId = Binder.clearCallingIdentity(); 542 try { 543 int index = indexOfToastLocked(pkg, callback); 544 if (index >= 0) { 545 cancelToastLocked(index); 546 } else { 547 Slog.w(TAG, "Toast already cancelled. pkg=" + pkg + " callback=" + callback); 548 } 549 } finally { 550 Binder.restoreCallingIdentity(callingId); 551 } 552 } 553 } 554 555 private void showNextToastLocked() { 556 ToastRecord record = mToastQueue.get(0); 557 while (record != null) { 558 if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback); 559 try { 560 record.callback.show(); 561 scheduleTimeoutLocked(record, false); 562 return; 563 } catch (RemoteException e) { 564 Slog.w(TAG, "Object died trying to show notification " + record.callback 565 + " in package " + record.pkg); 566 // remove it from the list and let the process die 567 int index = mToastQueue.indexOf(record); 568 if (index >= 0) { 569 mToastQueue.remove(index); 570 } 571 keepProcessAliveLocked(record.pid); 572 if (mToastQueue.size() > 0) { 573 record = mToastQueue.get(0); 574 } else { 575 record = null; 576 } 577 } 578 } 579 } 580 581 private void cancelToastLocked(int index) { 582 ToastRecord record = mToastQueue.get(index); 583 try { 584 record.callback.hide(); 585 } catch (RemoteException e) { 586 Slog.w(TAG, "Object died trying to hide notification " + record.callback 587 + " in package " + record.pkg); 588 // don't worry about this, we're about to remove it from 589 // the list anyway 590 } 591 mToastQueue.remove(index); 592 keepProcessAliveLocked(record.pid); 593 if (mToastQueue.size() > 0) { 594 // Show the next one. If the callback fails, this will remove 595 // it from the list, so don't assume that the list hasn't changed 596 // after this point. 597 showNextToastLocked(); 598 } 599 } 600 601 private void scheduleTimeoutLocked(ToastRecord r, boolean immediate) 602 { 603 Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r); 604 long delay = immediate ? 0 : (r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY); 605 mHandler.removeCallbacksAndMessages(r); 606 mHandler.sendMessageDelayed(m, delay); 607 } 608 609 private void handleTimeout(ToastRecord record) 610 { 611 if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback); 612 synchronized (mToastQueue) { 613 int index = indexOfToastLocked(record.pkg, record.callback); 614 if (index >= 0) { 615 cancelToastLocked(index); 616 } 617 } 618 } 619 620 // lock on mToastQueue 621 private int indexOfToastLocked(String pkg, ITransientNotification callback) 622 { 623 IBinder cbak = callback.asBinder(); 624 ArrayList<ToastRecord> list = mToastQueue; 625 int len = list.size(); 626 for (int i=0; i<len; i++) { 627 ToastRecord r = list.get(i); 628 if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) { 629 return i; 630 } 631 } 632 return -1; 633 } 634 635 // lock on mToastQueue 636 private void keepProcessAliveLocked(int pid) 637 { 638 int toastCount = 0; // toasts from this pid 639 ArrayList<ToastRecord> list = mToastQueue; 640 int N = list.size(); 641 for (int i=0; i<N; i++) { 642 ToastRecord r = list.get(i); 643 if (r.pid == pid) { 644 toastCount++; 645 } 646 } 647 try { 648 mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0); 649 } catch (RemoteException e) { 650 // Shouldn't happen. 651 } 652 } 653 654 private final class WorkerHandler extends Handler 655 { 656 @Override 657 public void handleMessage(Message msg) 658 { 659 switch (msg.what) 660 { 661 case MESSAGE_TIMEOUT: 662 handleTimeout((ToastRecord)msg.obj); 663 break; 664 } 665 } 666 } 667 668 669 // Notifications 670 // ============================================================================ 671 public void enqueueNotification(String pkg, int id, Notification notification, int[] idOut) 672 { 673 enqueueNotificationWithTag(pkg, null /* tag */, id, notification, idOut); 674 } 675 676 public void enqueueNotificationWithTag(String pkg, String tag, int id, Notification notification, 677 int[] idOut) 678 { 679 enqueueNotificationInternal(pkg, Binder.getCallingUid(), Binder.getCallingPid(), 680 tag, id, notification, idOut); 681 } 682 683 // Not exposed via Binder; for system use only (otherwise malicious apps could spoof the 684 // uid/pid of another application) 685 public void enqueueNotificationInternal(String pkg, int callingUid, int callingPid, 686 String tag, int id, Notification notification, int[] idOut) 687 { 688 Slog.d(TAG, "enqueueNotificationWithTag: calling uid=" + callingUid 689 + ", pid=" + callingPid); 690 691 checkIncomingCall(pkg); 692 693 // Limit the number of notifications that any given package except the android 694 // package can enqueue. Prevents DOS attacks and deals with leaks. 695 if (!"android".equals(pkg)) { 696 synchronized (mNotificationList) { 697 int count = 0; 698 final int N = mNotificationList.size(); 699 for (int i=0; i<N; i++) { 700 final NotificationRecord r = mNotificationList.get(i); 701 if (r.pkg.equals(pkg)) { 702 count++; 703 if (count >= MAX_PACKAGE_NOTIFICATIONS) { 704 Slog.e(TAG, "Package has already posted " + count 705 + " notifications. Not showing more. package=" + pkg); 706 return; 707 } 708 } 709 } 710 } 711 } 712 713 // This conditional is a dirty hack to limit the logging done on 714 // behalf of the download manager without affecting other apps. 715 if (!pkg.equals("com.android.providers.downloads") 716 || Log.isLoggable("DownloadManager", Log.VERBOSE)) { 717 EventLog.writeEvent(EventLogTags.NOTIFICATION_ENQUEUE, pkg, id, notification.toString()); 718 } 719 720 if (pkg == null || notification == null) { 721 throw new IllegalArgumentException("null not allowed: pkg=" + pkg 722 + " id=" + id + " notification=" + notification); 723 } 724 if (notification.icon != 0) { 725 if (notification.contentView == null) { 726 throw new IllegalArgumentException("contentView required: pkg=" + pkg 727 + " id=" + id + " notification=" + notification); 728 } 729 if (notification.contentIntent == null) { 730 throw new IllegalArgumentException("contentIntent required: pkg=" + pkg 731 + " id=" + id + " notification=" + notification); 732 } 733 } 734 735 synchronized (mNotificationList) { 736 NotificationRecord r = new NotificationRecord(pkg, tag, id, 737 callingUid, callingPid, notification); 738 NotificationRecord old = null; 739 740 int index = indexOfNotificationLocked(pkg, tag, id); 741 if (index < 0) { 742 mNotificationList.add(r); 743 } else { 744 old = mNotificationList.remove(index); 745 mNotificationList.add(index, r); 746 // Make sure we don't lose the foreground service state. 747 if (old != null) { 748 notification.flags |= 749 old.notification.flags&Notification.FLAG_FOREGROUND_SERVICE; 750 } 751 } 752 753 // Ensure if this is a foreground service that the proper additional 754 // flags are set. 755 if ((notification.flags&Notification.FLAG_FOREGROUND_SERVICE) != 0) { 756 notification.flags |= Notification.FLAG_ONGOING_EVENT 757 | Notification.FLAG_NO_CLEAR; 758 } 759 760 if (notification.icon != 0) { 761 StatusBarNotification n = new StatusBarNotification(pkg, id, tag, 762 r.uid, r.initialPid, notification); 763 if (old != null && old.statusBarKey != null) { 764 r.statusBarKey = old.statusBarKey; 765 long identity = Binder.clearCallingIdentity(); 766 try { 767 mStatusBar.updateNotification(r.statusBarKey, n); 768 } 769 finally { 770 Binder.restoreCallingIdentity(identity); 771 } 772 } else { 773 long identity = Binder.clearCallingIdentity(); 774 try { 775 r.statusBarKey = mStatusBar.addNotification(n); 776 mAttentionLight.pulse(); 777 } 778 finally { 779 Binder.restoreCallingIdentity(identity); 780 } 781 } 782 sendAccessibilityEvent(notification, pkg); 783 } else { 784 if (old != null && old.statusBarKey != null) { 785 long identity = Binder.clearCallingIdentity(); 786 try { 787 mStatusBar.removeNotification(old.statusBarKey); 788 } 789 finally { 790 Binder.restoreCallingIdentity(identity); 791 } 792 } 793 } 794 795 // If we're not supposed to beep, vibrate, etc. then don't. 796 if (((mDisabledNotifications & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) == 0) 797 && (!(old != null 798 && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 )) 799 && mSystemReady) { 800 801 final AudioManager audioManager = (AudioManager) mContext 802 .getSystemService(Context.AUDIO_SERVICE); 803 // sound 804 final boolean useDefaultSound = 805 (notification.defaults & Notification.DEFAULT_SOUND) != 0; 806 if (useDefaultSound || notification.sound != null) { 807 Uri uri; 808 if (useDefaultSound) { 809 uri = Settings.System.DEFAULT_NOTIFICATION_URI; 810 } else { 811 uri = notification.sound; 812 } 813 boolean looping = (notification.flags & Notification.FLAG_INSISTENT) != 0; 814 int audioStreamType; 815 if (notification.audioStreamType >= 0) { 816 audioStreamType = notification.audioStreamType; 817 } else { 818 audioStreamType = DEFAULT_STREAM_TYPE; 819 } 820 mSoundNotification = r; 821 // do not play notifications if stream volume is 0 822 // (typically because ringer mode is silent). 823 if (audioManager.getStreamVolume(audioStreamType) != 0) { 824 long identity = Binder.clearCallingIdentity(); 825 try { 826 mSound.play(mContext, uri, looping, audioStreamType); 827 } 828 finally { 829 Binder.restoreCallingIdentity(identity); 830 } 831 } 832 } 833 834 // vibrate 835 final boolean useDefaultVibrate = 836 (notification.defaults & Notification.DEFAULT_VIBRATE) != 0; 837 if ((useDefaultVibrate || notification.vibrate != null) 838 && audioManager.shouldVibrate(AudioManager.VIBRATE_TYPE_NOTIFICATION)) { 839 mVibrateNotification = r; 840 841 mVibrator.vibrate(useDefaultVibrate ? DEFAULT_VIBRATE_PATTERN 842 : notification.vibrate, 843 ((notification.flags & Notification.FLAG_INSISTENT) != 0) ? 0: -1); 844 } 845 } 846 847 // this option doesn't shut off the lights 848 849 // light 850 // the most recent thing gets the light 851 mLights.remove(old); 852 if (mLedNotification == old) { 853 mLedNotification = null; 854 } 855 //Slog.i(TAG, "notification.lights=" 856 // + ((old.notification.lights.flags & Notification.FLAG_SHOW_LIGHTS) != 0)); 857 if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0) { 858 mLights.add(r); 859 updateLightsLocked(); 860 } else { 861 if (old != null 862 && ((old.notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0)) { 863 updateLightsLocked(); 864 } 865 } 866 } 867 868 idOut[0] = id; 869 } 870 871 private void sendAccessibilityEvent(Notification notification, CharSequence packageName) { 872 AccessibilityManager manager = AccessibilityManager.getInstance(mContext); 873 if (!manager.isEnabled()) { 874 return; 875 } 876 877 AccessibilityEvent event = 878 AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED); 879 event.setPackageName(packageName); 880 event.setClassName(Notification.class.getName()); 881 event.setParcelableData(notification); 882 CharSequence tickerText = notification.tickerText; 883 if (!TextUtils.isEmpty(tickerText)) { 884 event.getText().add(tickerText); 885 } 886 887 manager.sendAccessibilityEvent(event); 888 } 889 890 private void cancelNotificationLocked(NotificationRecord r) { 891 // status bar 892 if (r.notification.icon != 0) { 893 long identity = Binder.clearCallingIdentity(); 894 try { 895 mStatusBar.removeNotification(r.statusBarKey); 896 } 897 finally { 898 Binder.restoreCallingIdentity(identity); 899 } 900 r.statusBarKey = null; 901 } 902 903 // sound 904 if (mSoundNotification == r) { 905 mSoundNotification = null; 906 long identity = Binder.clearCallingIdentity(); 907 try { 908 mSound.stop(); 909 } 910 finally { 911 Binder.restoreCallingIdentity(identity); 912 } 913 } 914 915 // vibrate 916 if (mVibrateNotification == r) { 917 mVibrateNotification = null; 918 long identity = Binder.clearCallingIdentity(); 919 try { 920 mVibrator.cancel(); 921 } 922 finally { 923 Binder.restoreCallingIdentity(identity); 924 } 925 } 926 927 // light 928 mLights.remove(r); 929 if (mLedNotification == r) { 930 mLedNotification = null; 931 } 932 } 933 934 /** 935 * Cancels a notification ONLY if it has all of the {@code mustHaveFlags} 936 * and none of the {@code mustNotHaveFlags}. 937 */ 938 private void cancelNotification(String pkg, String tag, int id, int mustHaveFlags, 939 int mustNotHaveFlags) { 940 EventLog.writeEvent(EventLogTags.NOTIFICATION_CANCEL, pkg, id, mustHaveFlags); 941 942 synchronized (mNotificationList) { 943 int index = indexOfNotificationLocked(pkg, tag, id); 944 if (index >= 0) { 945 NotificationRecord r = mNotificationList.get(index); 946 947 if ((r.notification.flags & mustHaveFlags) != mustHaveFlags) { 948 return; 949 } 950 if ((r.notification.flags & mustNotHaveFlags) != 0) { 951 return; 952 } 953 954 mNotificationList.remove(index); 955 956 cancelNotificationLocked(r); 957 updateLightsLocked(); 958 } 959 } 960 } 961 962 /** 963 * Cancels all notifications from a given package that have all of the 964 * {@code mustHaveFlags}. 965 */ 966 boolean cancelAllNotificationsInt(String pkg, int mustHaveFlags, 967 int mustNotHaveFlags, boolean doit) { 968 EventLog.writeEvent(EventLogTags.NOTIFICATION_CANCEL_ALL, pkg, mustHaveFlags); 969 970 synchronized (mNotificationList) { 971 final int N = mNotificationList.size(); 972 boolean canceledSomething = false; 973 for (int i = N-1; i >= 0; --i) { 974 NotificationRecord r = mNotificationList.get(i); 975 if ((r.notification.flags & mustHaveFlags) != mustHaveFlags) { 976 continue; 977 } 978 if ((r.notification.flags & mustNotHaveFlags) != 0) { 979 continue; 980 } 981 if (!r.pkg.equals(pkg)) { 982 continue; 983 } 984 canceledSomething = true; 985 if (!doit) { 986 return true; 987 } 988 mNotificationList.remove(i); 989 cancelNotificationLocked(r); 990 } 991 if (canceledSomething) { 992 updateLightsLocked(); 993 } 994 return canceledSomething; 995 } 996 } 997 998 999 public void cancelNotification(String pkg, int id) { 1000 cancelNotificationWithTag(pkg, null /* tag */, id); 1001 } 1002 1003 public void cancelNotificationWithTag(String pkg, String tag, int id) { 1004 checkIncomingCall(pkg); 1005 // Don't allow client applications to cancel foreground service notis. 1006 cancelNotification(pkg, tag, id, 0, 1007 Binder.getCallingUid() == Process.SYSTEM_UID 1008 ? 0 : Notification.FLAG_FOREGROUND_SERVICE); 1009 } 1010 1011 public void cancelAllNotifications(String pkg) { 1012 checkIncomingCall(pkg); 1013 1014 // Calling from user space, don't allow the canceling of actively 1015 // running foreground services. 1016 cancelAllNotificationsInt(pkg, 0, Notification.FLAG_FOREGROUND_SERVICE, true); 1017 } 1018 1019 void checkIncomingCall(String pkg) { 1020 int uid = Binder.getCallingUid(); 1021 if (uid == Process.SYSTEM_UID || uid == 0) { 1022 return; 1023 } 1024 try { 1025 ApplicationInfo ai = mContext.getPackageManager().getApplicationInfo( 1026 pkg, 0); 1027 if (ai.uid != uid) { 1028 throw new SecurityException("Calling uid " + uid + " gave package" 1029 + pkg + " which is owned by uid " + ai.uid); 1030 } 1031 } catch (PackageManager.NameNotFoundException e) { 1032 throw new SecurityException("Unknown package " + pkg); 1033 } 1034 } 1035 1036 void cancelAll() { 1037 synchronized (mNotificationList) { 1038 final int N = mNotificationList.size(); 1039 for (int i=N-1; i>=0; i--) { 1040 NotificationRecord r = mNotificationList.get(i); 1041 1042 if ((r.notification.flags & (Notification.FLAG_ONGOING_EVENT 1043 | Notification.FLAG_NO_CLEAR)) == 0) { 1044 if (r.notification.deleteIntent != null) { 1045 try { 1046 r.notification.deleteIntent.send(); 1047 } catch (PendingIntent.CanceledException ex) { 1048 // do nothing - there's no relevant way to recover, and 1049 // no reason to let this propagate 1050 Slog.w(TAG, "canceled PendingIntent for " + r.pkg, ex); 1051 } 1052 } 1053 mNotificationList.remove(i); 1054 cancelNotificationLocked(r); 1055 } 1056 } 1057 1058 updateLightsLocked(); 1059 } 1060 } 1061 1062 private void updateLights() { 1063 synchronized (mNotificationList) { 1064 updateLightsLocked(); 1065 } 1066 } 1067 1068 // lock on mNotificationList 1069 private void updateLightsLocked() 1070 { 1071 // Battery low always shows, other states only show if charging. 1072 if (mBatteryLow) { 1073 if (mBatteryCharging) { 1074 mBatteryLight.setColor(BATTERY_LOW_ARGB); 1075 } else { 1076 // Flash when battery is low and not charging 1077 mBatteryLight.setFlashing(BATTERY_LOW_ARGB, LightsService.LIGHT_FLASH_TIMED, 1078 BATTERY_BLINK_ON, BATTERY_BLINK_OFF); 1079 } 1080 } else if (mBatteryCharging) { 1081 if (mBatteryFull) { 1082 mBatteryLight.setColor(BATTERY_FULL_ARGB); 1083 } else { 1084 mBatteryLight.setColor(BATTERY_MEDIUM_ARGB); 1085 } 1086 } else { 1087 mBatteryLight.turnOff(); 1088 } 1089 1090 // handle notification lights 1091 if (mLedNotification == null) { 1092 // get next notification, if any 1093 int n = mLights.size(); 1094 if (n > 0) { 1095 mLedNotification = mLights.get(n-1); 1096 } 1097 } 1098 1099 // we only flash if screen is off and persistent pulsing is enabled 1100 // and we are not currently in a call 1101 if (mLedNotification == null || mScreenOn || mInCall) { 1102 mNotificationLight.turnOff(); 1103 } else { 1104 int ledARGB = mLedNotification.notification.ledARGB; 1105 int ledOnMS = mLedNotification.notification.ledOnMS; 1106 int ledOffMS = mLedNotification.notification.ledOffMS; 1107 if ((mLedNotification.notification.defaults & Notification.DEFAULT_LIGHTS) != 0) { 1108 ledARGB = mDefaultNotificationColor; 1109 ledOnMS = mDefaultNotificationLedOn; 1110 ledOffMS = mDefaultNotificationLedOff; 1111 } 1112 if (mNotificationPulseEnabled) { 1113 // pulse repeatedly 1114 mNotificationLight.setFlashing(ledARGB, LightsService.LIGHT_FLASH_TIMED, 1115 ledOnMS, ledOffMS); 1116 } else { 1117 // pulse only once 1118 mNotificationLight.pulse(ledARGB, ledOnMS); 1119 } 1120 } 1121 } 1122 1123 // lock on mNotificationList 1124 private int indexOfNotificationLocked(String pkg, String tag, int id) 1125 { 1126 ArrayList<NotificationRecord> list = mNotificationList; 1127 final int len = list.size(); 1128 for (int i=0; i<len; i++) { 1129 NotificationRecord r = list.get(i); 1130 if (tag == null) { 1131 if (r.tag != null) { 1132 continue; 1133 } 1134 } else { 1135 if (!tag.equals(r.tag)) { 1136 continue; 1137 } 1138 } 1139 if (r.id == id && r.pkg.equals(pkg)) { 1140 return i; 1141 } 1142 } 1143 return -1; 1144 } 1145 1146 // This is here instead of StatusBarPolicy because it is an important 1147 // security feature that we don't want people customizing the platform 1148 // to accidentally lose. 1149 private void updateAdbNotification(boolean adbEnabled) { 1150 if (adbEnabled) { 1151 if ("0".equals(SystemProperties.get("persist.adb.notify"))) { 1152 return; 1153 } 1154 if (!mAdbNotificationShown) { 1155 NotificationManager notificationManager = (NotificationManager) mContext 1156 .getSystemService(Context.NOTIFICATION_SERVICE); 1157 if (notificationManager != null) { 1158 Resources r = mContext.getResources(); 1159 CharSequence title = r.getText( 1160 com.android.internal.R.string.adb_active_notification_title); 1161 CharSequence message = r.getText( 1162 com.android.internal.R.string.adb_active_notification_message); 1163 1164 if (mAdbNotification == null) { 1165 mAdbNotification = new Notification(); 1166 mAdbNotification.icon = com.android.internal.R.drawable.stat_sys_adb; 1167 mAdbNotification.when = 0; 1168 mAdbNotification.flags = Notification.FLAG_ONGOING_EVENT; 1169 mAdbNotification.tickerText = title; 1170 mAdbNotification.defaults = 0; // please be quiet 1171 mAdbNotification.sound = null; 1172 mAdbNotification.vibrate = null; 1173 } 1174 1175 Intent intent = new Intent( 1176 Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS); 1177 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | 1178 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); 1179 // Note: we are hard-coding the component because this is 1180 // an important security UI that we don't want anyone 1181 // intercepting. 1182 intent.setComponent(new ComponentName("com.android.settings", 1183 "com.android.settings.DevelopmentSettings")); 1184 PendingIntent pi = PendingIntent.getActivity(mContext, 0, 1185 intent, 0); 1186 1187 mAdbNotification.setLatestEventInfo(mContext, title, message, pi); 1188 1189 mAdbNotificationShown = true; 1190 notificationManager.notify( 1191 com.android.internal.R.string.adb_active_notification_title, 1192 mAdbNotification); 1193 } 1194 } 1195 1196 } else if (mAdbNotificationShown) { 1197 NotificationManager notificationManager = (NotificationManager) mContext 1198 .getSystemService(Context.NOTIFICATION_SERVICE); 1199 if (notificationManager != null) { 1200 mAdbNotificationShown = false; 1201 notificationManager.cancel( 1202 com.android.internal.R.string.adb_active_notification_title); 1203 } 1204 } 1205 } 1206 1207 private void updateNotificationPulse() { 1208 synchronized (mNotificationList) { 1209 updateLightsLocked(); 1210 } 1211 } 1212 1213 // ====================================================================== 1214 @Override 1215 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1216 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) 1217 != PackageManager.PERMISSION_GRANTED) { 1218 pw.println("Permission Denial: can't dump NotificationManager from from pid=" 1219 + Binder.getCallingPid() 1220 + ", uid=" + Binder.getCallingUid()); 1221 return; 1222 } 1223 1224 pw.println("Current Notification Manager state:"); 1225 1226 int N; 1227 1228 synchronized (mToastQueue) { 1229 N = mToastQueue.size(); 1230 if (N > 0) { 1231 pw.println(" Toast Queue:"); 1232 for (int i=0; i<N; i++) { 1233 mToastQueue.get(i).dump(pw, " "); 1234 } 1235 pw.println(" "); 1236 } 1237 1238 } 1239 1240 synchronized (mNotificationList) { 1241 N = mNotificationList.size(); 1242 if (N > 0) { 1243 pw.println(" Notification List:"); 1244 for (int i=0; i<N; i++) { 1245 mNotificationList.get(i).dump(pw, " ", mContext); 1246 } 1247 pw.println(" "); 1248 } 1249 1250 N = mLights.size(); 1251 if (N > 0) { 1252 pw.println(" Lights List:"); 1253 for (int i=0; i<N; i++) { 1254 mLights.get(i).dump(pw, " ", mContext); 1255 } 1256 pw.println(" "); 1257 } 1258 1259 pw.println(" mSoundNotification=" + mSoundNotification); 1260 pw.println(" mSound=" + mSound); 1261 pw.println(" mVibrateNotification=" + mVibrateNotification); 1262 pw.println(" mDisabledNotifications=0x" + Integer.toHexString(mDisabledNotifications)); 1263 pw.println(" mSystemReady=" + mSystemReady); 1264 } 1265 } 1266} 1267