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