NotificationManagerService.java revision 4a7a9b963b42358a891b42187e46d76ecb92ff31
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 static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
20import static org.xmlpull.v1.XmlPullParser.END_TAG;
21import static org.xmlpull.v1.XmlPullParser.START_TAG;
22
23import android.app.ActivityManager;
24import android.app.ActivityManagerNative;
25import android.app.AppGlobals;
26import android.app.IActivityManager;
27import android.app.INotificationManager;
28import android.app.ITransientNotification;
29import android.app.Notification;
30import android.app.PendingIntent;
31import android.app.StatusBarManager;
32import android.content.BroadcastReceiver;
33import android.content.ContentResolver;
34import android.content.Context;
35import android.content.Intent;
36import android.content.IntentFilter;
37import android.content.pm.ApplicationInfo;
38import android.content.pm.PackageManager;
39import android.content.pm.PackageManager.NameNotFoundException;
40import android.content.res.Resources;
41import android.database.ContentObserver;
42import android.media.AudioManager;
43import android.media.IAudioService;
44import android.media.IRingtonePlayer;
45import android.net.Uri;
46import android.os.Binder;
47import android.os.Handler;
48import android.os.IBinder;
49import android.os.Message;
50import android.os.Process;
51import android.os.RemoteException;
52import android.os.ServiceManager;
53import android.os.UserHandle;
54import android.os.Vibrator;
55import android.provider.Settings;
56import android.telephony.TelephonyManager;
57import android.text.TextUtils;
58import android.util.AtomicFile;
59import android.util.EventLog;
60import android.util.Log;
61import android.util.Slog;
62import android.util.Xml;
63import android.view.accessibility.AccessibilityEvent;
64import android.view.accessibility.AccessibilityManager;
65import android.widget.RemoteViews;
66import android.widget.Toast;
67
68import com.android.internal.statusbar.StatusBarNotification;
69import com.android.internal.util.FastXmlSerializer;
70
71import org.xmlpull.v1.XmlPullParser;
72import org.xmlpull.v1.XmlPullParserException;
73import org.xmlpull.v1.XmlSerializer;
74
75import java.io.File;
76import java.io.FileDescriptor;
77import java.io.FileInputStream;
78import java.io.FileNotFoundException;
79import java.io.FileOutputStream;
80import java.io.IOException;
81import java.io.PrintWriter;
82import java.util.ArrayList;
83import java.util.Arrays;
84import java.util.HashSet;
85
86import libcore.io.IoUtils;
87
88
89/** {@hide} */
90public class NotificationManagerService extends INotificationManager.Stub
91{
92    private static final String TAG = "NotificationService";
93    private static final boolean DBG = false;
94
95    private static final int MAX_PACKAGE_NOTIFICATIONS = 50;
96
97    // message codes
98    private static final int MESSAGE_TIMEOUT = 2;
99
100    private static final int LONG_DELAY = 3500; // 3.5 seconds
101    private static final int SHORT_DELAY = 2000; // 2 seconds
102
103    private static final long[] DEFAULT_VIBRATE_PATTERN = {0, 250, 250, 250};
104    private static final int VIBRATE_PATTERN_MAXLEN = 8 * 2 + 1; // up to eight bumps
105
106    private static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_NOTIFICATION;
107    private static final boolean SCORE_ONGOING_HIGHER = false;
108
109    private static final int JUNK_SCORE = -1000;
110    private static final int NOTIFICATION_PRIORITY_MULTIPLIER = 10;
111    private static final int SCORE_DISPLAY_THRESHOLD = Notification.PRIORITY_MIN * NOTIFICATION_PRIORITY_MULTIPLIER;
112
113    private static final boolean ENABLE_BLOCKED_NOTIFICATIONS = true;
114    private static final boolean ENABLE_BLOCKED_TOASTS = true;
115
116    final Context mContext;
117    final IActivityManager mAm;
118    final IBinder mForegroundToken = new Binder();
119
120    private WorkerHandler mHandler;
121    private StatusBarManagerService mStatusBar;
122    private LightsService.Light mNotificationLight;
123    private LightsService.Light mAttentionLight;
124
125    private int mDefaultNotificationColor;
126    private int mDefaultNotificationLedOn;
127    private int mDefaultNotificationLedOff;
128
129    private long[] mDefaultVibrationPattern;
130    private long[] mFallbackVibrationPattern;
131
132    private boolean mSystemReady;
133    private int mDisabledNotifications;
134
135    private NotificationRecord mSoundNotification;
136    private NotificationRecord mVibrateNotification;
137
138    private IAudioService mAudioService;
139    private Vibrator mVibrator;
140
141    // for enabling and disabling notification pulse behavior
142    private boolean mScreenOn = true;
143    private boolean mInCall = false;
144    private boolean mNotificationPulseEnabled;
145
146    private final ArrayList<NotificationRecord> mNotificationList =
147            new ArrayList<NotificationRecord>();
148
149    private ArrayList<ToastRecord> mToastQueue;
150
151    private ArrayList<NotificationRecord> mLights = new ArrayList<NotificationRecord>();
152    private NotificationRecord mLedNotification;
153
154    // Notification control database. For now just contains disabled packages.
155    private AtomicFile mPolicyFile;
156    private HashSet<String> mBlockedPackages = new HashSet<String>();
157
158    private static final int DB_VERSION = 1;
159
160    private static final String TAG_BODY = "notification-policy";
161    private static final String ATTR_VERSION = "version";
162
163    private static final String TAG_BLOCKED_PKGS = "blocked-packages";
164    private static final String TAG_PACKAGE = "package";
165    private static final String ATTR_NAME = "name";
166
167    private void loadBlockDb() {
168        synchronized(mBlockedPackages) {
169            if (mPolicyFile == null) {
170                File dir = new File("/data/system");
171                mPolicyFile = new AtomicFile(new File(dir, "notification_policy.xml"));
172
173                mBlockedPackages.clear();
174
175                FileInputStream infile = null;
176                try {
177                    infile = mPolicyFile.openRead();
178                    final XmlPullParser parser = Xml.newPullParser();
179                    parser.setInput(infile, null);
180
181                    int type;
182                    String tag;
183                    int version = DB_VERSION;
184                    while ((type = parser.next()) != END_DOCUMENT) {
185                        tag = parser.getName();
186                        if (type == START_TAG) {
187                            if (TAG_BODY.equals(tag)) {
188                                version = Integer.parseInt(parser.getAttributeValue(null, ATTR_VERSION));
189                            } else if (TAG_BLOCKED_PKGS.equals(tag)) {
190                                while ((type = parser.next()) != END_DOCUMENT) {
191                                    tag = parser.getName();
192                                    if (TAG_PACKAGE.equals(tag)) {
193                                        mBlockedPackages.add(parser.getAttributeValue(null, ATTR_NAME));
194                                    } else if (TAG_BLOCKED_PKGS.equals(tag) && type == END_TAG) {
195                                        break;
196                                    }
197                                }
198                            }
199                        }
200                    }
201                } catch (FileNotFoundException e) {
202                    // No data yet
203                } catch (IOException e) {
204                    Log.wtf(TAG, "Unable to read blocked notifications database", e);
205                } catch (NumberFormatException e) {
206                    Log.wtf(TAG, "Unable to parse blocked notifications database", e);
207                } catch (XmlPullParserException e) {
208                    Log.wtf(TAG, "Unable to parse blocked notifications database", e);
209                } finally {
210                    IoUtils.closeQuietly(infile);
211                }
212            }
213        }
214    }
215
216    private void writeBlockDb() {
217        synchronized(mBlockedPackages) {
218            FileOutputStream outfile = null;
219            try {
220                outfile = mPolicyFile.startWrite();
221
222                XmlSerializer out = new FastXmlSerializer();
223                out.setOutput(outfile, "utf-8");
224
225                out.startDocument(null, true);
226
227                out.startTag(null, TAG_BODY); {
228                    out.attribute(null, ATTR_VERSION, String.valueOf(DB_VERSION));
229                    out.startTag(null, TAG_BLOCKED_PKGS); {
230                        // write all known network policies
231                        for (String pkg : mBlockedPackages) {
232                            out.startTag(null, TAG_PACKAGE); {
233                                out.attribute(null, ATTR_NAME, pkg);
234                            } out.endTag(null, TAG_PACKAGE);
235                        }
236                    } out.endTag(null, TAG_BLOCKED_PKGS);
237                } out.endTag(null, TAG_BODY);
238
239                out.endDocument();
240
241                mPolicyFile.finishWrite(outfile);
242            } catch (IOException e) {
243                if (outfile != null) {
244                    mPolicyFile.failWrite(outfile);
245                }
246            }
247        }
248    }
249
250    public boolean areNotificationsEnabledForPackage(String pkg) {
251        checkCallerIsSystem();
252        return areNotificationsEnabledForPackageInt(pkg);
253    }
254
255    // Unchecked. Not exposed via Binder, but can be called in the course of enqueue*().
256    private boolean areNotificationsEnabledForPackageInt(String pkg) {
257        final boolean enabled = !mBlockedPackages.contains(pkg);
258        if (DBG) {
259            Slog.v(TAG, "notifications are " + (enabled?"en":"dis") + "abled for " + pkg);
260        }
261        return enabled;
262    }
263
264    public void setNotificationsEnabledForPackage(String pkg, boolean enabled) {
265        checkCallerIsSystem();
266        if (DBG) {
267            Slog.v(TAG, (enabled?"en":"dis") + "abling notifications for " + pkg);
268        }
269        if (enabled) {
270            mBlockedPackages.remove(pkg);
271        } else {
272            mBlockedPackages.add(pkg);
273
274            // Now, cancel any outstanding notifications that are part of a just-disabled app
275            if (ENABLE_BLOCKED_NOTIFICATIONS) {
276                synchronized (mNotificationList) {
277                    final int N = mNotificationList.size();
278                    for (int i=0; i<N; i++) {
279                        final NotificationRecord r = mNotificationList.get(i);
280                        if (r.pkg.equals(pkg)) {
281                            cancelNotificationLocked(r, false);
282                        }
283                    }
284                }
285            }
286            // Don't bother canceling toasts, they'll go away soon enough.
287        }
288        writeBlockDb();
289    }
290
291
292    private static String idDebugString(Context baseContext, String packageName, int id) {
293        Context c = null;
294
295        if (packageName != null) {
296            try {
297                c = baseContext.createPackageContext(packageName, 0);
298            } catch (NameNotFoundException e) {
299                c = baseContext;
300            }
301        } else {
302            c = baseContext;
303        }
304
305        String pkg;
306        String type;
307        String name;
308
309        Resources r = c.getResources();
310        try {
311            return r.getResourceName(id);
312        } catch (Resources.NotFoundException e) {
313            return "<name unknown>";
314        }
315    }
316
317    private static final class NotificationRecord
318    {
319        final String pkg;
320        final String tag;
321        final int id;
322        final int uid;
323        final int initialPid;
324        final int userId;
325        final Notification notification;
326        final int score;
327        IBinder statusBarKey;
328
329        NotificationRecord(String pkg, String tag, int id, int uid, int initialPid,
330                int userId, int score, Notification notification)
331        {
332            this.pkg = pkg;
333            this.tag = tag;
334            this.id = id;
335            this.uid = uid;
336            this.initialPid = initialPid;
337            this.userId = userId;
338            this.score = score;
339            this.notification = notification;
340        }
341
342        void dump(PrintWriter pw, String prefix, Context baseContext) {
343            pw.println(prefix + this);
344            pw.println(prefix + "  icon=0x" + Integer.toHexString(notification.icon)
345                    + " / " + idDebugString(baseContext, this.pkg, notification.icon));
346            pw.println(prefix + "  pri=" + notification.priority);
347            pw.println(prefix + "  score=" + this.score);
348            pw.println(prefix + "  contentIntent=" + notification.contentIntent);
349            pw.println(prefix + "  deleteIntent=" + notification.deleteIntent);
350            pw.println(prefix + "  tickerText=" + notification.tickerText);
351            pw.println(prefix + "  contentView=" + notification.contentView);
352            pw.println(prefix + "  uid=" + uid + " userId=" + userId);
353            pw.println(prefix + "  defaults=0x" + Integer.toHexString(notification.defaults));
354            pw.println(prefix + "  flags=0x" + Integer.toHexString(notification.flags));
355            pw.println(prefix + "  sound=" + notification.sound);
356            pw.println(prefix + "  vibrate=" + Arrays.toString(notification.vibrate));
357            pw.println(prefix + "  ledARGB=0x" + Integer.toHexString(notification.ledARGB)
358                    + " ledOnMS=" + notification.ledOnMS
359                    + " ledOffMS=" + notification.ledOffMS);
360        }
361
362        @Override
363        public final String toString()
364        {
365            return "NotificationRecord{"
366                + Integer.toHexString(System.identityHashCode(this))
367                + " pkg=" + pkg
368                + " id=" + Integer.toHexString(id)
369                + " tag=" + tag
370                + " score=" + score
371                + "}";
372        }
373    }
374
375    private static final class ToastRecord
376    {
377        final int pid;
378        final String pkg;
379        final ITransientNotification callback;
380        int duration;
381
382        ToastRecord(int pid, String pkg, ITransientNotification callback, int duration)
383        {
384            this.pid = pid;
385            this.pkg = pkg;
386            this.callback = callback;
387            this.duration = duration;
388        }
389
390        void update(int duration) {
391            this.duration = duration;
392        }
393
394        void dump(PrintWriter pw, String prefix) {
395            pw.println(prefix + this);
396        }
397
398        @Override
399        public final String toString()
400        {
401            return "ToastRecord{"
402                + Integer.toHexString(System.identityHashCode(this))
403                + " pkg=" + pkg
404                + " callback=" + callback
405                + " duration=" + duration;
406        }
407    }
408
409    private StatusBarManagerService.NotificationCallbacks mNotificationCallbacks
410            = new StatusBarManagerService.NotificationCallbacks() {
411
412        public void onSetDisabled(int status) {
413            synchronized (mNotificationList) {
414                mDisabledNotifications = status;
415                if ((mDisabledNotifications & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) {
416                    // cancel whatever's going on
417                    long identity = Binder.clearCallingIdentity();
418                    try {
419                        final IRingtonePlayer player = mAudioService.getRingtonePlayer();
420                        if (player != null) {
421                            player.stopAsync();
422                        }
423                    } catch (RemoteException e) {
424                    } finally {
425                        Binder.restoreCallingIdentity(identity);
426                    }
427
428                    identity = Binder.clearCallingIdentity();
429                    try {
430                        mVibrator.cancel();
431                    } finally {
432                        Binder.restoreCallingIdentity(identity);
433                    }
434                }
435            }
436        }
437
438        public void onClearAll() {
439            // XXX to be totally correct, the caller should tell us which user
440            // this is for.
441            cancelAll(ActivityManager.getCurrentUser());
442        }
443
444        public void onNotificationClick(String pkg, String tag, int id) {
445            // XXX to be totally correct, the caller should tell us which user
446            // this is for.
447            cancelNotification(pkg, tag, id, Notification.FLAG_AUTO_CANCEL,
448                    Notification.FLAG_FOREGROUND_SERVICE, false,
449                    ActivityManager.getCurrentUser());
450        }
451
452        public void onNotificationClear(String pkg, String tag, int id) {
453            // XXX to be totally correct, the caller should tell us which user
454            // this is for.
455            cancelNotification(pkg, tag, id, 0,
456                Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
457                true, ActivityManager.getCurrentUser());
458        }
459
460        public void onPanelRevealed() {
461            synchronized (mNotificationList) {
462                // sound
463                mSoundNotification = null;
464
465                long identity = Binder.clearCallingIdentity();
466                try {
467                    final IRingtonePlayer player = mAudioService.getRingtonePlayer();
468                    if (player != null) {
469                        player.stopAsync();
470                    }
471                } catch (RemoteException e) {
472                } finally {
473                    Binder.restoreCallingIdentity(identity);
474                }
475
476                // vibrate
477                mVibrateNotification = null;
478                identity = Binder.clearCallingIdentity();
479                try {
480                    mVibrator.cancel();
481                } finally {
482                    Binder.restoreCallingIdentity(identity);
483                }
484
485                // light
486                mLights.clear();
487                mLedNotification = null;
488                updateLightsLocked();
489            }
490        }
491
492        public void onNotificationError(String pkg, String tag, int id,
493                int uid, int initialPid, String message) {
494            Slog.d(TAG, "onNotification error pkg=" + pkg + " tag=" + tag + " id=" + id
495                    + "; will crashApplication(uid=" + uid + ", pid=" + initialPid + ")");
496            // XXX to be totally correct, the caller should tell us which user
497            // this is for.
498            cancelNotification(pkg, tag, id, 0, 0, false, UserHandle.getUserId(uid));
499            long ident = Binder.clearCallingIdentity();
500            try {
501                ActivityManagerNative.getDefault().crashApplication(uid, initialPid, pkg,
502                        "Bad notification posted from package " + pkg
503                        + ": " + message);
504            } catch (RemoteException e) {
505            }
506            Binder.restoreCallingIdentity(ident);
507        }
508    };
509
510    private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
511        @Override
512        public void onReceive(Context context, Intent intent) {
513            String action = intent.getAction();
514
515            boolean queryRestart = false;
516            boolean packageChanged = false;
517
518            if (action.equals(Intent.ACTION_PACKAGE_REMOVED)
519                    || action.equals(Intent.ACTION_PACKAGE_RESTARTED)
520                    || (packageChanged=action.equals(Intent.ACTION_PACKAGE_CHANGED))
521                    || (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART))
522                    || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
523                String pkgList[] = null;
524                if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
525                    pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
526                } else if (queryRestart) {
527                    pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
528                } else {
529                    Uri uri = intent.getData();
530                    if (uri == null) {
531                        return;
532                    }
533                    String pkgName = uri.getSchemeSpecificPart();
534                    if (pkgName == null) {
535                        return;
536                    }
537                    if (packageChanged) {
538                        // We cancel notifications for packages which have just been disabled
539                        final int enabled = mContext.getPackageManager()
540                                .getApplicationEnabledSetting(pkgName);
541                        if (enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED
542                                || enabled == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
543                            return;
544                        }
545                    }
546                    pkgList = new String[]{pkgName};
547                }
548                if (pkgList != null && (pkgList.length > 0)) {
549                    for (String pkgName : pkgList) {
550                        cancelAllNotificationsInt(pkgName, 0, 0, !queryRestart,
551                                UserHandle.USER_ALL);
552                    }
553                }
554            } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
555                // Keep track of screen on/off state, but do not turn off the notification light
556                // until user passes through the lock screen or views the notification.
557                mScreenOn = true;
558            } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
559                mScreenOn = false;
560            } else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) {
561                mInCall = (intent.getStringExtra(TelephonyManager.EXTRA_STATE).equals(
562                        TelephonyManager.EXTRA_STATE_OFFHOOK));
563                updateNotificationPulse();
564            } else if (action.equals(Intent.ACTION_USER_STOPPED)) {
565                int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
566                if (userHandle >= 0) {
567                    cancelAllNotificationsInt(null, 0, 0, true, userHandle);
568                }
569            } else if (action.equals(Intent.ACTION_USER_PRESENT)) {
570                // turn off LED when user passes through lock screen
571                mNotificationLight.turnOff();
572            }
573        }
574    };
575
576    class SettingsObserver extends ContentObserver {
577        SettingsObserver(Handler handler) {
578            super(handler);
579        }
580
581        void observe() {
582            ContentResolver resolver = mContext.getContentResolver();
583            resolver.registerContentObserver(Settings.System.getUriFor(
584                    Settings.System.NOTIFICATION_LIGHT_PULSE), false, this);
585            update();
586        }
587
588        @Override public void onChange(boolean selfChange) {
589            update();
590        }
591
592        public void update() {
593            ContentResolver resolver = mContext.getContentResolver();
594            boolean pulseEnabled = Settings.System.getInt(resolver,
595                        Settings.System.NOTIFICATION_LIGHT_PULSE, 0) != 0;
596            if (mNotificationPulseEnabled != pulseEnabled) {
597                mNotificationPulseEnabled = pulseEnabled;
598                updateNotificationPulse();
599            }
600        }
601    }
602
603    static long[] getLongArray(Resources r, int resid, int maxlen, long[] def) {
604        int[] ar = r.getIntArray(resid);
605        if (ar == null) {
606            return def;
607        }
608        final int len = ar.length > maxlen ? maxlen : ar.length;
609        long[] out = new long[len];
610        for (int i=0; i<len; i++) {
611            out[i] = ar[i];
612        }
613        return out;
614    }
615
616    NotificationManagerService(Context context, StatusBarManagerService statusBar,
617            LightsService lights)
618    {
619        super();
620        mContext = context;
621        mVibrator = (Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE);
622        mAm = ActivityManagerNative.getDefault();
623        mToastQueue = new ArrayList<ToastRecord>();
624        mHandler = new WorkerHandler();
625
626        loadBlockDb();
627
628        mStatusBar = statusBar;
629        statusBar.setNotificationCallbacks(mNotificationCallbacks);
630
631        mNotificationLight = lights.getLight(LightsService.LIGHT_ID_NOTIFICATIONS);
632        mAttentionLight = lights.getLight(LightsService.LIGHT_ID_ATTENTION);
633
634        Resources resources = mContext.getResources();
635        mDefaultNotificationColor = resources.getColor(
636                com.android.internal.R.color.config_defaultNotificationColor);
637        mDefaultNotificationLedOn = resources.getInteger(
638                com.android.internal.R.integer.config_defaultNotificationLedOn);
639        mDefaultNotificationLedOff = resources.getInteger(
640                com.android.internal.R.integer.config_defaultNotificationLedOff);
641
642        mDefaultVibrationPattern = getLongArray(resources,
643                com.android.internal.R.array.config_defaultNotificationVibePattern,
644                VIBRATE_PATTERN_MAXLEN,
645                DEFAULT_VIBRATE_PATTERN);
646
647        mFallbackVibrationPattern = getLongArray(resources,
648                com.android.internal.R.array.config_notificationFallbackVibePattern,
649                VIBRATE_PATTERN_MAXLEN,
650                DEFAULT_VIBRATE_PATTERN);
651
652        // Don't start allowing notifications until the setup wizard has run once.
653        // After that, including subsequent boots, init with notifications turned on.
654        // This works on the first boot because the setup wizard will toggle this
655        // flag at least once and we'll go back to 0 after that.
656        if (0 == Settings.Global.getInt(mContext.getContentResolver(),
657                    Settings.Global.DEVICE_PROVISIONED, 0)) {
658            mDisabledNotifications = StatusBarManager.DISABLE_NOTIFICATION_ALERTS;
659        }
660
661        // register for various Intents
662        IntentFilter filter = new IntentFilter();
663        filter.addAction(Intent.ACTION_SCREEN_ON);
664        filter.addAction(Intent.ACTION_SCREEN_OFF);
665        filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
666        filter.addAction(Intent.ACTION_USER_PRESENT);
667        filter.addAction(Intent.ACTION_USER_STOPPED);
668        mContext.registerReceiver(mIntentReceiver, filter);
669        IntentFilter pkgFilter = new IntentFilter();
670        pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
671        pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
672        pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
673        pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
674        pkgFilter.addDataScheme("package");
675        mContext.registerReceiver(mIntentReceiver, pkgFilter);
676        IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
677        mContext.registerReceiver(mIntentReceiver, sdFilter);
678
679        SettingsObserver observer = new SettingsObserver(mHandler);
680        observer.observe();
681    }
682
683    void systemReady() {
684        mAudioService = IAudioService.Stub.asInterface(
685                ServiceManager.getService(Context.AUDIO_SERVICE));
686
687        // no beeping until we're basically done booting
688        mSystemReady = true;
689    }
690
691    // Toasts
692    // ============================================================================
693    public void enqueueToast(String pkg, ITransientNotification callback, int duration)
694    {
695        if (DBG) Slog.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback + " duration=" + duration);
696
697        if (pkg == null || callback == null) {
698            Slog.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback);
699            return ;
700        }
701
702        final boolean isSystemToast = ("android".equals(pkg));
703
704        if (ENABLE_BLOCKED_TOASTS && !isSystemToast && !areNotificationsEnabledForPackageInt(pkg)) {
705            Slog.e(TAG, "Suppressing toast from package " + pkg + " by user request.");
706            return;
707        }
708
709        synchronized (mToastQueue) {
710            int callingPid = Binder.getCallingPid();
711            long callingId = Binder.clearCallingIdentity();
712            try {
713                ToastRecord record;
714                int index = indexOfToastLocked(pkg, callback);
715                // If it's already in the queue, we update it in place, we don't
716                // move it to the end of the queue.
717                if (index >= 0) {
718                    record = mToastQueue.get(index);
719                    record.update(duration);
720                } else {
721                    // Limit the number of toasts that any given package except the android
722                    // package can enqueue.  Prevents DOS attacks and deals with leaks.
723                    if (!isSystemToast) {
724                        int count = 0;
725                        final int N = mToastQueue.size();
726                        for (int i=0; i<N; i++) {
727                             final ToastRecord r = mToastQueue.get(i);
728                             if (r.pkg.equals(pkg)) {
729                                 count++;
730                                 if (count >= MAX_PACKAGE_NOTIFICATIONS) {
731                                     Slog.e(TAG, "Package has already posted " + count
732                                            + " toasts. Not showing more. Package=" + pkg);
733                                     return;
734                                 }
735                             }
736                        }
737                    }
738
739                    record = new ToastRecord(callingPid, pkg, callback, duration);
740                    mToastQueue.add(record);
741                    index = mToastQueue.size() - 1;
742                    keepProcessAliveLocked(callingPid);
743                }
744                // If it's at index 0, it's the current toast.  It doesn't matter if it's
745                // new or just been updated.  Call back and tell it to show itself.
746                // If the callback fails, this will remove it from the list, so don't
747                // assume that it's valid after this.
748                if (index == 0) {
749                    showNextToastLocked();
750                }
751            } finally {
752                Binder.restoreCallingIdentity(callingId);
753            }
754        }
755    }
756
757    public void cancelToast(String pkg, ITransientNotification callback) {
758        Slog.i(TAG, "cancelToast pkg=" + pkg + " callback=" + callback);
759
760        if (pkg == null || callback == null) {
761            Slog.e(TAG, "Not cancelling notification. pkg=" + pkg + " callback=" + callback);
762            return ;
763        }
764
765        synchronized (mToastQueue) {
766            long callingId = Binder.clearCallingIdentity();
767            try {
768                int index = indexOfToastLocked(pkg, callback);
769                if (index >= 0) {
770                    cancelToastLocked(index);
771                } else {
772                    Slog.w(TAG, "Toast already cancelled. pkg=" + pkg + " callback=" + callback);
773                }
774            } finally {
775                Binder.restoreCallingIdentity(callingId);
776            }
777        }
778    }
779
780    private void showNextToastLocked() {
781        ToastRecord record = mToastQueue.get(0);
782        while (record != null) {
783            if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback);
784            try {
785                record.callback.show();
786                scheduleTimeoutLocked(record, false);
787                return;
788            } catch (RemoteException e) {
789                Slog.w(TAG, "Object died trying to show notification " + record.callback
790                        + " in package " + record.pkg);
791                // remove it from the list and let the process die
792                int index = mToastQueue.indexOf(record);
793                if (index >= 0) {
794                    mToastQueue.remove(index);
795                }
796                keepProcessAliveLocked(record.pid);
797                if (mToastQueue.size() > 0) {
798                    record = mToastQueue.get(0);
799                } else {
800                    record = null;
801                }
802            }
803        }
804    }
805
806    private void cancelToastLocked(int index) {
807        ToastRecord record = mToastQueue.get(index);
808        try {
809            record.callback.hide();
810        } catch (RemoteException e) {
811            Slog.w(TAG, "Object died trying to hide notification " + record.callback
812                    + " in package " + record.pkg);
813            // don't worry about this, we're about to remove it from
814            // the list anyway
815        }
816        mToastQueue.remove(index);
817        keepProcessAliveLocked(record.pid);
818        if (mToastQueue.size() > 0) {
819            // Show the next one. If the callback fails, this will remove
820            // it from the list, so don't assume that the list hasn't changed
821            // after this point.
822            showNextToastLocked();
823        }
824    }
825
826    private void scheduleTimeoutLocked(ToastRecord r, boolean immediate)
827    {
828        Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r);
829        long delay = immediate ? 0 : (r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY);
830        mHandler.removeCallbacksAndMessages(r);
831        mHandler.sendMessageDelayed(m, delay);
832    }
833
834    private void handleTimeout(ToastRecord record)
835    {
836        if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback);
837        synchronized (mToastQueue) {
838            int index = indexOfToastLocked(record.pkg, record.callback);
839            if (index >= 0) {
840                cancelToastLocked(index);
841            }
842        }
843    }
844
845    // lock on mToastQueue
846    private int indexOfToastLocked(String pkg, ITransientNotification callback)
847    {
848        IBinder cbak = callback.asBinder();
849        ArrayList<ToastRecord> list = mToastQueue;
850        int len = list.size();
851        for (int i=0; i<len; i++) {
852            ToastRecord r = list.get(i);
853            if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) {
854                return i;
855            }
856        }
857        return -1;
858    }
859
860    // lock on mToastQueue
861    private void keepProcessAliveLocked(int pid)
862    {
863        int toastCount = 0; // toasts from this pid
864        ArrayList<ToastRecord> list = mToastQueue;
865        int N = list.size();
866        for (int i=0; i<N; i++) {
867            ToastRecord r = list.get(i);
868            if (r.pid == pid) {
869                toastCount++;
870            }
871        }
872        try {
873            mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0);
874        } catch (RemoteException e) {
875            // Shouldn't happen.
876        }
877    }
878
879    private final class WorkerHandler extends Handler
880    {
881        @Override
882        public void handleMessage(Message msg)
883        {
884            switch (msg.what)
885            {
886                case MESSAGE_TIMEOUT:
887                    handleTimeout((ToastRecord)msg.obj);
888                    break;
889            }
890        }
891    }
892
893
894    // Notifications
895    // ============================================================================
896    public void enqueueNotificationWithTag(String pkg, String tag, int id, Notification notification,
897            int[] idOut, int userId)
898    {
899        enqueueNotificationInternal(pkg, Binder.getCallingUid(), Binder.getCallingPid(),
900                tag, id, notification, idOut, userId);
901    }
902
903    private final static int clamp(int x, int low, int high) {
904        return (x < low) ? low : ((x > high) ? high : x);
905    }
906
907    // Not exposed via Binder; for system use only (otherwise malicious apps could spoof the
908    // uid/pid of another application)
909    public void enqueueNotificationInternal(String pkg, int callingUid, int callingPid,
910            String tag, int id, Notification notification, int[] idOut, int userId)
911    {
912        if (DBG) {
913            Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id + " notification=" + notification);
914        }
915        checkCallerIsSystemOrSameApp(pkg);
916        final boolean isSystemNotification = ("android".equals(pkg));
917
918        userId = ActivityManager.handleIncomingUser(callingPid,
919                callingUid, userId, true, false, "enqueueNotification", pkg);
920        final UserHandle user = new UserHandle(userId);
921
922        // Limit the number of notifications that any given package except the android
923        // package can enqueue.  Prevents DOS attacks and deals with leaks.
924        if (!isSystemNotification) {
925            synchronized (mNotificationList) {
926                int count = 0;
927                final int N = mNotificationList.size();
928                for (int i=0; i<N; i++) {
929                    final NotificationRecord r = mNotificationList.get(i);
930                    if (r.pkg.equals(pkg) && r.userId == userId) {
931                        count++;
932                        if (count >= MAX_PACKAGE_NOTIFICATIONS) {
933                            Slog.e(TAG, "Package has already posted " + count
934                                    + " notifications.  Not showing more.  package=" + pkg);
935                            return;
936                        }
937                    }
938                }
939            }
940        }
941
942        // This conditional is a dirty hack to limit the logging done on
943        //     behalf of the download manager without affecting other apps.
944        if (!pkg.equals("com.android.providers.downloads")
945                || Log.isLoggable("DownloadManager", Log.VERBOSE)) {
946            EventLog.writeEvent(EventLogTags.NOTIFICATION_ENQUEUE, pkg, id, tag, userId,
947                    notification.toString());
948        }
949
950        if (pkg == null || notification == null) {
951            throw new IllegalArgumentException("null not allowed: pkg=" + pkg
952                    + " id=" + id + " notification=" + notification);
953        }
954        if (notification.icon != 0) {
955            if (notification.contentView == null) {
956                throw new IllegalArgumentException("contentView required: pkg=" + pkg
957                        + " id=" + id + " notification=" + notification);
958            }
959        }
960
961        // === Scoring ===
962
963        // 0. Sanitize inputs
964        notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN, Notification.PRIORITY_MAX);
965        // Migrate notification flags to scores
966        if (0 != (notification.flags & Notification.FLAG_HIGH_PRIORITY)) {
967            if (notification.priority < Notification.PRIORITY_MAX) notification.priority = Notification.PRIORITY_MAX;
968        } else if (SCORE_ONGOING_HIGHER && 0 != (notification.flags & Notification.FLAG_ONGOING_EVENT)) {
969            if (notification.priority < Notification.PRIORITY_HIGH) notification.priority = Notification.PRIORITY_HIGH;
970        }
971
972        // 1. initial score: buckets of 10, around the app
973        int score = notification.priority * NOTIFICATION_PRIORITY_MULTIPLIER; //[-20..20]
974
975        // 2. Consult external heuristics (TBD)
976
977        // 3. Apply local rules
978
979        // blocked apps
980        if (ENABLE_BLOCKED_NOTIFICATIONS && !isSystemNotification && !areNotificationsEnabledForPackageInt(pkg)) {
981            score = JUNK_SCORE;
982            Slog.e(TAG, "Suppressing notification from package " + pkg + " by user request.");
983        }
984
985        if (DBG) {
986            Slog.v(TAG, "Assigned score=" + score + " to " + notification);
987        }
988
989        if (score < SCORE_DISPLAY_THRESHOLD) {
990            // Notification will be blocked because the score is too low.
991            return;
992        }
993
994        synchronized (mNotificationList) {
995            NotificationRecord r = new NotificationRecord(pkg, tag, id,
996                    callingUid, callingPid, userId,
997                    score,
998                    notification);
999            NotificationRecord old = null;
1000
1001            int index = indexOfNotificationLocked(pkg, tag, id, userId);
1002            if (index < 0) {
1003                mNotificationList.add(r);
1004            } else {
1005                old = mNotificationList.remove(index);
1006                mNotificationList.add(index, r);
1007                // Make sure we don't lose the foreground service state.
1008                if (old != null) {
1009                    notification.flags |=
1010                        old.notification.flags&Notification.FLAG_FOREGROUND_SERVICE;
1011                }
1012            }
1013
1014            // Ensure if this is a foreground service that the proper additional
1015            // flags are set.
1016            if ((notification.flags&Notification.FLAG_FOREGROUND_SERVICE) != 0) {
1017                notification.flags |= Notification.FLAG_ONGOING_EVENT
1018                        | Notification.FLAG_NO_CLEAR;
1019            }
1020
1021            final int currentUser;
1022            final long token = Binder.clearCallingIdentity();
1023            try {
1024                currentUser = ActivityManager.getCurrentUser();
1025            } finally {
1026                Binder.restoreCallingIdentity(token);
1027            }
1028
1029            if (notification.icon != 0) {
1030                final StatusBarNotification n = new StatusBarNotification(
1031                        pkg, id, tag, r.uid, r.initialPid, score, notification, user);
1032                if (old != null && old.statusBarKey != null) {
1033                    r.statusBarKey = old.statusBarKey;
1034                    long identity = Binder.clearCallingIdentity();
1035                    try {
1036                        mStatusBar.updateNotification(r.statusBarKey, n);
1037                    }
1038                    finally {
1039                        Binder.restoreCallingIdentity(identity);
1040                    }
1041                } else {
1042                    long identity = Binder.clearCallingIdentity();
1043                    try {
1044                        r.statusBarKey = mStatusBar.addNotification(n);
1045                        if ((n.notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0) {
1046                            mAttentionLight.pulse();
1047                        }
1048                    }
1049                    finally {
1050                        Binder.restoreCallingIdentity(identity);
1051                    }
1052                }
1053                // Send accessibility events only for the current user.
1054                if (currentUser == userId) {
1055                    sendAccessibilityEvent(notification, pkg);
1056                }
1057            } else {
1058                Slog.e(TAG, "Ignoring notification with icon==0: " + notification);
1059                if (old != null && old.statusBarKey != null) {
1060                    long identity = Binder.clearCallingIdentity();
1061                    try {
1062                        mStatusBar.removeNotification(old.statusBarKey);
1063                    }
1064                    finally {
1065                        Binder.restoreCallingIdentity(identity);
1066                    }
1067                }
1068            }
1069
1070            // If we're not supposed to beep, vibrate, etc. then don't.
1071            if (((mDisabledNotifications & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) == 0)
1072                    && (!(old != null
1073                        && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 ))
1074                    && (r.userId == UserHandle.USER_ALL ||
1075                        (r.userId == userId && r.userId == currentUser))
1076                    && mSystemReady) {
1077
1078                final AudioManager audioManager = (AudioManager) mContext
1079                .getSystemService(Context.AUDIO_SERVICE);
1080
1081                // sound
1082                final boolean useDefaultSound =
1083                    (notification.defaults & Notification.DEFAULT_SOUND) != 0;
1084
1085                Uri soundUri = null;
1086                boolean hasValidSound = false;
1087
1088                if (useDefaultSound) {
1089                    soundUri = Settings.System.DEFAULT_NOTIFICATION_URI;
1090
1091                    // check to see if the default notification sound is silent
1092                    ContentResolver resolver = mContext.getContentResolver();
1093                    hasValidSound = Settings.System.getString(resolver,
1094                           Settings.System.NOTIFICATION_SOUND) != null;
1095                } else if (notification.sound != null) {
1096                    soundUri = notification.sound;
1097                    hasValidSound = (soundUri != null);
1098                }
1099
1100                if (hasValidSound) {
1101                    boolean looping = (notification.flags & Notification.FLAG_INSISTENT) != 0;
1102                    int audioStreamType;
1103                    if (notification.audioStreamType >= 0) {
1104                        audioStreamType = notification.audioStreamType;
1105                    } else {
1106                        audioStreamType = DEFAULT_STREAM_TYPE;
1107                    }
1108                    mSoundNotification = r;
1109                    // do not play notifications if stream volume is 0
1110                    // (typically because ringer mode is silent) or if speech recognition is active.
1111                    if ((audioManager.getStreamVolume(audioStreamType) != 0)
1112                            && !audioManager.isSpeechRecognitionActive()) {
1113                        final long identity = Binder.clearCallingIdentity();
1114                        try {
1115                            final IRingtonePlayer player = mAudioService.getRingtonePlayer();
1116                            if (player != null) {
1117                                player.playAsync(soundUri, user, looping, audioStreamType);
1118                            }
1119                        } catch (RemoteException e) {
1120                        } finally {
1121                            Binder.restoreCallingIdentity(identity);
1122                        }
1123                    }
1124                }
1125
1126                // vibrate
1127                // Does the notification want to specify its own vibration?
1128                final boolean hasCustomVibrate = notification.vibrate != null;
1129
1130                // new in 4.2: if there was supposed to be a sound and we're in vibrate mode,
1131                // and no other vibration is specified, we fall back to vibration
1132                final boolean convertSoundToVibration =
1133                           !hasCustomVibrate
1134                        && hasValidSound
1135                        && (audioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE);
1136
1137                // The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback.
1138                final boolean useDefaultVibrate =
1139                        (notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
1140
1141                if ((useDefaultVibrate || convertSoundToVibration || hasCustomVibrate)
1142                        && !(audioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT)) {
1143                    mVibrateNotification = r;
1144
1145                    if (useDefaultVibrate || convertSoundToVibration) {
1146                        // Escalate privileges so we can use the vibrator even if the notifying app
1147                        // does not have the VIBRATE permission.
1148                        long identity = Binder.clearCallingIdentity();
1149                        try {
1150                            mVibrator.vibrate(useDefaultVibrate ? mDefaultVibrationPattern
1151                                                                : mFallbackVibrationPattern,
1152                                ((notification.flags & Notification.FLAG_INSISTENT) != 0) ? 0: -1);
1153                        } finally {
1154                            Binder.restoreCallingIdentity(identity);
1155                        }
1156                    } else if (notification.vibrate.length > 1) {
1157                        // If you want your own vibration pattern, you need the VIBRATE permission
1158                        mVibrator.vibrate(notification.vibrate,
1159                            ((notification.flags & Notification.FLAG_INSISTENT) != 0) ? 0: -1);
1160                    }
1161                }
1162            }
1163
1164            // this option doesn't shut off the lights
1165
1166            // light
1167            // the most recent thing gets the light
1168            mLights.remove(old);
1169            if (mLedNotification == old) {
1170                mLedNotification = null;
1171            }
1172            //Slog.i(TAG, "notification.lights="
1173            //        + ((old.notification.lights.flags & Notification.FLAG_SHOW_LIGHTS) != 0));
1174            if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0) {
1175                mLights.add(r);
1176                updateLightsLocked();
1177            } else {
1178                if (old != null
1179                        && ((old.notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0)) {
1180                    updateLightsLocked();
1181                }
1182            }
1183        }
1184
1185        idOut[0] = id;
1186    }
1187
1188    private void sendAccessibilityEvent(Notification notification, CharSequence packageName) {
1189        AccessibilityManager manager = AccessibilityManager.getInstance(mContext);
1190        if (!manager.isEnabled()) {
1191            return;
1192        }
1193
1194        AccessibilityEvent event =
1195            AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
1196        event.setPackageName(packageName);
1197        event.setClassName(Notification.class.getName());
1198        event.setParcelableData(notification);
1199        CharSequence tickerText = notification.tickerText;
1200        if (!TextUtils.isEmpty(tickerText)) {
1201            event.getText().add(tickerText);
1202        }
1203
1204        manager.sendAccessibilityEvent(event);
1205    }
1206
1207    private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete) {
1208        // tell the app
1209        if (sendDelete) {
1210            if (r.notification.deleteIntent != null) {
1211                try {
1212                    r.notification.deleteIntent.send();
1213                } catch (PendingIntent.CanceledException ex) {
1214                    // do nothing - there's no relevant way to recover, and
1215                    //     no reason to let this propagate
1216                    Slog.w(TAG, "canceled PendingIntent for " + r.pkg, ex);
1217                }
1218            }
1219        }
1220
1221        // status bar
1222        if (r.notification.icon != 0) {
1223            long identity = Binder.clearCallingIdentity();
1224            try {
1225                mStatusBar.removeNotification(r.statusBarKey);
1226            }
1227            finally {
1228                Binder.restoreCallingIdentity(identity);
1229            }
1230            r.statusBarKey = null;
1231        }
1232
1233        // sound
1234        if (mSoundNotification == r) {
1235            mSoundNotification = null;
1236            final long identity = Binder.clearCallingIdentity();
1237            try {
1238                final IRingtonePlayer player = mAudioService.getRingtonePlayer();
1239                if (player != null) {
1240                    player.stopAsync();
1241                }
1242            } catch (RemoteException e) {
1243            } finally {
1244                Binder.restoreCallingIdentity(identity);
1245            }
1246        }
1247
1248        // vibrate
1249        if (mVibrateNotification == r) {
1250            mVibrateNotification = null;
1251            long identity = Binder.clearCallingIdentity();
1252            try {
1253                mVibrator.cancel();
1254            }
1255            finally {
1256                Binder.restoreCallingIdentity(identity);
1257            }
1258        }
1259
1260        // light
1261        mLights.remove(r);
1262        if (mLedNotification == r) {
1263            mLedNotification = null;
1264        }
1265    }
1266
1267    /**
1268     * Cancels a notification ONLY if it has all of the {@code mustHaveFlags}
1269     * and none of the {@code mustNotHaveFlags}.
1270     */
1271    private void cancelNotification(String pkg, String tag, int id, int mustHaveFlags,
1272            int mustNotHaveFlags, boolean sendDelete, int userId) {
1273        EventLog.writeEvent(EventLogTags.NOTIFICATION_CANCEL, pkg, id, tag, userId,
1274                mustHaveFlags, mustNotHaveFlags);
1275
1276        synchronized (mNotificationList) {
1277            int index = indexOfNotificationLocked(pkg, tag, id, userId);
1278            if (index >= 0) {
1279                NotificationRecord r = mNotificationList.get(index);
1280
1281                if ((r.notification.flags & mustHaveFlags) != mustHaveFlags) {
1282                    return;
1283                }
1284                if ((r.notification.flags & mustNotHaveFlags) != 0) {
1285                    return;
1286                }
1287
1288                mNotificationList.remove(index);
1289
1290                cancelNotificationLocked(r, sendDelete);
1291                updateLightsLocked();
1292            }
1293        }
1294    }
1295
1296    /**
1297     * Determine whether the userId applies to the notification in question, either because
1298     * they match exactly, or one of them is USER_ALL (which is treated as a wildcard).
1299     */
1300    private boolean notificationMatchesUserId(NotificationRecord r, int userId) {
1301        return
1302                // looking for USER_ALL notifications? match everything
1303                   userId == UserHandle.USER_ALL
1304                // a notification sent to USER_ALL matches any query
1305                || r.userId == UserHandle.USER_ALL
1306                // an exact user match
1307                || r.userId == userId;
1308    }
1309
1310    /**
1311     * Cancels all notifications from a given package that have all of the
1312     * {@code mustHaveFlags}.
1313     */
1314    boolean cancelAllNotificationsInt(String pkg, int mustHaveFlags,
1315            int mustNotHaveFlags, boolean doit, int userId) {
1316        EventLog.writeEvent(EventLogTags.NOTIFICATION_CANCEL_ALL, pkg, userId,
1317                mustHaveFlags, mustNotHaveFlags);
1318
1319        synchronized (mNotificationList) {
1320            final int N = mNotificationList.size();
1321            boolean canceledSomething = false;
1322            for (int i = N-1; i >= 0; --i) {
1323                NotificationRecord r = mNotificationList.get(i);
1324                if (!notificationMatchesUserId(r, userId)) {
1325                    continue;
1326                }
1327                // Don't remove notifications to all, if there's no package name specified
1328                if (r.userId == UserHandle.USER_ALL && pkg == null) {
1329                    continue;
1330                }
1331                if ((r.notification.flags & mustHaveFlags) != mustHaveFlags) {
1332                    continue;
1333                }
1334                if ((r.notification.flags & mustNotHaveFlags) != 0) {
1335                    continue;
1336                }
1337                if (pkg != null && !r.pkg.equals(pkg)) {
1338                    continue;
1339                }
1340                canceledSomething = true;
1341                if (!doit) {
1342                    return true;
1343                }
1344                mNotificationList.remove(i);
1345                cancelNotificationLocked(r, false);
1346            }
1347            if (canceledSomething) {
1348                updateLightsLocked();
1349            }
1350            return canceledSomething;
1351        }
1352    }
1353
1354    public void cancelNotificationWithTag(String pkg, String tag, int id, int userId) {
1355        checkCallerIsSystemOrSameApp(pkg);
1356        userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1357                Binder.getCallingUid(), userId, true, false, "cancelNotificationWithTag", pkg);
1358        // Don't allow client applications to cancel foreground service notis.
1359        cancelNotification(pkg, tag, id, 0,
1360                Binder.getCallingUid() == Process.SYSTEM_UID
1361                ? 0 : Notification.FLAG_FOREGROUND_SERVICE, false, userId);
1362    }
1363
1364    public void cancelAllNotifications(String pkg, int userId) {
1365        checkCallerIsSystemOrSameApp(pkg);
1366
1367        userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1368                Binder.getCallingUid(), userId, true, false, "cancelAllNotifications", pkg);
1369
1370        // Calling from user space, don't allow the canceling of actively
1371        // running foreground services.
1372        cancelAllNotificationsInt(pkg, 0, Notification.FLAG_FOREGROUND_SERVICE, true, userId);
1373    }
1374
1375    void checkCallerIsSystem() {
1376        int uid = Binder.getCallingUid();
1377        if (UserHandle.getAppId(uid) == Process.SYSTEM_UID || uid == 0) {
1378            return;
1379        }
1380        throw new SecurityException("Disallowed call for uid " + uid);
1381    }
1382
1383    void checkCallerIsSystemOrSameApp(String pkg) {
1384        int uid = Binder.getCallingUid();
1385        if (UserHandle.getAppId(uid) == Process.SYSTEM_UID || uid == 0) {
1386            return;
1387        }
1388        try {
1389            ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo(
1390                    pkg, 0, UserHandle.getCallingUserId());
1391            if (!UserHandle.isSameApp(ai.uid, uid)) {
1392                throw new SecurityException("Calling uid " + uid + " gave package"
1393                        + pkg + " which is owned by uid " + ai.uid);
1394            }
1395        } catch (RemoteException re) {
1396            throw new SecurityException("Unknown package " + pkg + "\n" + re);
1397        }
1398    }
1399
1400    void cancelAll(int userId) {
1401        synchronized (mNotificationList) {
1402            final int N = mNotificationList.size();
1403            for (int i=N-1; i>=0; i--) {
1404                NotificationRecord r = mNotificationList.get(i);
1405
1406                if (!notificationMatchesUserId(r, userId)) {
1407                    continue;
1408                }
1409
1410                if ((r.notification.flags & (Notification.FLAG_ONGOING_EVENT
1411                                | Notification.FLAG_NO_CLEAR)) == 0) {
1412                    mNotificationList.remove(i);
1413                    cancelNotificationLocked(r, true);
1414                }
1415            }
1416
1417            updateLightsLocked();
1418        }
1419    }
1420
1421    // lock on mNotificationList
1422    private void updateLightsLocked()
1423    {
1424        // handle notification lights
1425        if (mLedNotification == null) {
1426            // get next notification, if any
1427            int n = mLights.size();
1428            if (n > 0) {
1429                mLedNotification = mLights.get(n-1);
1430            }
1431        }
1432
1433        // Don't flash while we are in a call or screen is on
1434        if (mLedNotification == null || mInCall || mScreenOn) {
1435            mNotificationLight.turnOff();
1436        } else {
1437            int ledARGB = mLedNotification.notification.ledARGB;
1438            int ledOnMS = mLedNotification.notification.ledOnMS;
1439            int ledOffMS = mLedNotification.notification.ledOffMS;
1440            if ((mLedNotification.notification.defaults & Notification.DEFAULT_LIGHTS) != 0) {
1441                ledARGB = mDefaultNotificationColor;
1442                ledOnMS = mDefaultNotificationLedOn;
1443                ledOffMS = mDefaultNotificationLedOff;
1444            }
1445            if (mNotificationPulseEnabled) {
1446                // pulse repeatedly
1447                mNotificationLight.setFlashing(ledARGB, LightsService.LIGHT_FLASH_TIMED,
1448                        ledOnMS, ledOffMS);
1449            }
1450        }
1451    }
1452
1453    // lock on mNotificationList
1454    private int indexOfNotificationLocked(String pkg, String tag, int id, int userId)
1455    {
1456        ArrayList<NotificationRecord> list = mNotificationList;
1457        final int len = list.size();
1458        for (int i=0; i<len; i++) {
1459            NotificationRecord r = list.get(i);
1460            if (!notificationMatchesUserId(r, userId) || r.id != id) {
1461                continue;
1462            }
1463            if (tag == null) {
1464                if (r.tag != null) {
1465                    continue;
1466                }
1467            } else {
1468                if (!tag.equals(r.tag)) {
1469                    continue;
1470                }
1471            }
1472            if (r.pkg.equals(pkg)) {
1473                return i;
1474            }
1475        }
1476        return -1;
1477    }
1478
1479    private void updateNotificationPulse() {
1480        synchronized (mNotificationList) {
1481            updateLightsLocked();
1482        }
1483    }
1484
1485    // ======================================================================
1486    @Override
1487    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1488        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
1489                != PackageManager.PERMISSION_GRANTED) {
1490            pw.println("Permission Denial: can't dump NotificationManager from from pid="
1491                    + Binder.getCallingPid()
1492                    + ", uid=" + Binder.getCallingUid());
1493            return;
1494        }
1495
1496        pw.println("Current Notification Manager state:");
1497
1498        int N;
1499
1500        synchronized (mToastQueue) {
1501            N = mToastQueue.size();
1502            if (N > 0) {
1503                pw.println("  Toast Queue:");
1504                for (int i=0; i<N; i++) {
1505                    mToastQueue.get(i).dump(pw, "    ");
1506                }
1507                pw.println("  ");
1508            }
1509
1510        }
1511
1512        synchronized (mNotificationList) {
1513            N = mNotificationList.size();
1514            if (N > 0) {
1515                pw.println("  Notification List:");
1516                for (int i=0; i<N; i++) {
1517                    mNotificationList.get(i).dump(pw, "    ", mContext);
1518                }
1519                pw.println("  ");
1520            }
1521
1522            N = mLights.size();
1523            if (N > 0) {
1524                pw.println("  Lights List:");
1525                for (int i=0; i<N; i++) {
1526                    mLights.get(i).dump(pw, "    ", mContext);
1527                }
1528                pw.println("  ");
1529            }
1530
1531            pw.println("  mSoundNotification=" + mSoundNotification);
1532            pw.println("  mVibrateNotification=" + mVibrateNotification);
1533            pw.println("  mDisabledNotifications=0x" + Integer.toHexString(mDisabledNotifications));
1534            pw.println("  mSystemReady=" + mSystemReady);
1535        }
1536    }
1537}
1538