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