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 static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; 20 import static org.xmlpull.v1.XmlPullParser.END_TAG; 21 import static org.xmlpull.v1.XmlPullParser.START_TAG; 22 23 import android.app.ActivityManager; 24 import android.app.ActivityManagerNative; 25 import android.app.AppGlobals; 26 import android.app.IActivityManager; 27 import android.app.INotificationManager; 28 import android.app.ITransientNotification; 29 import android.app.Notification; 30 import android.app.PendingIntent; 31 import android.app.StatusBarManager; 32 import android.content.BroadcastReceiver; 33 import android.content.ContentResolver; 34 import android.content.Context; 35 import android.content.Intent; 36 import android.content.IntentFilter; 37 import android.content.pm.ApplicationInfo; 38 import android.content.pm.PackageManager; 39 import android.content.pm.PackageManager.NameNotFoundException; 40 import android.content.res.Resources; 41 import android.database.ContentObserver; 42 import android.media.AudioManager; 43 import android.media.IAudioService; 44 import android.media.IRingtonePlayer; 45 import android.net.Uri; 46 import android.os.Binder; 47 import android.os.Handler; 48 import android.os.IBinder; 49 import android.os.Message; 50 import android.os.Process; 51 import android.os.RemoteException; 52 import android.os.ServiceManager; 53 import android.os.UserHandle; 54 import android.os.Vibrator; 55 import android.provider.Settings; 56 import android.telephony.TelephonyManager; 57 import android.text.TextUtils; 58 import android.util.AtomicFile; 59 import android.util.EventLog; 60 import android.util.Log; 61 import android.util.Slog; 62 import android.util.Xml; 63 import android.view.accessibility.AccessibilityEvent; 64 import android.view.accessibility.AccessibilityManager; 65 import android.widget.RemoteViews; 66 import android.widget.Toast; 67 68 import com.android.internal.statusbar.StatusBarNotification; 69 import com.android.internal.util.FastXmlSerializer; 70 71 import org.xmlpull.v1.XmlPullParser; 72 import org.xmlpull.v1.XmlPullParserException; 73 import org.xmlpull.v1.XmlSerializer; 74 75 import java.io.File; 76 import java.io.FileDescriptor; 77 import java.io.FileInputStream; 78 import java.io.FileNotFoundException; 79 import java.io.FileOutputStream; 80 import java.io.IOException; 81 import java.io.PrintWriter; 82 import java.util.ArrayList; 83 import java.util.Arrays; 84 import java.util.HashSet; 85 86 import libcore.io.IoUtils; 87 88 89 /** {@hide} */ 90 public class NotificationManagerService extends INotificationManager.Stub 91 { 92 private static final String TAG = "NotificationService"; 93 private static final boolean DBG = false; 94 95 private static final int MAX_PACKAGE_NOTIFICATIONS = 50; 96 97 // message codes 98 private static final int MESSAGE_TIMEOUT = 2; 99 100 private static final int LONG_DELAY = 3500; // 3.5 seconds 101 private static final int SHORT_DELAY = 2000; // 2 seconds 102 103 private static final long[] DEFAULT_VIBRATE_PATTERN = {0, 250, 250, 250}; 104 105 private static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_NOTIFICATION; 106 private static final boolean SCORE_ONGOING_HIGHER = false; 107 108 private static final int JUNK_SCORE = -1000; 109 private static final int NOTIFICATION_PRIORITY_MULTIPLIER = 10; 110 private static final int SCORE_DISPLAY_THRESHOLD = Notification.PRIORITY_MIN * NOTIFICATION_PRIORITY_MULTIPLIER; 111 112 private static final boolean ENABLE_BLOCKED_NOTIFICATIONS = true; 113 private static final boolean ENABLE_BLOCKED_TOASTS = true; 114 115 final Context mContext; 116 final IActivityManager mAm; 117 final IBinder mForegroundToken = new Binder(); 118 119 private WorkerHandler mHandler; 120 private StatusBarManagerService mStatusBar; 121 private LightsService.Light mNotificationLight; 122 private LightsService.Light mAttentionLight; 123 124 private int mDefaultNotificationColor; 125 private int mDefaultNotificationLedOn; 126 private int mDefaultNotificationLedOff; 127 128 private boolean mSystemReady; 129 private int mDisabledNotifications; 130 131 private NotificationRecord mSoundNotification; 132 private NotificationRecord mVibrateNotification; 133 134 private IAudioService mAudioService; 135 private Vibrator mVibrator; 136 137 // for enabling and disabling notification pulse behavior 138 private boolean mScreenOn = true; 139 private boolean mInCall = false; 140 private boolean mNotificationPulseEnabled; 141 142 private final ArrayList<NotificationRecord> mNotificationList = 143 new ArrayList<NotificationRecord>(); 144 145 private ArrayList<ToastRecord> mToastQueue; 146 147 private ArrayList<NotificationRecord> mLights = new ArrayList<NotificationRecord>(); 148 private NotificationRecord mLedNotification; 149 150 // Notification control database. For now just contains disabled packages. 151 private AtomicFile mPolicyFile; 152 private HashSet<String> mBlockedPackages = new HashSet<String>(); 153 154 private static final int DB_VERSION = 1; 155 156 private static final String TAG_BODY = "notification-policy"; 157 private static final String ATTR_VERSION = "version"; 158 159 private static final String TAG_BLOCKED_PKGS = "blocked-packages"; 160 private static final String TAG_PACKAGE = "package"; 161 private static final String ATTR_NAME = "name"; 162 163 private void loadBlockDb() { 164 synchronized(mBlockedPackages) { 165 if (mPolicyFile == null) { 166 File dir = new File("/data/system"); 167 mPolicyFile = new AtomicFile(new File(dir, "notification_policy.xml")); 168 169 mBlockedPackages.clear(); 170 171 FileInputStream infile = null; 172 try { 173 infile = mPolicyFile.openRead(); 174 final XmlPullParser parser = Xml.newPullParser(); 175 parser.setInput(infile, null); 176 177 int type; 178 String tag; 179 int version = DB_VERSION; 180 while ((type = parser.next()) != END_DOCUMENT) { 181 tag = parser.getName(); 182 if (type == START_TAG) { 183 if (TAG_BODY.equals(tag)) { 184 version = Integer.parseInt(parser.getAttributeValue(null, ATTR_VERSION)); 185 } else if (TAG_BLOCKED_PKGS.equals(tag)) { 186 while ((type = parser.next()) != END_DOCUMENT) { 187 tag = parser.getName(); 188 if (TAG_PACKAGE.equals(tag)) { 189 mBlockedPackages.add(parser.getAttributeValue(null, ATTR_NAME)); 190 } else if (TAG_BLOCKED_PKGS.equals(tag) && type == END_TAG) { 191 break; 192 } 193 } 194 } 195 } 196 } 197 } catch (FileNotFoundException e) { 198 // No data yet 199 } catch (IOException e) { 200 Log.wtf(TAG, "Unable to read blocked notifications database", e); 201 } catch (NumberFormatException e) { 202 Log.wtf(TAG, "Unable to parse blocked notifications database", e); 203 } catch (XmlPullParserException e) { 204 Log.wtf(TAG, "Unable to parse blocked notifications database", e); 205 } finally { 206 IoUtils.closeQuietly(infile); 207 } 208 } 209 } 210 } 211 212 private void writeBlockDb() { 213 synchronized(mBlockedPackages) { 214 FileOutputStream outfile = null; 215 try { 216 outfile = mPolicyFile.startWrite(); 217 218 XmlSerializer out = new FastXmlSerializer(); 219 out.setOutput(outfile, "utf-8"); 220 221 out.startDocument(null, true); 222 223 out.startTag(null, TAG_BODY); { 224 out.attribute(null, ATTR_VERSION, String.valueOf(DB_VERSION)); 225 out.startTag(null, TAG_BLOCKED_PKGS); { 226 // write all known network policies 227 for (String pkg : mBlockedPackages) { 228 out.startTag(null, TAG_PACKAGE); { 229 out.attribute(null, ATTR_NAME, pkg); 230 } out.endTag(null, TAG_PACKAGE); 231 } 232 } out.endTag(null, TAG_BLOCKED_PKGS); 233 } out.endTag(null, TAG_BODY); 234 235 out.endDocument(); 236 237 mPolicyFile.finishWrite(outfile); 238 } catch (IOException e) { 239 if (outfile != null) { 240 mPolicyFile.failWrite(outfile); 241 } 242 } 243 } 244 } 245 246 public boolean areNotificationsEnabledForPackage(String pkg) { 247 checkCallerIsSystem(); 248 return areNotificationsEnabledForPackageInt(pkg); 249 } 250 251 // Unchecked. Not exposed via Binder, but can be called in the course of enqueue*(). 252 private boolean areNotificationsEnabledForPackageInt(String pkg) { 253 final boolean enabled = !mBlockedPackages.contains(pkg); 254 if (DBG) { 255 Slog.v(TAG, "notifications are " + (enabled?"en":"dis") + "abled for " + pkg); 256 } 257 return enabled; 258 } 259 260 public void setNotificationsEnabledForPackage(String pkg, boolean enabled) { 261 checkCallerIsSystem(); 262 if (DBG) { 263 Slog.v(TAG, (enabled?"en":"dis") + "abling notifications for " + pkg); 264 } 265 if (enabled) { 266 mBlockedPackages.remove(pkg); 267 } else { 268 mBlockedPackages.add(pkg); 269 270 // Now, cancel any outstanding notifications that are part of a just-disabled app 271 if (ENABLE_BLOCKED_NOTIFICATIONS) { 272 synchronized (mNotificationList) { 273 final int N = mNotificationList.size(); 274 for (int i=0; i<N; i++) { 275 final NotificationRecord r = mNotificationList.get(i); 276 if (r.pkg.equals(pkg)) { 277 cancelNotificationLocked(r, false); 278 } 279 } 280 } 281 } 282 // Don't bother canceling toasts, they'll go away soon enough. 283 } 284 writeBlockDb(); 285 } 286 287 288 private static String idDebugString(Context baseContext, String packageName, int id) { 289 Context c = null; 290 291 if (packageName != null) { 292 try { 293 c = baseContext.createPackageContext(packageName, 0); 294 } catch (NameNotFoundException e) { 295 c = baseContext; 296 } 297 } else { 298 c = baseContext; 299 } 300 301 String pkg; 302 String type; 303 String name; 304 305 Resources r = c.getResources(); 306 try { 307 return r.getResourceName(id); 308 } catch (Resources.NotFoundException e) { 309 return "<name unknown>"; 310 } 311 } 312 313 private static final class NotificationRecord 314 { 315 final String pkg; 316 final String tag; 317 final int id; 318 final int uid; 319 final int initialPid; 320 final int userId; 321 final Notification notification; 322 final int score; 323 IBinder statusBarKey; 324 325 NotificationRecord(String pkg, String tag, int id, int uid, int initialPid, 326 int userId, int score, Notification notification) 327 { 328 this.pkg = pkg; 329 this.tag = tag; 330 this.id = id; 331 this.uid = uid; 332 this.initialPid = initialPid; 333 this.userId = userId; 334 this.score = score; 335 this.notification = notification; 336 } 337 338 void dump(PrintWriter pw, String prefix, Context baseContext) { 339 pw.println(prefix + this); 340 pw.println(prefix + " icon=0x" + Integer.toHexString(notification.icon) 341 + " / " + idDebugString(baseContext, this.pkg, notification.icon)); 342 pw.println(prefix + " pri=" + notification.priority); 343 pw.println(prefix + " score=" + this.score); 344 pw.println(prefix + " contentIntent=" + notification.contentIntent); 345 pw.println(prefix + " deleteIntent=" + notification.deleteIntent); 346 pw.println(prefix + " tickerText=" + notification.tickerText); 347 pw.println(prefix + " contentView=" + notification.contentView); 348 pw.println(prefix + " uid=" + uid + " userId=" + userId); 349 pw.println(prefix + " defaults=0x" + Integer.toHexString(notification.defaults)); 350 pw.println(prefix + " flags=0x" + Integer.toHexString(notification.flags)); 351 pw.println(prefix + " sound=" + notification.sound); 352 pw.println(prefix + " vibrate=" + Arrays.toString(notification.vibrate)); 353 pw.println(prefix + " ledARGB=0x" + Integer.toHexString(notification.ledARGB) 354 + " ledOnMS=" + notification.ledOnMS 355 + " ledOffMS=" + notification.ledOffMS); 356 } 357 358 @Override 359 public final String toString() 360 { 361 return "NotificationRecord{" 362 + Integer.toHexString(System.identityHashCode(this)) 363 + " pkg=" + pkg 364 + " id=" + Integer.toHexString(id) 365 + " tag=" + tag 366 + " score=" + score 367 + "}"; 368 } 369 } 370 371 private static final class ToastRecord 372 { 373 final int pid; 374 final String pkg; 375 final ITransientNotification callback; 376 int duration; 377 378 ToastRecord(int pid, String pkg, ITransientNotification callback, int duration) 379 { 380 this.pid = pid; 381 this.pkg = pkg; 382 this.callback = callback; 383 this.duration = duration; 384 } 385 386 void update(int duration) { 387 this.duration = duration; 388 } 389 390 void dump(PrintWriter pw, String prefix) { 391 pw.println(prefix + this); 392 } 393 394 @Override 395 public final String toString() 396 { 397 return "ToastRecord{" 398 + Integer.toHexString(System.identityHashCode(this)) 399 + " pkg=" + pkg 400 + " callback=" + callback 401 + " duration=" + duration; 402 } 403 } 404 405 private StatusBarManagerService.NotificationCallbacks mNotificationCallbacks 406 = new StatusBarManagerService.NotificationCallbacks() { 407 408 public void onSetDisabled(int status) { 409 synchronized (mNotificationList) { 410 mDisabledNotifications = status; 411 if ((mDisabledNotifications & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) { 412 // cancel whatever's going on 413 long identity = Binder.clearCallingIdentity(); 414 try { 415 final IRingtonePlayer player = mAudioService.getRingtonePlayer(); 416 if (player != null) { 417 player.stopAsync(); 418 } 419 } catch (RemoteException e) { 420 } finally { 421 Binder.restoreCallingIdentity(identity); 422 } 423 424 identity = Binder.clearCallingIdentity(); 425 try { 426 mVibrator.cancel(); 427 } finally { 428 Binder.restoreCallingIdentity(identity); 429 } 430 } 431 } 432 } 433 434 public void onClearAll() { 435 // XXX to be totally correct, the caller should tell us which user 436 // this is for. 437 cancelAll(ActivityManager.getCurrentUser()); 438 } 439 440 public void onNotificationClick(String pkg, String tag, int id) { 441 // XXX to be totally correct, the caller should tell us which user 442 // this is for. 443 cancelNotification(pkg, tag, id, Notification.FLAG_AUTO_CANCEL, 444 Notification.FLAG_FOREGROUND_SERVICE, false, 445 ActivityManager.getCurrentUser()); 446 } 447 448 public void onNotificationClear(String pkg, String tag, int id) { 449 // XXX to be totally correct, the caller should tell us which user 450 // this is for. 451 cancelNotification(pkg, tag, id, 0, 452 Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE, 453 true, ActivityManager.getCurrentUser()); 454 } 455 456 public void onPanelRevealed() { 457 synchronized (mNotificationList) { 458 // sound 459 mSoundNotification = null; 460 461 long identity = Binder.clearCallingIdentity(); 462 try { 463 final IRingtonePlayer player = mAudioService.getRingtonePlayer(); 464 if (player != null) { 465 player.stopAsync(); 466 } 467 } catch (RemoteException e) { 468 } finally { 469 Binder.restoreCallingIdentity(identity); 470 } 471 472 // vibrate 473 mVibrateNotification = null; 474 identity = Binder.clearCallingIdentity(); 475 try { 476 mVibrator.cancel(); 477 } finally { 478 Binder.restoreCallingIdentity(identity); 479 } 480 481 // light 482 mLights.clear(); 483 mLedNotification = null; 484 updateLightsLocked(); 485 } 486 } 487 488 public void onNotificationError(String pkg, String tag, int id, 489 int uid, int initialPid, String message) { 490 Slog.d(TAG, "onNotification error pkg=" + pkg + " tag=" + tag + " id=" + id 491 + "; will crashApplication(uid=" + uid + ", pid=" + initialPid + ")"); 492 // XXX to be totally correct, the caller should tell us which user 493 // this is for. 494 cancelNotification(pkg, tag, id, 0, 0, false, UserHandle.getUserId(uid)); 495 long ident = Binder.clearCallingIdentity(); 496 try { 497 ActivityManagerNative.getDefault().crashApplication(uid, initialPid, pkg, 498 "Bad notification posted from package " + pkg 499 + ": " + message); 500 } catch (RemoteException e) { 501 } 502 Binder.restoreCallingIdentity(ident); 503 } 504 }; 505 506 private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { 507 @Override 508 public void onReceive(Context context, Intent intent) { 509 String action = intent.getAction(); 510 511 boolean queryRestart = false; 512 boolean packageChanged = false; 513 514 if (action.equals(Intent.ACTION_PACKAGE_REMOVED) 515 || action.equals(Intent.ACTION_PACKAGE_RESTARTED) 516 || (packageChanged=action.equals(Intent.ACTION_PACKAGE_CHANGED)) 517 || (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART)) 518 || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) { 519 String pkgList[] = null; 520 if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) { 521 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); 522 } else if (queryRestart) { 523 pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES); 524 } else { 525 Uri uri = intent.getData(); 526 if (uri == null) { 527 return; 528 } 529 String pkgName = uri.getSchemeSpecificPart(); 530 if (pkgName == null) { 531 return; 532 } 533 if (packageChanged) { 534 // We cancel notifications for packages which have just been disabled 535 final int enabled = mContext.getPackageManager() 536 .getApplicationEnabledSetting(pkgName); 537 if (enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED 538 || enabled == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) { 539 return; 540 } 541 } 542 pkgList = new String[]{pkgName}; 543 } 544 if (pkgList != null && (pkgList.length > 0)) { 545 for (String pkgName : pkgList) { 546 cancelAllNotificationsInt(pkgName, 0, 0, !queryRestart, 547 UserHandle.USER_ALL); 548 } 549 } 550 } else if (action.equals(Intent.ACTION_SCREEN_ON)) { 551 // Keep track of screen on/off state, but do not turn off the notification light 552 // until user passes through the lock screen or views the notification. 553 mScreenOn = true; 554 } else if (action.equals(Intent.ACTION_SCREEN_OFF)) { 555 mScreenOn = false; 556 } else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) { 557 mInCall = (intent.getStringExtra(TelephonyManager.EXTRA_STATE).equals( 558 TelephonyManager.EXTRA_STATE_OFFHOOK)); 559 updateNotificationPulse(); 560 } else if (action.equals(Intent.ACTION_USER_STOPPED)) { 561 int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); 562 if (userHandle >= 0) { 563 cancelAllNotificationsInt(null, 0, 0, true, userHandle); 564 } 565 } else if (action.equals(Intent.ACTION_USER_PRESENT)) { 566 // turn off LED when user passes through lock screen 567 mNotificationLight.turnOff(); 568 } 569 } 570 }; 571 572 class SettingsObserver extends ContentObserver { 573 SettingsObserver(Handler handler) { 574 super(handler); 575 } 576 577 void observe() { 578 ContentResolver resolver = mContext.getContentResolver(); 579 resolver.registerContentObserver(Settings.System.getUriFor( 580 Settings.System.NOTIFICATION_LIGHT_PULSE), false, this); 581 update(); 582 } 583 584 @Override public void onChange(boolean selfChange) { 585 update(); 586 } 587 588 public void update() { 589 ContentResolver resolver = mContext.getContentResolver(); 590 boolean pulseEnabled = Settings.System.getInt(resolver, 591 Settings.System.NOTIFICATION_LIGHT_PULSE, 0) != 0; 592 if (mNotificationPulseEnabled != pulseEnabled) { 593 mNotificationPulseEnabled = pulseEnabled; 594 updateNotificationPulse(); 595 } 596 } 597 } 598 599 NotificationManagerService(Context context, StatusBarManagerService statusBar, 600 LightsService lights) 601 { 602 super(); 603 mContext = context; 604 mVibrator = (Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE); 605 mAm = ActivityManagerNative.getDefault(); 606 mToastQueue = new ArrayList<ToastRecord>(); 607 mHandler = new WorkerHandler(); 608 609 loadBlockDb(); 610 611 mStatusBar = statusBar; 612 statusBar.setNotificationCallbacks(mNotificationCallbacks); 613 614 mNotificationLight = lights.getLight(LightsService.LIGHT_ID_NOTIFICATIONS); 615 mAttentionLight = lights.getLight(LightsService.LIGHT_ID_ATTENTION); 616 617 Resources resources = mContext.getResources(); 618 mDefaultNotificationColor = resources.getColor( 619 com.android.internal.R.color.config_defaultNotificationColor); 620 mDefaultNotificationLedOn = resources.getInteger( 621 com.android.internal.R.integer.config_defaultNotificationLedOn); 622 mDefaultNotificationLedOff = resources.getInteger( 623 com.android.internal.R.integer.config_defaultNotificationLedOff); 624 625 // Don't start allowing notifications until the setup wizard has run once. 626 // After that, including subsequent boots, init with notifications turned on. 627 // This works on the first boot because the setup wizard will toggle this 628 // flag at least once and we'll go back to 0 after that. 629 if (0 == Settings.Global.getInt(mContext.getContentResolver(), 630 Settings.Global.DEVICE_PROVISIONED, 0)) { 631 mDisabledNotifications = StatusBarManager.DISABLE_NOTIFICATION_ALERTS; 632 } 633 634 // register for various Intents 635 IntentFilter filter = new IntentFilter(); 636 filter.addAction(Intent.ACTION_SCREEN_ON); 637 filter.addAction(Intent.ACTION_SCREEN_OFF); 638 filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED); 639 filter.addAction(Intent.ACTION_USER_PRESENT); 640 filter.addAction(Intent.ACTION_USER_STOPPED); 641 mContext.registerReceiver(mIntentReceiver, filter); 642 IntentFilter pkgFilter = new IntentFilter(); 643 pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); 644 pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED); 645 pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED); 646 pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART); 647 pkgFilter.addDataScheme("package"); 648 mContext.registerReceiver(mIntentReceiver, pkgFilter); 649 IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); 650 mContext.registerReceiver(mIntentReceiver, sdFilter); 651 652 SettingsObserver observer = new SettingsObserver(mHandler); 653 observer.observe(); 654 } 655 656 void systemReady() { 657 mAudioService = IAudioService.Stub.asInterface( 658 ServiceManager.getService(Context.AUDIO_SERVICE)); 659 660 // no beeping until we're basically done booting 661 mSystemReady = true; 662 } 663 664 // Toasts 665 // ============================================================================ 666 public void enqueueToast(String pkg, ITransientNotification callback, int duration) 667 { 668 if (DBG) Slog.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback + " duration=" + duration); 669 670 if (pkg == null || callback == null) { 671 Slog.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback); 672 return ; 673 } 674 675 final boolean isSystemToast = ("android".equals(pkg)); 676 677 if (ENABLE_BLOCKED_TOASTS && !isSystemToast && !areNotificationsEnabledForPackageInt(pkg)) { 678 Slog.e(TAG, "Suppressing toast from package " + pkg + " by user request."); 679 return; 680 } 681 682 synchronized (mToastQueue) { 683 int callingPid = Binder.getCallingPid(); 684 long callingId = Binder.clearCallingIdentity(); 685 try { 686 ToastRecord record; 687 int index = indexOfToastLocked(pkg, callback); 688 // If it's already in the queue, we update it in place, we don't 689 // move it to the end of the queue. 690 if (index >= 0) { 691 record = mToastQueue.get(index); 692 record.update(duration); 693 } else { 694 // Limit the number of toasts that any given package except the android 695 // package can enqueue. Prevents DOS attacks and deals with leaks. 696 if (!isSystemToast) { 697 int count = 0; 698 final int N = mToastQueue.size(); 699 for (int i=0; i<N; i++) { 700 final ToastRecord r = mToastQueue.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 + " toasts. Not showing more. Package=" + pkg); 706 return; 707 } 708 } 709 } 710 } 711 712 record = new ToastRecord(callingPid, pkg, callback, duration); 713 mToastQueue.add(record); 714 index = mToastQueue.size() - 1; 715 keepProcessAliveLocked(callingPid); 716 } 717 // If it's at index 0, it's the current toast. It doesn't matter if it's 718 // new or just been updated. Call back and tell it to show itself. 719 // If the callback fails, this will remove it from the list, so don't 720 // assume that it's valid after this. 721 if (index == 0) { 722 showNextToastLocked(); 723 } 724 } finally { 725 Binder.restoreCallingIdentity(callingId); 726 } 727 } 728 } 729 730 public void cancelToast(String pkg, ITransientNotification callback) { 731 Slog.i(TAG, "cancelToast pkg=" + pkg + " callback=" + callback); 732 733 if (pkg == null || callback == null) { 734 Slog.e(TAG, "Not cancelling notification. pkg=" + pkg + " callback=" + callback); 735 return ; 736 } 737 738 synchronized (mToastQueue) { 739 long callingId = Binder.clearCallingIdentity(); 740 try { 741 int index = indexOfToastLocked(pkg, callback); 742 if (index >= 0) { 743 cancelToastLocked(index); 744 } else { 745 Slog.w(TAG, "Toast already cancelled. pkg=" + pkg + " callback=" + callback); 746 } 747 } finally { 748 Binder.restoreCallingIdentity(callingId); 749 } 750 } 751 } 752 753 private void showNextToastLocked() { 754 ToastRecord record = mToastQueue.get(0); 755 while (record != null) { 756 if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback); 757 try { 758 record.callback.show(); 759 scheduleTimeoutLocked(record, false); 760 return; 761 } catch (RemoteException e) { 762 Slog.w(TAG, "Object died trying to show notification " + record.callback 763 + " in package " + record.pkg); 764 // remove it from the list and let the process die 765 int index = mToastQueue.indexOf(record); 766 if (index >= 0) { 767 mToastQueue.remove(index); 768 } 769 keepProcessAliveLocked(record.pid); 770 if (mToastQueue.size() > 0) { 771 record = mToastQueue.get(0); 772 } else { 773 record = null; 774 } 775 } 776 } 777 } 778 779 private void cancelToastLocked(int index) { 780 ToastRecord record = mToastQueue.get(index); 781 try { 782 record.callback.hide(); 783 } catch (RemoteException e) { 784 Slog.w(TAG, "Object died trying to hide notification " + record.callback 785 + " in package " + record.pkg); 786 // don't worry about this, we're about to remove it from 787 // the list anyway 788 } 789 mToastQueue.remove(index); 790 keepProcessAliveLocked(record.pid); 791 if (mToastQueue.size() > 0) { 792 // Show the next one. If the callback fails, this will remove 793 // it from the list, so don't assume that the list hasn't changed 794 // after this point. 795 showNextToastLocked(); 796 } 797 } 798 799 private void scheduleTimeoutLocked(ToastRecord r, boolean immediate) 800 { 801 Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r); 802 long delay = immediate ? 0 : (r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY); 803 mHandler.removeCallbacksAndMessages(r); 804 mHandler.sendMessageDelayed(m, delay); 805 } 806 807 private void handleTimeout(ToastRecord record) 808 { 809 if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback); 810 synchronized (mToastQueue) { 811 int index = indexOfToastLocked(record.pkg, record.callback); 812 if (index >= 0) { 813 cancelToastLocked(index); 814 } 815 } 816 } 817 818 // lock on mToastQueue 819 private int indexOfToastLocked(String pkg, ITransientNotification callback) 820 { 821 IBinder cbak = callback.asBinder(); 822 ArrayList<ToastRecord> list = mToastQueue; 823 int len = list.size(); 824 for (int i=0; i<len; i++) { 825 ToastRecord r = list.get(i); 826 if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) { 827 return i; 828 } 829 } 830 return -1; 831 } 832 833 // lock on mToastQueue 834 private void keepProcessAliveLocked(int pid) 835 { 836 int toastCount = 0; // toasts from this pid 837 ArrayList<ToastRecord> list = mToastQueue; 838 int N = list.size(); 839 for (int i=0; i<N; i++) { 840 ToastRecord r = list.get(i); 841 if (r.pid == pid) { 842 toastCount++; 843 } 844 } 845 try { 846 mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0); 847 } catch (RemoteException e) { 848 // Shouldn't happen. 849 } 850 } 851 852 private final class WorkerHandler extends Handler 853 { 854 @Override 855 public void handleMessage(Message msg) 856 { 857 switch (msg.what) 858 { 859 case MESSAGE_TIMEOUT: 860 handleTimeout((ToastRecord)msg.obj); 861 break; 862 } 863 } 864 } 865 866 867 // Notifications 868 // ============================================================================ 869 public void enqueueNotificationWithTag(String pkg, String tag, int id, Notification notification, 870 int[] idOut, int userId) 871 { 872 enqueueNotificationInternal(pkg, Binder.getCallingUid(), Binder.getCallingPid(), 873 tag, id, notification, idOut, userId); 874 } 875 876 private final static int clamp(int x, int low, int high) { 877 return (x < low) ? low : ((x > high) ? high : x); 878 } 879 880 // Not exposed via Binder; for system use only (otherwise malicious apps could spoof the 881 // uid/pid of another application) 882 public void enqueueNotificationInternal(String pkg, int callingUid, int callingPid, 883 String tag, int id, Notification notification, int[] idOut, int userId) 884 { 885 if (DBG) { 886 Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id + " notification=" + notification); 887 } 888 checkCallerIsSystemOrSameApp(pkg); 889 final boolean isSystemNotification = ("android".equals(pkg)); 890 891 userId = ActivityManager.handleIncomingUser(callingPid, 892 callingUid, userId, true, false, "enqueueNotification", pkg); 893 final UserHandle user = new UserHandle(userId); 894 895 // Limit the number of notifications that any given package except the android 896 // package can enqueue. Prevents DOS attacks and deals with leaks. 897 if (!isSystemNotification) { 898 synchronized (mNotificationList) { 899 int count = 0; 900 final int N = mNotificationList.size(); 901 for (int i=0; i<N; i++) { 902 final NotificationRecord r = mNotificationList.get(i); 903 if (r.pkg.equals(pkg) && r.userId == userId) { 904 count++; 905 if (count >= MAX_PACKAGE_NOTIFICATIONS) { 906 Slog.e(TAG, "Package has already posted " + count 907 + " notifications. Not showing more. package=" + pkg); 908 return; 909 } 910 } 911 } 912 } 913 } 914 915 // This conditional is a dirty hack to limit the logging done on 916 // behalf of the download manager without affecting other apps. 917 if (!pkg.equals("com.android.providers.downloads") 918 || Log.isLoggable("DownloadManager", Log.VERBOSE)) { 919 EventLog.writeEvent(EventLogTags.NOTIFICATION_ENQUEUE, pkg, id, tag, userId, 920 notification.toString()); 921 } 922 923 if (pkg == null || notification == null) { 924 throw new IllegalArgumentException("null not allowed: pkg=" + pkg 925 + " id=" + id + " notification=" + notification); 926 } 927 if (notification.icon != 0) { 928 if (notification.contentView == null) { 929 throw new IllegalArgumentException("contentView required: pkg=" + pkg 930 + " id=" + id + " notification=" + notification); 931 } 932 } 933 934 // === Scoring === 935 936 // 0. Sanitize inputs 937 notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN, Notification.PRIORITY_MAX); 938 // Migrate notification flags to scores 939 if (0 != (notification.flags & Notification.FLAG_HIGH_PRIORITY)) { 940 if (notification.priority < Notification.PRIORITY_MAX) notification.priority = Notification.PRIORITY_MAX; 941 } else if (SCORE_ONGOING_HIGHER && 0 != (notification.flags & Notification.FLAG_ONGOING_EVENT)) { 942 if (notification.priority < Notification.PRIORITY_HIGH) notification.priority = Notification.PRIORITY_HIGH; 943 } 944 945 // 1. initial score: buckets of 10, around the app 946 int score = notification.priority * NOTIFICATION_PRIORITY_MULTIPLIER; //[-20..20] 947 948 // 2. Consult external heuristics (TBD) 949 950 // 3. Apply local rules 951 952 // blocked apps 953 if (ENABLE_BLOCKED_NOTIFICATIONS && !isSystemNotification && !areNotificationsEnabledForPackageInt(pkg)) { 954 score = JUNK_SCORE; 955 Slog.e(TAG, "Suppressing notification from package " + pkg + " by user request."); 956 } 957 958 if (DBG) { 959 Slog.v(TAG, "Assigned score=" + score + " to " + notification); 960 } 961 962 if (score < SCORE_DISPLAY_THRESHOLD) { 963 // Notification will be blocked because the score is too low. 964 return; 965 } 966 967 synchronized (mNotificationList) { 968 NotificationRecord r = new NotificationRecord(pkg, tag, id, 969 callingUid, callingPid, userId, 970 score, 971 notification); 972 NotificationRecord old = null; 973 974 int index = indexOfNotificationLocked(pkg, tag, id, userId); 975 if (index < 0) { 976 mNotificationList.add(r); 977 } else { 978 old = mNotificationList.remove(index); 979 mNotificationList.add(index, r); 980 // Make sure we don't lose the foreground service state. 981 if (old != null) { 982 notification.flags |= 983 old.notification.flags&Notification.FLAG_FOREGROUND_SERVICE; 984 } 985 } 986 987 // Ensure if this is a foreground service that the proper additional 988 // flags are set. 989 if ((notification.flags&Notification.FLAG_FOREGROUND_SERVICE) != 0) { 990 notification.flags |= Notification.FLAG_ONGOING_EVENT 991 | Notification.FLAG_NO_CLEAR; 992 } 993 994 final int currentUser; 995 final long token = Binder.clearCallingIdentity(); 996 try { 997 currentUser = ActivityManager.getCurrentUser(); 998 } finally { 999 Binder.restoreCallingIdentity(token); 1000 } 1001 1002 if (notification.icon != 0) { 1003 final StatusBarNotification n = new StatusBarNotification( 1004 pkg, id, tag, r.uid, r.initialPid, score, notification, user); 1005 if (old != null && old.statusBarKey != null) { 1006 r.statusBarKey = old.statusBarKey; 1007 long identity = Binder.clearCallingIdentity(); 1008 try { 1009 mStatusBar.updateNotification(r.statusBarKey, n); 1010 } 1011 finally { 1012 Binder.restoreCallingIdentity(identity); 1013 } 1014 } else { 1015 long identity = Binder.clearCallingIdentity(); 1016 try { 1017 r.statusBarKey = mStatusBar.addNotification(n); 1018 if ((n.notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0) { 1019 mAttentionLight.pulse(); 1020 } 1021 } 1022 finally { 1023 Binder.restoreCallingIdentity(identity); 1024 } 1025 } 1026 // Send accessibility events only for the current user. 1027 if (currentUser == userId) { 1028 sendAccessibilityEvent(notification, pkg); 1029 } 1030 } else { 1031 Slog.e(TAG, "Ignoring notification with icon==0: " + notification); 1032 if (old != null && old.statusBarKey != null) { 1033 long identity = Binder.clearCallingIdentity(); 1034 try { 1035 mStatusBar.removeNotification(old.statusBarKey); 1036 } 1037 finally { 1038 Binder.restoreCallingIdentity(identity); 1039 } 1040 } 1041 } 1042 1043 // If we're not supposed to beep, vibrate, etc. then don't. 1044 if (((mDisabledNotifications & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) == 0) 1045 && (!(old != null 1046 && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 )) 1047 && (r.userId == UserHandle.USER_ALL || 1048 (r.userId == userId && r.userId == currentUser)) 1049 && mSystemReady) { 1050 1051 final AudioManager audioManager = (AudioManager) mContext 1052 .getSystemService(Context.AUDIO_SERVICE); 1053 // sound 1054 final boolean useDefaultSound = 1055 (notification.defaults & Notification.DEFAULT_SOUND) != 0; 1056 if (useDefaultSound || notification.sound != null) { 1057 Uri uri; 1058 if (useDefaultSound) { 1059 uri = Settings.System.DEFAULT_NOTIFICATION_URI; 1060 } else { 1061 uri = notification.sound; 1062 } 1063 boolean looping = (notification.flags & Notification.FLAG_INSISTENT) != 0; 1064 int audioStreamType; 1065 if (notification.audioStreamType >= 0) { 1066 audioStreamType = notification.audioStreamType; 1067 } else { 1068 audioStreamType = DEFAULT_STREAM_TYPE; 1069 } 1070 mSoundNotification = r; 1071 // do not play notifications if stream volume is 0 1072 // (typically because ringer mode is silent) or if speech recognition is active. 1073 if ((audioManager.getStreamVolume(audioStreamType) != 0) 1074 && !audioManager.isSpeechRecognitionActive()) { 1075 final long identity = Binder.clearCallingIdentity(); 1076 try { 1077 final IRingtonePlayer player = mAudioService.getRingtonePlayer(); 1078 if (player != null) { 1079 player.playAsync(uri, user, looping, audioStreamType); 1080 } 1081 } catch (RemoteException e) { 1082 } finally { 1083 Binder.restoreCallingIdentity(identity); 1084 } 1085 } 1086 } 1087 1088 // vibrate 1089 // new in 4.2: if there was supposed to be a sound and we're in vibrate mode, 1090 // we always vibrate, even if no vibration was specified 1091 final boolean convertSoundToVibration = 1092 notification.vibrate == null 1093 && (useDefaultSound || notification.sound != null) 1094 && (audioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE); 1095 1096 final boolean useDefaultVibrate = 1097 (notification.defaults & Notification.DEFAULT_VIBRATE) != 0 1098 || convertSoundToVibration; 1099 1100 if ((useDefaultVibrate || notification.vibrate != null) 1101 && !(audioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT)) { 1102 mVibrateNotification = r; 1103 1104 mVibrator.vibrate(useDefaultVibrate ? DEFAULT_VIBRATE_PATTERN 1105 : notification.vibrate, 1106 ((notification.flags & Notification.FLAG_INSISTENT) != 0) ? 0: -1); 1107 } 1108 } 1109 1110 // this option doesn't shut off the lights 1111 1112 // light 1113 // the most recent thing gets the light 1114 mLights.remove(old); 1115 if (mLedNotification == old) { 1116 mLedNotification = null; 1117 } 1118 //Slog.i(TAG, "notification.lights=" 1119 // + ((old.notification.lights.flags & Notification.FLAG_SHOW_LIGHTS) != 0)); 1120 if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0) { 1121 mLights.add(r); 1122 updateLightsLocked(); 1123 } else { 1124 if (old != null 1125 && ((old.notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0)) { 1126 updateLightsLocked(); 1127 } 1128 } 1129 } 1130 1131 idOut[0] = id; 1132 } 1133 1134 private void sendAccessibilityEvent(Notification notification, CharSequence packageName) { 1135 AccessibilityManager manager = AccessibilityManager.getInstance(mContext); 1136 if (!manager.isEnabled()) { 1137 return; 1138 } 1139 1140 AccessibilityEvent event = 1141 AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED); 1142 event.setPackageName(packageName); 1143 event.setClassName(Notification.class.getName()); 1144 event.setParcelableData(notification); 1145 CharSequence tickerText = notification.tickerText; 1146 if (!TextUtils.isEmpty(tickerText)) { 1147 event.getText().add(tickerText); 1148 } 1149 1150 manager.sendAccessibilityEvent(event); 1151 } 1152 1153 private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete) { 1154 // tell the app 1155 if (sendDelete) { 1156 if (r.notification.deleteIntent != null) { 1157 try { 1158 r.notification.deleteIntent.send(); 1159 } catch (PendingIntent.CanceledException ex) { 1160 // do nothing - there's no relevant way to recover, and 1161 // no reason to let this propagate 1162 Slog.w(TAG, "canceled PendingIntent for " + r.pkg, ex); 1163 } 1164 } 1165 } 1166 1167 // status bar 1168 if (r.notification.icon != 0) { 1169 long identity = Binder.clearCallingIdentity(); 1170 try { 1171 mStatusBar.removeNotification(r.statusBarKey); 1172 } 1173 finally { 1174 Binder.restoreCallingIdentity(identity); 1175 } 1176 r.statusBarKey = null; 1177 } 1178 1179 // sound 1180 if (mSoundNotification == r) { 1181 mSoundNotification = null; 1182 final long identity = Binder.clearCallingIdentity(); 1183 try { 1184 final IRingtonePlayer player = mAudioService.getRingtonePlayer(); 1185 if (player != null) { 1186 player.stopAsync(); 1187 } 1188 } catch (RemoteException e) { 1189 } finally { 1190 Binder.restoreCallingIdentity(identity); 1191 } 1192 } 1193 1194 // vibrate 1195 if (mVibrateNotification == r) { 1196 mVibrateNotification = null; 1197 long identity = Binder.clearCallingIdentity(); 1198 try { 1199 mVibrator.cancel(); 1200 } 1201 finally { 1202 Binder.restoreCallingIdentity(identity); 1203 } 1204 } 1205 1206 // light 1207 mLights.remove(r); 1208 if (mLedNotification == r) { 1209 mLedNotification = null; 1210 } 1211 } 1212 1213 /** 1214 * Cancels a notification ONLY if it has all of the {@code mustHaveFlags} 1215 * and none of the {@code mustNotHaveFlags}. 1216 */ 1217 private void cancelNotification(String pkg, String tag, int id, int mustHaveFlags, 1218 int mustNotHaveFlags, boolean sendDelete, int userId) { 1219 EventLog.writeEvent(EventLogTags.NOTIFICATION_CANCEL, pkg, id, tag, userId, 1220 mustHaveFlags, mustNotHaveFlags); 1221 1222 synchronized (mNotificationList) { 1223 int index = indexOfNotificationLocked(pkg, tag, id, userId); 1224 if (index >= 0) { 1225 NotificationRecord r = mNotificationList.get(index); 1226 1227 if ((r.notification.flags & mustHaveFlags) != mustHaveFlags) { 1228 return; 1229 } 1230 if ((r.notification.flags & mustNotHaveFlags) != 0) { 1231 return; 1232 } 1233 1234 mNotificationList.remove(index); 1235 1236 cancelNotificationLocked(r, sendDelete); 1237 updateLightsLocked(); 1238 } 1239 } 1240 } 1241 1242 /** 1243 * Determine whether the userId applies to the notification in question, either because 1244 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard). 1245 */ 1246 private boolean notificationMatchesUserId(NotificationRecord r, int userId) { 1247 return 1248 // looking for USER_ALL notifications? match everything 1249 userId == UserHandle.USER_ALL 1250 // a notification sent to USER_ALL matches any query 1251 || r.userId == UserHandle.USER_ALL 1252 // an exact user match 1253 || r.userId == userId; 1254 } 1255 1256 /** 1257 * Cancels all notifications from a given package that have all of the 1258 * {@code mustHaveFlags}. 1259 */ 1260 boolean cancelAllNotificationsInt(String pkg, int mustHaveFlags, 1261 int mustNotHaveFlags, boolean doit, int userId) { 1262 EventLog.writeEvent(EventLogTags.NOTIFICATION_CANCEL_ALL, pkg, userId, 1263 mustHaveFlags, mustNotHaveFlags); 1264 1265 synchronized (mNotificationList) { 1266 final int N = mNotificationList.size(); 1267 boolean canceledSomething = false; 1268 for (int i = N-1; i >= 0; --i) { 1269 NotificationRecord r = mNotificationList.get(i); 1270 if (!notificationMatchesUserId(r, userId)) { 1271 continue; 1272 } 1273 // Don't remove notifications to all, if there's no package name specified 1274 if (r.userId == UserHandle.USER_ALL && pkg == null) { 1275 continue; 1276 } 1277 if ((r.notification.flags & mustHaveFlags) != mustHaveFlags) { 1278 continue; 1279 } 1280 if ((r.notification.flags & mustNotHaveFlags) != 0) { 1281 continue; 1282 } 1283 if (pkg != null && !r.pkg.equals(pkg)) { 1284 continue; 1285 } 1286 canceledSomething = true; 1287 if (!doit) { 1288 return true; 1289 } 1290 mNotificationList.remove(i); 1291 cancelNotificationLocked(r, false); 1292 } 1293 if (canceledSomething) { 1294 updateLightsLocked(); 1295 } 1296 return canceledSomething; 1297 } 1298 } 1299 1300 public void cancelNotificationWithTag(String pkg, String tag, int id, int userId) { 1301 checkCallerIsSystemOrSameApp(pkg); 1302 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), 1303 Binder.getCallingUid(), userId, true, false, "cancelNotificationWithTag", pkg); 1304 // Don't allow client applications to cancel foreground service notis. 1305 cancelNotification(pkg, tag, id, 0, 1306 Binder.getCallingUid() == Process.SYSTEM_UID 1307 ? 0 : Notification.FLAG_FOREGROUND_SERVICE, false, userId); 1308 } 1309 1310 public void cancelAllNotifications(String pkg, int userId) { 1311 checkCallerIsSystemOrSameApp(pkg); 1312 1313 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), 1314 Binder.getCallingUid(), userId, true, false, "cancelAllNotifications", pkg); 1315 1316 // Calling from user space, don't allow the canceling of actively 1317 // running foreground services. 1318 cancelAllNotificationsInt(pkg, 0, Notification.FLAG_FOREGROUND_SERVICE, true, userId); 1319 } 1320 1321 void checkCallerIsSystem() { 1322 int uid = Binder.getCallingUid(); 1323 if (UserHandle.getAppId(uid) == Process.SYSTEM_UID || uid == 0) { 1324 return; 1325 } 1326 throw new SecurityException("Disallowed call for uid " + uid); 1327 } 1328 1329 void checkCallerIsSystemOrSameApp(String pkg) { 1330 int uid = Binder.getCallingUid(); 1331 if (UserHandle.getAppId(uid) == Process.SYSTEM_UID || uid == 0) { 1332 return; 1333 } 1334 try { 1335 ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo( 1336 pkg, 0, UserHandle.getCallingUserId()); 1337 if (!UserHandle.isSameApp(ai.uid, uid)) { 1338 throw new SecurityException("Calling uid " + uid + " gave package" 1339 + pkg + " which is owned by uid " + ai.uid); 1340 } 1341 } catch (RemoteException re) { 1342 throw new SecurityException("Unknown package " + pkg + "\n" + re); 1343 } 1344 } 1345 1346 void cancelAll(int userId) { 1347 synchronized (mNotificationList) { 1348 final int N = mNotificationList.size(); 1349 for (int i=N-1; i>=0; i--) { 1350 NotificationRecord r = mNotificationList.get(i); 1351 1352 if (!notificationMatchesUserId(r, userId)) { 1353 continue; 1354 } 1355 1356 if ((r.notification.flags & (Notification.FLAG_ONGOING_EVENT 1357 | Notification.FLAG_NO_CLEAR)) == 0) { 1358 mNotificationList.remove(i); 1359 cancelNotificationLocked(r, true); 1360 } 1361 } 1362 1363 updateLightsLocked(); 1364 } 1365 } 1366 1367 // lock on mNotificationList 1368 private void updateLightsLocked() 1369 { 1370 // handle notification lights 1371 if (mLedNotification == null) { 1372 // get next notification, if any 1373 int n = mLights.size(); 1374 if (n > 0) { 1375 mLedNotification = mLights.get(n-1); 1376 } 1377 } 1378 1379 // Don't flash while we are in a call or screen is on 1380 if (mLedNotification == null || mInCall || mScreenOn) { 1381 mNotificationLight.turnOff(); 1382 } else { 1383 int ledARGB = mLedNotification.notification.ledARGB; 1384 int ledOnMS = mLedNotification.notification.ledOnMS; 1385 int ledOffMS = mLedNotification.notification.ledOffMS; 1386 if ((mLedNotification.notification.defaults & Notification.DEFAULT_LIGHTS) != 0) { 1387 ledARGB = mDefaultNotificationColor; 1388 ledOnMS = mDefaultNotificationLedOn; 1389 ledOffMS = mDefaultNotificationLedOff; 1390 } 1391 if (mNotificationPulseEnabled) { 1392 // pulse repeatedly 1393 mNotificationLight.setFlashing(ledARGB, LightsService.LIGHT_FLASH_TIMED, 1394 ledOnMS, ledOffMS); 1395 } 1396 } 1397 } 1398 1399 // lock on mNotificationList 1400 private int indexOfNotificationLocked(String pkg, String tag, int id, int userId) 1401 { 1402 ArrayList<NotificationRecord> list = mNotificationList; 1403 final int len = list.size(); 1404 for (int i=0; i<len; i++) { 1405 NotificationRecord r = list.get(i); 1406 if (!notificationMatchesUserId(r, userId) || r.id != id) { 1407 continue; 1408 } 1409 if (tag == null) { 1410 if (r.tag != null) { 1411 continue; 1412 } 1413 } else { 1414 if (!tag.equals(r.tag)) { 1415 continue; 1416 } 1417 } 1418 if (r.pkg.equals(pkg)) { 1419 return i; 1420 } 1421 } 1422 return -1; 1423 } 1424 1425 private void updateNotificationPulse() { 1426 synchronized (mNotificationList) { 1427 updateLightsLocked(); 1428 } 1429 } 1430 1431 // ====================================================================== 1432 @Override 1433 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1434 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) 1435 != PackageManager.PERMISSION_GRANTED) { 1436 pw.println("Permission Denial: can't dump NotificationManager from from pid=" 1437 + Binder.getCallingPid() 1438 + ", uid=" + Binder.getCallingUid()); 1439 return; 1440 } 1441 1442 pw.println("Current Notification Manager state:"); 1443 1444 int N; 1445 1446 synchronized (mToastQueue) { 1447 N = mToastQueue.size(); 1448 if (N > 0) { 1449 pw.println(" Toast Queue:"); 1450 for (int i=0; i<N; i++) { 1451 mToastQueue.get(i).dump(pw, " "); 1452 } 1453 pw.println(" "); 1454 } 1455 1456 } 1457 1458 synchronized (mNotificationList) { 1459 N = mNotificationList.size(); 1460 if (N > 0) { 1461 pw.println(" Notification List:"); 1462 for (int i=0; i<N; i++) { 1463 mNotificationList.get(i).dump(pw, " ", mContext); 1464 } 1465 pw.println(" "); 1466 } 1467 1468 N = mLights.size(); 1469 if (N > 0) { 1470 pw.println(" Lights List:"); 1471 for (int i=0; i<N; i++) { 1472 mLights.get(i).dump(pw, " ", mContext); 1473 } 1474 pw.println(" "); 1475 } 1476 1477 pw.println(" mSoundNotification=" + mSoundNotification); 1478 pw.println(" mVibrateNotification=" + mVibrateNotification); 1479 pw.println(" mDisabledNotifications=0x" + Integer.toHexString(mDisabledNotifications)); 1480 pw.println(" mSystemReady=" + mSystemReady); 1481 } 1482 } 1483 } 1484