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