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