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