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