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