Home | History | Annotate | Download | only in server
      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