NotificationManagerService.java revision f9eb06ca691ed14c8b55840a07333330ae347e22
1/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server;
18
19import com.android.internal.statusbar.StatusBarNotification;
20import com.android.server.StatusBarManagerService;
21
22import android.app.ActivityManagerNative;
23import android.app.IActivityManager;
24import android.app.INotificationManager;
25import android.app.ITransientNotification;
26import android.app.Notification;
27import android.app.NotificationManager;
28import android.app.PendingIntent;
29import android.app.StatusBarManager;
30import android.content.BroadcastReceiver;
31import android.content.ComponentName;
32import android.content.ContentResolver;
33import android.content.Context;
34import android.content.Intent;
35import android.content.IntentFilter;
36import android.content.pm.ApplicationInfo;
37import android.content.pm.PackageManager;
38import android.content.pm.PackageManager.NameNotFoundException;
39import android.content.res.Resources;
40import android.database.ContentObserver;
41import android.hardware.usb.UsbManager;
42import android.media.AudioManager;
43import android.net.Uri;
44import android.os.BatteryManager;
45import android.os.Bundle;
46import android.os.Binder;
47import android.os.Handler;
48import android.os.IBinder;
49import android.os.Message;
50import android.os.Power;
51import android.os.Process;
52import android.os.RemoteException;
53import android.os.SystemProperties;
54import android.os.Vibrator;
55import android.provider.Settings;
56import android.telephony.TelephonyManager;
57import android.text.TextUtils;
58import android.util.EventLog;
59import android.util.Slog;
60import android.util.Log;
61import android.view.accessibility.AccessibilityEvent;
62import android.view.accessibility.AccessibilityManager;
63import android.widget.Toast;
64
65import java.io.FileDescriptor;
66import java.io.PrintWriter;
67import java.util.ArrayList;
68import java.util.Arrays;
69
70/** {@hide} */
71public 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 int mBatteryLowARGB;
137    private static int mBatteryMediumARGB;
138    private static int mBatteryFullARGB;
139    private static int mBatteryLedOn;
140    private static int mBatteryLedOff;
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(UsbManager.ACTION_USB_STATE)) {
356                Bundle extras = intent.getExtras();
357                boolean usbConnected = extras.getBoolean(UsbManager.USB_CONNECTED);
358                boolean adbEnabled = (UsbManager.USB_FUNCTION_ENABLED.equals(
359                                    extras.getString(UsbManager.USB_FUNCTION_ADB)));
360                updateAdbNotification(usbConnected && adbEnabled);
361            } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)
362                    || action.equals(Intent.ACTION_PACKAGE_RESTARTED)
363                    || (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART))
364                    || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
365                String pkgList[] = null;
366                if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
367                    pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
368                } else if (queryRestart) {
369                    pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
370                } else {
371                    Uri uri = intent.getData();
372                    if (uri == null) {
373                        return;
374                    }
375                    String pkgName = uri.getSchemeSpecificPart();
376                    if (pkgName == null) {
377                        return;
378                    }
379                    pkgList = new String[]{pkgName};
380                }
381                if (pkgList != null && (pkgList.length > 0)) {
382                    for (String pkgName : pkgList) {
383                        cancelAllNotificationsInt(pkgName, 0, 0, !queryRestart);
384                    }
385                }
386            } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
387                mScreenOn = true;
388                updateNotificationPulse();
389            } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
390                mScreenOn = false;
391                updateNotificationPulse();
392            } else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) {
393                mInCall = (intent.getStringExtra(TelephonyManager.EXTRA_STATE).equals(TelephonyManager.EXTRA_STATE_OFFHOOK));
394                updateNotificationPulse();
395            }
396        }
397    };
398
399    class SettingsObserver extends ContentObserver {
400        SettingsObserver(Handler handler) {
401            super(handler);
402        }
403
404        void observe() {
405            ContentResolver resolver = mContext.getContentResolver();
406            resolver.registerContentObserver(Settings.System.getUriFor(
407                    Settings.System.NOTIFICATION_LIGHT_PULSE), false, this);
408            update();
409        }
410
411        @Override public void onChange(boolean selfChange) {
412            update();
413        }
414
415        public void update() {
416            ContentResolver resolver = mContext.getContentResolver();
417            boolean pulseEnabled = Settings.System.getInt(resolver,
418                        Settings.System.NOTIFICATION_LIGHT_PULSE, 0) != 0;
419            if (mNotificationPulseEnabled != pulseEnabled) {
420                mNotificationPulseEnabled = pulseEnabled;
421                updateNotificationPulse();
422            }
423        }
424    }
425
426    NotificationManagerService(Context context, StatusBarManagerService statusBar,
427            LightsService lights)
428    {
429        super();
430        mContext = context;
431        mLightsService = lights;
432        mAm = ActivityManagerNative.getDefault();
433        mSound = new NotificationPlayer(TAG);
434        mSound.setUsesWakeLock(context);
435        mToastQueue = new ArrayList<ToastRecord>();
436        mHandler = new WorkerHandler();
437
438        mStatusBar = statusBar;
439        statusBar.setNotificationCallbacks(mNotificationCallbacks);
440
441        mBatteryLight = lights.getLight(LightsService.LIGHT_ID_BATTERY);
442        mNotificationLight = lights.getLight(LightsService.LIGHT_ID_NOTIFICATIONS);
443        mAttentionLight = lights.getLight(LightsService.LIGHT_ID_ATTENTION);
444
445        Resources resources = mContext.getResources();
446        mDefaultNotificationColor = resources.getColor(
447                com.android.internal.R.color.config_defaultNotificationColor);
448        mDefaultNotificationLedOn = resources.getInteger(
449                com.android.internal.R.integer.config_defaultNotificationLedOn);
450        mDefaultNotificationLedOff = resources.getInteger(
451                com.android.internal.R.integer.config_defaultNotificationLedOff);
452
453        mBatteryLowARGB = mContext.getResources().getInteger(
454                com.android.internal.R.integer.config_notificationsBatteryLowARGB);
455        mBatteryMediumARGB = mContext.getResources().getInteger(
456                com.android.internal.R.integer.config_notificationsBatteryMediumARGB);
457        mBatteryFullARGB = mContext.getResources().getInteger(
458                com.android.internal.R.integer.config_notificationsBatteryFullARGB);
459        mBatteryLedOn = mContext.getResources().getInteger(
460                com.android.internal.R.integer.config_notificationsBatteryLedOn);
461        mBatteryLedOff = mContext.getResources().getInteger(
462                com.android.internal.R.integer.config_notificationsBatteryLedOff);
463
464        // Don't start allowing notifications until the setup wizard has run once.
465        // After that, including subsequent boots, init with notifications turned on.
466        // This works on the first boot because the setup wizard will toggle this
467        // flag at least once and we'll go back to 0 after that.
468        if (0 == Settings.Secure.getInt(mContext.getContentResolver(),
469                    Settings.Secure.DEVICE_PROVISIONED, 0)) {
470            mDisabledNotifications = StatusBarManager.DISABLE_NOTIFICATION_ALERTS;
471        }
472
473        // register for battery changed notifications
474        IntentFilter filter = new IntentFilter();
475        filter.addAction(Intent.ACTION_BATTERY_CHANGED);
476        filter.addAction(UsbManager.ACTION_USB_STATE);
477        filter.addAction(Intent.ACTION_SCREEN_ON);
478        filter.addAction(Intent.ACTION_SCREEN_OFF);
479        filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
480        mContext.registerReceiver(mIntentReceiver, filter);
481        IntentFilter pkgFilter = new IntentFilter();
482        pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
483        pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
484        pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
485        pkgFilter.addDataScheme("package");
486        mContext.registerReceiver(mIntentReceiver, pkgFilter);
487        IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
488        mContext.registerReceiver(mIntentReceiver, sdFilter);
489
490        SettingsObserver observer = new SettingsObserver(mHandler);
491        observer.observe();
492    }
493
494    void systemReady() {
495        // no beeping until we're basically done booting
496        mSystemReady = true;
497    }
498
499    // Toasts
500    // ============================================================================
501    public void enqueueToast(String pkg, ITransientNotification callback, int duration)
502    {
503        if (DBG) Slog.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback + " duration=" + duration);
504
505        if (pkg == null || callback == null) {
506            Slog.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback);
507            return ;
508        }
509
510        synchronized (mToastQueue) {
511            int callingPid = Binder.getCallingPid();
512            long callingId = Binder.clearCallingIdentity();
513            try {
514                ToastRecord record;
515                int index = indexOfToastLocked(pkg, callback);
516                // If it's already in the queue, we update it in place, we don't
517                // move it to the end of the queue.
518                if (index >= 0) {
519                    record = mToastQueue.get(index);
520                    record.update(duration);
521                } else {
522                    // Limit the number of toasts that any given package except the android
523                    // package can enqueue.  Prevents DOS attacks and deals with leaks.
524                    if (!"android".equals(pkg)) {
525                        int count = 0;
526                        final int N = mToastQueue.size();
527                        for (int i=0; i<N; i++) {
528                             final ToastRecord r = mToastQueue.get(i);
529                             if (r.pkg.equals(pkg)) {
530                                 count++;
531                                 if (count >= MAX_PACKAGE_NOTIFICATIONS) {
532                                     Slog.e(TAG, "Package has already posted " + count
533                                            + " toasts. Not showing more. Package=" + pkg);
534                                     return;
535                                 }
536                             }
537                        }
538                    }
539
540                    record = new ToastRecord(callingPid, pkg, callback, duration);
541                    mToastQueue.add(record);
542                    index = mToastQueue.size() - 1;
543                    keepProcessAliveLocked(callingPid);
544                }
545                // If it's at index 0, it's the current toast.  It doesn't matter if it's
546                // new or just been updated.  Call back and tell it to show itself.
547                // If the callback fails, this will remove it from the list, so don't
548                // assume that it's valid after this.
549                if (index == 0) {
550                    showNextToastLocked();
551                }
552            } finally {
553                Binder.restoreCallingIdentity(callingId);
554            }
555        }
556    }
557
558    public void cancelToast(String pkg, ITransientNotification callback) {
559        Slog.i(TAG, "cancelToast pkg=" + pkg + " callback=" + callback);
560
561        if (pkg == null || callback == null) {
562            Slog.e(TAG, "Not cancelling notification. pkg=" + pkg + " callback=" + callback);
563            return ;
564        }
565
566        synchronized (mToastQueue) {
567            long callingId = Binder.clearCallingIdentity();
568            try {
569                int index = indexOfToastLocked(pkg, callback);
570                if (index >= 0) {
571                    cancelToastLocked(index);
572                } else {
573                    Slog.w(TAG, "Toast already cancelled. pkg=" + pkg + " callback=" + callback);
574                }
575            } finally {
576                Binder.restoreCallingIdentity(callingId);
577            }
578        }
579    }
580
581    private void showNextToastLocked() {
582        ToastRecord record = mToastQueue.get(0);
583        while (record != null) {
584            if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback);
585            try {
586                record.callback.show();
587                scheduleTimeoutLocked(record, false);
588                return;
589            } catch (RemoteException e) {
590                Slog.w(TAG, "Object died trying to show notification " + record.callback
591                        + " in package " + record.pkg);
592                // remove it from the list and let the process die
593                int index = mToastQueue.indexOf(record);
594                if (index >= 0) {
595                    mToastQueue.remove(index);
596                }
597                keepProcessAliveLocked(record.pid);
598                if (mToastQueue.size() > 0) {
599                    record = mToastQueue.get(0);
600                } else {
601                    record = null;
602                }
603            }
604        }
605    }
606
607    private void cancelToastLocked(int index) {
608        ToastRecord record = mToastQueue.get(index);
609        try {
610            record.callback.hide();
611        } catch (RemoteException e) {
612            Slog.w(TAG, "Object died trying to hide notification " + record.callback
613                    + " in package " + record.pkg);
614            // don't worry about this, we're about to remove it from
615            // the list anyway
616        }
617        mToastQueue.remove(index);
618        keepProcessAliveLocked(record.pid);
619        if (mToastQueue.size() > 0) {
620            // Show the next one. If the callback fails, this will remove
621            // it from the list, so don't assume that the list hasn't changed
622            // after this point.
623            showNextToastLocked();
624        }
625    }
626
627    private void scheduleTimeoutLocked(ToastRecord r, boolean immediate)
628    {
629        Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r);
630        long delay = immediate ? 0 : (r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY);
631        mHandler.removeCallbacksAndMessages(r);
632        mHandler.sendMessageDelayed(m, delay);
633    }
634
635    private void handleTimeout(ToastRecord record)
636    {
637        if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback);
638        synchronized (mToastQueue) {
639            int index = indexOfToastLocked(record.pkg, record.callback);
640            if (index >= 0) {
641                cancelToastLocked(index);
642            }
643        }
644    }
645
646    // lock on mToastQueue
647    private int indexOfToastLocked(String pkg, ITransientNotification callback)
648    {
649        IBinder cbak = callback.asBinder();
650        ArrayList<ToastRecord> list = mToastQueue;
651        int len = list.size();
652        for (int i=0; i<len; i++) {
653            ToastRecord r = list.get(i);
654            if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) {
655                return i;
656            }
657        }
658        return -1;
659    }
660
661    // lock on mToastQueue
662    private void keepProcessAliveLocked(int pid)
663    {
664        int toastCount = 0; // toasts from this pid
665        ArrayList<ToastRecord> list = mToastQueue;
666        int N = list.size();
667        for (int i=0; i<N; i++) {
668            ToastRecord r = list.get(i);
669            if (r.pid == pid) {
670                toastCount++;
671            }
672        }
673        try {
674            mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0);
675        } catch (RemoteException e) {
676            // Shouldn't happen.
677        }
678    }
679
680    private final class WorkerHandler extends Handler
681    {
682        @Override
683        public void handleMessage(Message msg)
684        {
685            switch (msg.what)
686            {
687                case MESSAGE_TIMEOUT:
688                    handleTimeout((ToastRecord)msg.obj);
689                    break;
690            }
691        }
692    }
693
694
695    // Notifications
696    // ============================================================================
697    public void enqueueNotification(String pkg, int id, Notification notification, int[] idOut)
698    {
699        enqueueNotificationWithTag(pkg, null /* tag */, id, notification, idOut);
700    }
701
702    public void enqueueNotificationWithTag(String pkg, String tag, int id, Notification notification,
703            int[] idOut)
704    {
705        enqueueNotificationInternal(pkg, Binder.getCallingUid(), Binder.getCallingPid(),
706                tag, id, notification, idOut);
707    }
708
709    // Not exposed via Binder; for system use only (otherwise malicious apps could spoof the
710    // uid/pid of another application)
711    public void enqueueNotificationInternal(String pkg, int callingUid, int callingPid,
712            String tag, int id, Notification notification, int[] idOut)
713    {
714        checkIncomingCall(pkg);
715
716        // Limit the number of notifications that any given package except the android
717        // package can enqueue.  Prevents DOS attacks and deals with leaks.
718        if (!"android".equals(pkg)) {
719            synchronized (mNotificationList) {
720                int count = 0;
721                final int N = mNotificationList.size();
722                for (int i=0; i<N; i++) {
723                    final NotificationRecord r = mNotificationList.get(i);
724                    if (r.pkg.equals(pkg)) {
725                        count++;
726                        if (count >= MAX_PACKAGE_NOTIFICATIONS) {
727                            Slog.e(TAG, "Package has already posted " + count
728                                    + " notifications.  Not showing more.  package=" + pkg);
729                            return;
730                        }
731                    }
732                }
733            }
734        }
735
736        // This conditional is a dirty hack to limit the logging done on
737        //     behalf of the download manager without affecting other apps.
738        if (!pkg.equals("com.android.providers.downloads")
739                || Log.isLoggable("DownloadManager", Log.VERBOSE)) {
740            EventLog.writeEvent(EventLogTags.NOTIFICATION_ENQUEUE, pkg, id, notification.toString());
741        }
742
743        if (pkg == null || notification == null) {
744            throw new IllegalArgumentException("null not allowed: pkg=" + pkg
745                    + " id=" + id + " notification=" + notification);
746        }
747        if (notification.icon != 0) {
748            if (notification.contentView == null) {
749                throw new IllegalArgumentException("contentView required: pkg=" + pkg
750                        + " id=" + id + " notification=" + notification);
751            }
752            if (notification.contentIntent == null) {
753                throw new IllegalArgumentException("contentIntent required: pkg=" + pkg
754                        + " id=" + id + " notification=" + notification);
755            }
756        }
757
758        synchronized (mNotificationList) {
759            NotificationRecord r = new NotificationRecord(pkg, tag, id,
760                    callingUid, callingPid, notification);
761            NotificationRecord old = null;
762
763            int index = indexOfNotificationLocked(pkg, tag, id);
764            if (index < 0) {
765                mNotificationList.add(r);
766            } else {
767                old = mNotificationList.remove(index);
768                mNotificationList.add(index, r);
769                // Make sure we don't lose the foreground service state.
770                if (old != null) {
771                    notification.flags |=
772                        old.notification.flags&Notification.FLAG_FOREGROUND_SERVICE;
773                }
774            }
775
776            // Ensure if this is a foreground service that the proper additional
777            // flags are set.
778            if ((notification.flags&Notification.FLAG_FOREGROUND_SERVICE) != 0) {
779                notification.flags |= Notification.FLAG_ONGOING_EVENT
780                        | Notification.FLAG_NO_CLEAR;
781            }
782
783            if (notification.icon != 0) {
784                StatusBarNotification n = new StatusBarNotification(pkg, id, tag,
785                        r.uid, r.initialPid, notification);
786                if (old != null && old.statusBarKey != null) {
787                    r.statusBarKey = old.statusBarKey;
788                    long identity = Binder.clearCallingIdentity();
789                    try {
790                        mStatusBar.updateNotification(r.statusBarKey, n);
791                    }
792                    finally {
793                        Binder.restoreCallingIdentity(identity);
794                    }
795                } else {
796                    long identity = Binder.clearCallingIdentity();
797                    try {
798                        r.statusBarKey = mStatusBar.addNotification(n);
799                        mAttentionLight.pulse();
800                    }
801                    finally {
802                        Binder.restoreCallingIdentity(identity);
803                    }
804                }
805                sendAccessibilityEvent(notification, pkg);
806            } else {
807                if (old != null && old.statusBarKey != null) {
808                    long identity = Binder.clearCallingIdentity();
809                    try {
810                        mStatusBar.removeNotification(old.statusBarKey);
811                    }
812                    finally {
813                        Binder.restoreCallingIdentity(identity);
814                    }
815                }
816            }
817
818            // If we're not supposed to beep, vibrate, etc. then don't.
819            if (((mDisabledNotifications & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) == 0)
820                    && (!(old != null
821                        && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 ))
822                    && mSystemReady) {
823
824                final AudioManager audioManager = (AudioManager) mContext
825                .getSystemService(Context.AUDIO_SERVICE);
826                // sound
827                final boolean useDefaultSound =
828                    (notification.defaults & Notification.DEFAULT_SOUND) != 0;
829                if (useDefaultSound || notification.sound != null) {
830                    Uri uri;
831                    if (useDefaultSound) {
832                        uri = Settings.System.DEFAULT_NOTIFICATION_URI;
833                    } else {
834                        uri = notification.sound;
835                    }
836                    boolean looping = (notification.flags & Notification.FLAG_INSISTENT) != 0;
837                    int audioStreamType;
838                    if (notification.audioStreamType >= 0) {
839                        audioStreamType = notification.audioStreamType;
840                    } else {
841                        audioStreamType = DEFAULT_STREAM_TYPE;
842                    }
843                    mSoundNotification = r;
844                    // do not play notifications if stream volume is 0
845                    // (typically because ringer mode is silent).
846                    if (audioManager.getStreamVolume(audioStreamType) != 0) {
847                        long identity = Binder.clearCallingIdentity();
848                        try {
849                            mSound.play(mContext, uri, looping, audioStreamType);
850                        }
851                        finally {
852                            Binder.restoreCallingIdentity(identity);
853                        }
854                    }
855                }
856
857                // vibrate
858                final boolean useDefaultVibrate =
859                    (notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
860                if ((useDefaultVibrate || notification.vibrate != null)
861                        && audioManager.shouldVibrate(AudioManager.VIBRATE_TYPE_NOTIFICATION)) {
862                    mVibrateNotification = r;
863
864                    mVibrator.vibrate(useDefaultVibrate ? DEFAULT_VIBRATE_PATTERN
865                                                        : notification.vibrate,
866                              ((notification.flags & Notification.FLAG_INSISTENT) != 0) ? 0: -1);
867                }
868            }
869
870            // this option doesn't shut off the lights
871
872            // light
873            // the most recent thing gets the light
874            mLights.remove(old);
875            if (mLedNotification == old) {
876                mLedNotification = null;
877            }
878            //Slog.i(TAG, "notification.lights="
879            //        + ((old.notification.lights.flags & Notification.FLAG_SHOW_LIGHTS) != 0));
880            if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0) {
881                mLights.add(r);
882                updateLightsLocked();
883            } else {
884                if (old != null
885                        && ((old.notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0)) {
886                    updateLightsLocked();
887                }
888            }
889        }
890
891        idOut[0] = id;
892    }
893
894    private void sendAccessibilityEvent(Notification notification, CharSequence packageName) {
895        AccessibilityManager manager = AccessibilityManager.getInstance(mContext);
896        if (!manager.isEnabled()) {
897            return;
898        }
899
900        AccessibilityEvent event =
901            AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
902        event.setPackageName(packageName);
903        event.setClassName(Notification.class.getName());
904        event.setParcelableData(notification);
905        CharSequence tickerText = notification.tickerText;
906        if (!TextUtils.isEmpty(tickerText)) {
907            event.getText().add(tickerText);
908        }
909
910        manager.sendAccessibilityEvent(event);
911    }
912
913    private void cancelNotificationLocked(NotificationRecord r) {
914        // status bar
915        if (r.notification.icon != 0) {
916            long identity = Binder.clearCallingIdentity();
917            try {
918                mStatusBar.removeNotification(r.statusBarKey);
919            }
920            finally {
921                Binder.restoreCallingIdentity(identity);
922            }
923            r.statusBarKey = null;
924        }
925
926        // sound
927        if (mSoundNotification == r) {
928            mSoundNotification = null;
929            long identity = Binder.clearCallingIdentity();
930            try {
931                mSound.stop();
932            }
933            finally {
934                Binder.restoreCallingIdentity(identity);
935            }
936        }
937
938        // vibrate
939        if (mVibrateNotification == r) {
940            mVibrateNotification = null;
941            long identity = Binder.clearCallingIdentity();
942            try {
943                mVibrator.cancel();
944            }
945            finally {
946                Binder.restoreCallingIdentity(identity);
947            }
948        }
949
950        // light
951        mLights.remove(r);
952        if (mLedNotification == r) {
953            mLedNotification = null;
954        }
955    }
956
957    /**
958     * Cancels a notification ONLY if it has all of the {@code mustHaveFlags}
959     * and none of the {@code mustNotHaveFlags}.
960     */
961    private void cancelNotification(String pkg, String tag, int id, int mustHaveFlags,
962            int mustNotHaveFlags) {
963        EventLog.writeEvent(EventLogTags.NOTIFICATION_CANCEL, pkg, id, mustHaveFlags);
964
965        synchronized (mNotificationList) {
966            int index = indexOfNotificationLocked(pkg, tag, id);
967            if (index >= 0) {
968                NotificationRecord r = mNotificationList.get(index);
969
970                if ((r.notification.flags & mustHaveFlags) != mustHaveFlags) {
971                    return;
972                }
973                if ((r.notification.flags & mustNotHaveFlags) != 0) {
974                    return;
975                }
976
977                mNotificationList.remove(index);
978
979                cancelNotificationLocked(r);
980                updateLightsLocked();
981            }
982        }
983    }
984
985    /**
986     * Cancels all notifications from a given package that have all of the
987     * {@code mustHaveFlags}.
988     */
989    boolean cancelAllNotificationsInt(String pkg, int mustHaveFlags,
990            int mustNotHaveFlags, boolean doit) {
991        EventLog.writeEvent(EventLogTags.NOTIFICATION_CANCEL_ALL, pkg, mustHaveFlags);
992
993        synchronized (mNotificationList) {
994            final int N = mNotificationList.size();
995            boolean canceledSomething = false;
996            for (int i = N-1; i >= 0; --i) {
997                NotificationRecord r = mNotificationList.get(i);
998                if ((r.notification.flags & mustHaveFlags) != mustHaveFlags) {
999                    continue;
1000                }
1001                if ((r.notification.flags & mustNotHaveFlags) != 0) {
1002                    continue;
1003                }
1004                if (!r.pkg.equals(pkg)) {
1005                    continue;
1006                }
1007                canceledSomething = true;
1008                if (!doit) {
1009                    return true;
1010                }
1011                mNotificationList.remove(i);
1012                cancelNotificationLocked(r);
1013            }
1014            if (canceledSomething) {
1015                updateLightsLocked();
1016            }
1017            return canceledSomething;
1018        }
1019    }
1020
1021
1022    public void cancelNotification(String pkg, int id) {
1023        cancelNotificationWithTag(pkg, null /* tag */, id);
1024    }
1025
1026    public void cancelNotificationWithTag(String pkg, String tag, int id) {
1027        checkIncomingCall(pkg);
1028        // Don't allow client applications to cancel foreground service notis.
1029        cancelNotification(pkg, tag, id, 0,
1030                Binder.getCallingUid() == Process.SYSTEM_UID
1031                ? 0 : Notification.FLAG_FOREGROUND_SERVICE);
1032    }
1033
1034    public void cancelAllNotifications(String pkg) {
1035        checkIncomingCall(pkg);
1036
1037        // Calling from user space, don't allow the canceling of actively
1038        // running foreground services.
1039        cancelAllNotificationsInt(pkg, 0, Notification.FLAG_FOREGROUND_SERVICE, true);
1040    }
1041
1042    void checkIncomingCall(String pkg) {
1043        int uid = Binder.getCallingUid();
1044        if (uid == Process.SYSTEM_UID || uid == 0) {
1045            return;
1046        }
1047        try {
1048            ApplicationInfo ai = mContext.getPackageManager().getApplicationInfo(
1049                    pkg, 0);
1050            if (ai.uid != uid) {
1051                throw new SecurityException("Calling uid " + uid + " gave package"
1052                        + pkg + " which is owned by uid " + ai.uid);
1053            }
1054        } catch (PackageManager.NameNotFoundException e) {
1055            throw new SecurityException("Unknown package " + pkg);
1056        }
1057    }
1058
1059    void cancelAll() {
1060        synchronized (mNotificationList) {
1061            final int N = mNotificationList.size();
1062            for (int i=N-1; i>=0; i--) {
1063                NotificationRecord r = mNotificationList.get(i);
1064
1065                if ((r.notification.flags & (Notification.FLAG_ONGOING_EVENT
1066                                | Notification.FLAG_NO_CLEAR)) == 0) {
1067                    if (r.notification.deleteIntent != null) {
1068                        try {
1069                            r.notification.deleteIntent.send();
1070                        } catch (PendingIntent.CanceledException ex) {
1071                            // do nothing - there's no relevant way to recover, and
1072                            //     no reason to let this propagate
1073                            Slog.w(TAG, "canceled PendingIntent for " + r.pkg, ex);
1074                        }
1075                    }
1076                    mNotificationList.remove(i);
1077                    cancelNotificationLocked(r);
1078                }
1079            }
1080
1081            updateLightsLocked();
1082        }
1083    }
1084
1085    private void updateLights() {
1086        synchronized (mNotificationList) {
1087            updateLightsLocked();
1088        }
1089    }
1090
1091    // lock on mNotificationList
1092    private void updateLightsLocked()
1093    {
1094        // Battery low always shows, other states only show if charging.
1095        if (mBatteryLow) {
1096            if (mBatteryCharging) {
1097                mBatteryLight.setColor(mBatteryLowARGB);
1098            } else {
1099                // Flash when battery is low and not charging
1100                mBatteryLight.setFlashing(mBatteryLowARGB, LightsService.LIGHT_FLASH_TIMED,
1101                        mBatteryLedOn, mBatteryLedOff);
1102            }
1103        } else if (mBatteryCharging) {
1104            if (mBatteryFull) {
1105                mBatteryLight.setColor(mBatteryFullARGB);
1106            } else {
1107                mBatteryLight.setColor(mBatteryMediumARGB);
1108            }
1109        } else {
1110            mBatteryLight.turnOff();
1111        }
1112
1113        // clear pending pulse notification if screen is on
1114        if (mScreenOn || mLedNotification == null) {
1115            mPendingPulseNotification = false;
1116        }
1117
1118        // handle notification lights
1119        if (mLedNotification == null) {
1120            // get next notification, if any
1121            int n = mLights.size();
1122            if (n > 0) {
1123                mLedNotification = mLights.get(n-1);
1124            }
1125            if (mLedNotification != null && !mScreenOn) {
1126                mPendingPulseNotification = true;
1127            }
1128        }
1129
1130        // we only flash if screen is off and persistent pulsing is enabled
1131        // and we are not currently in a call
1132        if (!mPendingPulseNotification || mScreenOn || mInCall) {
1133            mNotificationLight.turnOff();
1134        } else {
1135            int ledARGB = mLedNotification.notification.ledARGB;
1136            int ledOnMS = mLedNotification.notification.ledOnMS;
1137            int ledOffMS = mLedNotification.notification.ledOffMS;
1138            if ((mLedNotification.notification.defaults & Notification.DEFAULT_LIGHTS) != 0) {
1139                ledARGB = mDefaultNotificationColor;
1140                ledOnMS = mDefaultNotificationLedOn;
1141                ledOffMS = mDefaultNotificationLedOff;
1142            }
1143            if (mNotificationPulseEnabled) {
1144                // pulse repeatedly
1145                mNotificationLight.setFlashing(ledARGB, LightsService.LIGHT_FLASH_TIMED,
1146                        ledOnMS, ledOffMS);
1147            } else {
1148                // pulse only once
1149                mNotificationLight.pulse(ledARGB, ledOnMS);
1150            }
1151        }
1152    }
1153
1154    // lock on mNotificationList
1155    private int indexOfNotificationLocked(String pkg, String tag, int id)
1156    {
1157        ArrayList<NotificationRecord> list = mNotificationList;
1158        final int len = list.size();
1159        for (int i=0; i<len; i++) {
1160            NotificationRecord r = list.get(i);
1161            if (tag == null) {
1162                if (r.tag != null) {
1163                    continue;
1164                }
1165            } else {
1166                if (!tag.equals(r.tag)) {
1167                    continue;
1168                }
1169            }
1170            if (r.id == id && r.pkg.equals(pkg)) {
1171                return i;
1172            }
1173        }
1174        return -1;
1175    }
1176
1177    // This is here instead of StatusBarPolicy because it is an important
1178    // security feature that we don't want people customizing the platform
1179    // to accidentally lose.
1180    private void updateAdbNotification(boolean adbEnabled) {
1181        if (adbEnabled) {
1182            if ("0".equals(SystemProperties.get("persist.adb.notify"))) {
1183                return;
1184            }
1185            if (!mAdbNotificationShown) {
1186                NotificationManager notificationManager = (NotificationManager) mContext
1187                        .getSystemService(Context.NOTIFICATION_SERVICE);
1188                if (notificationManager != null) {
1189                    Resources r = mContext.getResources();
1190                    CharSequence title = r.getText(
1191                            com.android.internal.R.string.adb_active_notification_title);
1192                    CharSequence message = r.getText(
1193                            com.android.internal.R.string.adb_active_notification_message);
1194
1195                    if (mAdbNotification == null) {
1196                        mAdbNotification = new Notification();
1197                        mAdbNotification.icon = com.android.internal.R.drawable.stat_sys_adb;
1198                        mAdbNotification.when = 0;
1199                        mAdbNotification.flags = Notification.FLAG_ONGOING_EVENT;
1200                        mAdbNotification.tickerText = title;
1201                        mAdbNotification.defaults = 0; // please be quiet
1202                        mAdbNotification.sound = null;
1203                        mAdbNotification.vibrate = null;
1204                    }
1205
1206                    Intent intent = new Intent(
1207                            Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS);
1208                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
1209                            Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
1210                    // Note: we are hard-coding the component because this is
1211                    // an important security UI that we don't want anyone
1212                    // intercepting.
1213                    intent.setComponent(new ComponentName("com.android.settings",
1214                            "com.android.settings.DevelopmentSettings"));
1215                    PendingIntent pi = PendingIntent.getActivity(mContext, 0,
1216                            intent, 0);
1217
1218                    mAdbNotification.setLatestEventInfo(mContext, title, message, pi);
1219
1220                    mAdbNotificationShown = true;
1221                    notificationManager.notify(
1222                            com.android.internal.R.string.adb_active_notification_title,
1223                            mAdbNotification);
1224                }
1225            }
1226
1227        } else if (mAdbNotificationShown) {
1228            NotificationManager notificationManager = (NotificationManager) mContext
1229                    .getSystemService(Context.NOTIFICATION_SERVICE);
1230            if (notificationManager != null) {
1231                mAdbNotificationShown = false;
1232                notificationManager.cancel(
1233                        com.android.internal.R.string.adb_active_notification_title);
1234            }
1235        }
1236    }
1237
1238    private void updateNotificationPulse() {
1239        synchronized (mNotificationList) {
1240            updateLightsLocked();
1241        }
1242    }
1243
1244    // ======================================================================
1245    @Override
1246    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1247        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
1248                != PackageManager.PERMISSION_GRANTED) {
1249            pw.println("Permission Denial: can't dump NotificationManager from from pid="
1250                    + Binder.getCallingPid()
1251                    + ", uid=" + Binder.getCallingUid());
1252            return;
1253        }
1254
1255        pw.println("Current Notification Manager state:");
1256
1257        int N;
1258
1259        synchronized (mToastQueue) {
1260            N = mToastQueue.size();
1261            if (N > 0) {
1262                pw.println("  Toast Queue:");
1263                for (int i=0; i<N; i++) {
1264                    mToastQueue.get(i).dump(pw, "    ");
1265                }
1266                pw.println("  ");
1267            }
1268
1269        }
1270
1271        synchronized (mNotificationList) {
1272            N = mNotificationList.size();
1273            if (N > 0) {
1274                pw.println("  Notification List:");
1275                for (int i=0; i<N; i++) {
1276                    mNotificationList.get(i).dump(pw, "    ", mContext);
1277                }
1278                pw.println("  ");
1279            }
1280
1281            N = mLights.size();
1282            if (N > 0) {
1283                pw.println("  Lights List:");
1284                for (int i=0; i<N; i++) {
1285                    mLights.get(i).dump(pw, "    ", mContext);
1286                }
1287                pw.println("  ");
1288            }
1289
1290            pw.println("  mSoundNotification=" + mSoundNotification);
1291            pw.println("  mSound=" + mSound);
1292            pw.println("  mVibrateNotification=" + mVibrateNotification);
1293            pw.println("  mDisabledNotifications=0x" + Integer.toHexString(mDisabledNotifications));
1294            pw.println("  mSystemReady=" + mSystemReady);
1295        }
1296    }
1297}
1298