NotificationManagerService.java revision a263e4e438746f91fb78857bd569ba4f796a346d
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.notification;
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.ComponentName;
35import android.content.ContentResolver;
36import android.content.Context;
37import android.content.Intent;
38import android.content.IntentFilter;
39import android.content.ServiceConnection;
40import android.content.pm.ApplicationInfo;
41import android.content.pm.PackageInfo;
42import android.content.pm.PackageManager;
43import android.content.pm.ResolveInfo;
44import android.content.pm.ServiceInfo;
45import android.content.pm.PackageManager.NameNotFoundException;
46import android.content.pm.UserInfo;
47import android.content.res.Resources;
48import android.database.ContentObserver;
49import android.graphics.Bitmap;
50import android.media.AudioManager;
51import android.media.IRingtonePlayer;
52import android.net.Uri;
53import android.os.Binder;
54import android.os.Build;
55import android.os.Handler;
56import android.os.IBinder;
57import android.os.Message;
58import android.os.Process;
59import android.os.RemoteException;
60import android.os.UserHandle;
61import android.os.UserManager;
62import android.os.Vibrator;
63import android.provider.Settings;
64import android.service.notification.INotificationListener;
65import android.service.notification.NotificationListenerService;
66import android.service.notification.StatusBarNotification;
67import android.telephony.TelephonyManager;
68import android.text.TextUtils;
69import android.util.ArrayMap;
70import android.util.AtomicFile;
71import android.util.Log;
72import android.util.Slog;
73import android.util.SparseArray;
74import android.util.Xml;
75import android.view.accessibility.AccessibilityEvent;
76import android.view.accessibility.AccessibilityManager;
77import android.widget.Toast;
78
79import com.android.internal.R;
80import com.android.internal.notification.NotificationScorer;
81import com.android.server.EventLogTags;
82import com.android.server.notification.NotificationUsageStats.SingleNotificationStats;
83import com.android.server.statusbar.StatusBarManagerInternal;
84import com.android.server.SystemService;
85import com.android.server.lights.Light;
86import com.android.server.lights.LightsManager;
87
88import org.xmlpull.v1.XmlPullParser;
89import org.xmlpull.v1.XmlPullParserException;
90
91import java.io.File;
92import java.io.FileDescriptor;
93import java.io.FileInputStream;
94import java.io.FileNotFoundException;
95import java.io.IOException;
96import java.io.PrintWriter;
97import java.lang.reflect.Array;
98import java.util.ArrayDeque;
99import java.util.ArrayList;
100import java.util.Arrays;
101import java.util.HashSet;
102import java.util.Iterator;
103import java.util.List;
104import java.util.NoSuchElementException;
105import java.util.Set;
106
107import libcore.io.IoUtils;
108
109/** {@hide} */
110public class NotificationManagerService extends SystemService {
111    static final String TAG = "NotificationService";
112    static final boolean DBG = false;
113
114    static final int MAX_PACKAGE_NOTIFICATIONS = 50;
115
116    // message codes
117    static final int MESSAGE_TIMEOUT = 2;
118
119    static final int LONG_DELAY = 3500; // 3.5 seconds
120    static final int SHORT_DELAY = 2000; // 2 seconds
121
122    static final long[] DEFAULT_VIBRATE_PATTERN = {0, 250, 250, 250};
123    static final int VIBRATE_PATTERN_MAXLEN = 8 * 2 + 1; // up to eight bumps
124
125    static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_NOTIFICATION;
126    static final boolean SCORE_ONGOING_HIGHER = false;
127
128    static final int JUNK_SCORE = -1000;
129    static final int NOTIFICATION_PRIORITY_MULTIPLIER = 10;
130    static final int SCORE_DISPLAY_THRESHOLD = Notification.PRIORITY_MIN * NOTIFICATION_PRIORITY_MULTIPLIER;
131
132    // Notifications with scores below this will not interrupt the user, either via LED or
133    // sound or vibration
134    static final int SCORE_INTERRUPTION_THRESHOLD =
135            Notification.PRIORITY_LOW * NOTIFICATION_PRIORITY_MULTIPLIER;
136
137    static final boolean ENABLE_BLOCKED_NOTIFICATIONS = true;
138    static final boolean ENABLE_BLOCKED_TOASTS = true;
139
140    static final String ENABLED_NOTIFICATION_LISTENERS_SEPARATOR = ":";
141
142    private IActivityManager mAm;
143    AudioManager mAudioManager;
144    StatusBarManagerInternal mStatusBar;
145    Vibrator mVibrator;
146
147    final IBinder mForegroundToken = new Binder();
148    private WorkerHandler mHandler;
149
150    private Light mNotificationLight;
151    Light mAttentionLight;
152    private int mDefaultNotificationColor;
153    private int mDefaultNotificationLedOn;
154
155    private int mDefaultNotificationLedOff;
156    private long[] mDefaultVibrationPattern;
157
158    private long[] mFallbackVibrationPattern;
159    boolean mSystemReady;
160
161    private boolean mDisableNotificationAlerts;
162    NotificationRecord mSoundNotification;
163    NotificationRecord mVibrateNotification;
164
165    // for enabling and disabling notification pulse behavior
166    private boolean mScreenOn = true;
167    private boolean mInCall = false;
168    private boolean mNotificationPulseEnabled;
169
170    // used as a mutex for access to all active notifications & listeners
171    final ArrayList<NotificationRecord> mNotificationList =
172            new ArrayList<NotificationRecord>();
173    final ArrayMap<String, NotificationRecord> mNotificationsByKey =
174            new ArrayMap<String, NotificationRecord>();
175    final ArrayList<ToastRecord> mToastQueue = new ArrayList<ToastRecord>();
176
177    ArrayList<NotificationRecord> mLights = new ArrayList<NotificationRecord>();
178    NotificationRecord mLedNotification;
179
180    private AppOpsManager mAppOps;
181
182    // contains connections to all connected listeners, including app services
183    // and system listeners
184    private ArrayList<NotificationListenerInfo> mListeners
185            = new ArrayList<NotificationListenerInfo>();
186    // things that will be put into mListeners as soon as they're ready
187    private ArrayList<String> mServicesBinding = new ArrayList<String>();
188    // lists the component names of all enabled (and therefore connected) listener
189    // app services for current profiles.
190    private HashSet<ComponentName> mEnabledListenersForCurrentProfiles
191            = new HashSet<ComponentName>();
192    // Just the packages from mEnabledListenersForCurrentProfiles
193    private HashSet<String> mEnabledListenerPackageNames = new HashSet<String>();
194
195    // Notification control database. For now just contains disabled packages.
196    private AtomicFile mPolicyFile;
197    private HashSet<String> mBlockedPackages = new HashSet<String>();
198
199    private static final int DB_VERSION = 1;
200
201    private static final String TAG_BODY = "notification-policy";
202    private static final String ATTR_VERSION = "version";
203
204    private static final String TAG_BLOCKED_PKGS = "blocked-packages";
205    private static final String TAG_PACKAGE = "package";
206    private static final String ATTR_NAME = "name";
207
208    final ArrayList<NotificationScorer> mScorers = new ArrayList<NotificationScorer>();
209
210    private final NotificationUsageStats mUsageStats = new NotificationUsageStats();
211
212    private int mZenMode;
213    // temporary, until we update apps to provide metadata
214    private static final Set<String> CALL_PACKAGES = new HashSet<String>(Arrays.asList(
215            "com.google.android.dialer",
216            "com.android.phone"
217            ));
218    private static final Set<String> ALARM_PACKAGES = new HashSet<String>(Arrays.asList(
219            "com.google.android.deskclock"
220            ));
221    private static final String EXTRA_INTERCEPT = "android.intercept";
222
223    // Profiles of the current user.
224    final protected SparseArray<UserInfo> mCurrentProfiles = new SparseArray<UserInfo>();
225
226    private static final int MY_UID = Process.myUid();
227    private static final int MY_PID = Process.myPid();
228    private static final int REASON_DELEGATE_CLICK = 1;
229    private static final int REASON_DELEGATE_CANCEL = 2;
230    private static final int REASON_DELEGATE_CANCEL_ALL = 3;
231    private static final int REASON_DELEGATE_ERROR = 4;
232    private static final int REASON_PACKAGE_CHANGED = 5;
233    private static final int REASON_USER_STOPPED = 6;
234    private static final int REASON_PACKAGE_BANNED = 7;
235    private static final int REASON_NOMAN_CANCEL = 8;
236    private static final int REASON_NOMAN_CANCEL_ALL = 9;
237    private static final int REASON_LISTENER_CANCEL = 10;
238    private static final int REASON_LISTENER_CANCEL_ALL = 11;
239
240    private class NotificationListenerInfo implements IBinder.DeathRecipient {
241        INotificationListener listener;
242        ComponentName component;
243        int userid;
244        boolean isSystem;
245        ServiceConnection connection;
246        int targetSdkVersion;
247
248        public NotificationListenerInfo(INotificationListener listener, ComponentName component,
249                int userid, boolean isSystem, int targetSdkVersion) {
250            this.listener = listener;
251            this.component = component;
252            this.userid = userid;
253            this.isSystem = isSystem;
254            this.connection = null;
255            this.targetSdkVersion = targetSdkVersion;
256        }
257
258        public NotificationListenerInfo(INotificationListener listener, ComponentName component,
259                int userid, ServiceConnection connection, int targetSdkVersion) {
260            this.listener = listener;
261            this.component = component;
262            this.userid = userid;
263            this.isSystem = false;
264            this.connection = connection;
265            this.targetSdkVersion = targetSdkVersion;
266        }
267
268        boolean enabledAndUserMatches(StatusBarNotification sbn) {
269            final int nid = sbn.getUserId();
270            if (!isEnabledForCurrentProfiles()) {
271                return false;
272            }
273            if (this.userid == UserHandle.USER_ALL) return true;
274            if (nid == UserHandle.USER_ALL || nid == this.userid) return true;
275            return supportsProfiles() && isCurrentProfile(nid);
276        }
277
278        boolean supportsProfiles() {
279            return targetSdkVersion >= Build.VERSION_CODES.L;
280        }
281
282        public void notifyPostedIfUserMatch(StatusBarNotification sbn) {
283            if (!enabledAndUserMatches(sbn)) {
284                return;
285            }
286            try {
287                listener.onNotificationPosted(sbn);
288            } catch (RemoteException ex) {
289                Log.e(TAG, "unable to notify listener (posted): " + listener, ex);
290            }
291        }
292
293        public void notifyRemovedIfUserMatch(StatusBarNotification sbn) {
294            if (!enabledAndUserMatches(sbn)) return;
295            try {
296                listener.onNotificationRemoved(sbn);
297            } catch (RemoteException ex) {
298                Log.e(TAG, "unable to notify listener (removed): " + listener, ex);
299            }
300        }
301
302        @Override
303        public void binderDied() {
304            // Remove the listener, but don't unbind from the service. The system will bring the
305            // service back up, and the onServiceConnected handler will readd the listener with the
306            // new binding. If this isn't a bound service, and is just a registered
307            // INotificationListener, just removing it from the list is all we need to do anyway.
308            removeListenerImpl(this.listener, this.userid);
309        }
310
311        /** convenience method for looking in mEnabledListenersForCurrentProfiles */
312        public boolean isEnabledForCurrentProfiles() {
313            if (this.isSystem) return true;
314            if (this.connection == null) return false;
315            return mEnabledListenersForCurrentProfiles.contains(this.component);
316        }
317    }
318
319    private static class Archive {
320        static final int BUFFER_SIZE = 250;
321        ArrayDeque<StatusBarNotification> mBuffer = new ArrayDeque<StatusBarNotification>(BUFFER_SIZE);
322
323        public Archive() {
324        }
325
326        public String toString() {
327            final StringBuilder sb = new StringBuilder();
328            final int N = mBuffer.size();
329            sb.append("Archive (");
330            sb.append(N);
331            sb.append(" notification");
332            sb.append((N==1)?")":"s)");
333            return sb.toString();
334        }
335
336        public void record(StatusBarNotification nr) {
337            if (mBuffer.size() == BUFFER_SIZE) {
338                mBuffer.removeFirst();
339            }
340
341            // We don't want to store the heavy bits of the notification in the archive,
342            // but other clients in the system process might be using the object, so we
343            // store a (lightened) copy.
344            mBuffer.addLast(nr.cloneLight());
345        }
346
347
348        public void clear() {
349            mBuffer.clear();
350        }
351
352        public Iterator<StatusBarNotification> descendingIterator() {
353            return mBuffer.descendingIterator();
354        }
355        public Iterator<StatusBarNotification> ascendingIterator() {
356            return mBuffer.iterator();
357        }
358        public Iterator<StatusBarNotification> filter(
359                final Iterator<StatusBarNotification> iter, final String pkg, final int userId) {
360            return new Iterator<StatusBarNotification>() {
361                StatusBarNotification mNext = findNext();
362
363                private StatusBarNotification findNext() {
364                    while (iter.hasNext()) {
365                        StatusBarNotification nr = iter.next();
366                        if ((pkg == null || nr.getPackageName() == pkg)
367                                && (userId == UserHandle.USER_ALL || nr.getUserId() == userId)) {
368                            return nr;
369                        }
370                    }
371                    return null;
372                }
373
374                @Override
375                public boolean hasNext() {
376                    return mNext == null;
377                }
378
379                @Override
380                public StatusBarNotification next() {
381                    StatusBarNotification next = mNext;
382                    if (next == null) {
383                        throw new NoSuchElementException();
384                    }
385                    mNext = findNext();
386                    return next;
387                }
388
389                @Override
390                public void remove() {
391                    iter.remove();
392                }
393            };
394        }
395
396        public StatusBarNotification[] getArray(int count) {
397            if (count == 0) count = Archive.BUFFER_SIZE;
398            final StatusBarNotification[] a
399                    = new StatusBarNotification[Math.min(count, mBuffer.size())];
400            Iterator<StatusBarNotification> iter = descendingIterator();
401            int i=0;
402            while (iter.hasNext() && i < count) {
403                a[i++] = iter.next();
404            }
405            return a;
406        }
407
408        public StatusBarNotification[] getArray(int count, String pkg, int userId) {
409            if (count == 0) count = Archive.BUFFER_SIZE;
410            final StatusBarNotification[] a
411                    = new StatusBarNotification[Math.min(count, mBuffer.size())];
412            Iterator<StatusBarNotification> iter = filter(descendingIterator(), pkg, userId);
413            int i=0;
414            while (iter.hasNext() && i < count) {
415                a[i++] = iter.next();
416            }
417            return a;
418        }
419
420    }
421
422    Archive mArchive = new Archive();
423
424    private void loadBlockDb() {
425        synchronized(mBlockedPackages) {
426            if (mPolicyFile == null) {
427                File dir = new File("/data/system");
428                mPolicyFile = new AtomicFile(new File(dir, "notification_policy.xml"));
429
430                mBlockedPackages.clear();
431
432                FileInputStream infile = null;
433                try {
434                    infile = mPolicyFile.openRead();
435                    final XmlPullParser parser = Xml.newPullParser();
436                    parser.setInput(infile, null);
437
438                    int type;
439                    String tag;
440                    int version = DB_VERSION;
441                    while ((type = parser.next()) != END_DOCUMENT) {
442                        tag = parser.getName();
443                        if (type == START_TAG) {
444                            if (TAG_BODY.equals(tag)) {
445                                version = Integer.parseInt(
446                                        parser.getAttributeValue(null, ATTR_VERSION));
447                            } else if (TAG_BLOCKED_PKGS.equals(tag)) {
448                                while ((type = parser.next()) != END_DOCUMENT) {
449                                    tag = parser.getName();
450                                    if (TAG_PACKAGE.equals(tag)) {
451                                        mBlockedPackages.add(
452                                                parser.getAttributeValue(null, ATTR_NAME));
453                                    } else if (TAG_BLOCKED_PKGS.equals(tag) && type == END_TAG) {
454                                        break;
455                                    }
456                                }
457                            }
458                        }
459                    }
460                } catch (FileNotFoundException e) {
461                    // No data yet
462                } catch (IOException e) {
463                    Log.wtf(TAG, "Unable to read blocked notifications database", e);
464                } catch (NumberFormatException e) {
465                    Log.wtf(TAG, "Unable to parse blocked notifications database", e);
466                } catch (XmlPullParserException e) {
467                    Log.wtf(TAG, "Unable to parse blocked notifications database", e);
468                } finally {
469                    IoUtils.closeQuietly(infile);
470                }
471            }
472        }
473    }
474
475    /** Use this when you actually want to post a notification or toast.
476     *
477     * Unchecked. Not exposed via Binder, but can be called in the course of enqueue*().
478     */
479    private boolean noteNotificationOp(String pkg, int uid) {
480        if (mAppOps.noteOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
481                != AppOpsManager.MODE_ALLOWED) {
482            Slog.v(TAG, "notifications are disabled by AppOps for " + pkg);
483            return false;
484        }
485        return true;
486    }
487
488    private static String idDebugString(Context baseContext, String packageName, int id) {
489        Context c = null;
490
491        if (packageName != null) {
492            try {
493                c = baseContext.createPackageContext(packageName, 0);
494            } catch (NameNotFoundException e) {
495                c = baseContext;
496            }
497        } else {
498            c = baseContext;
499        }
500
501        String pkg;
502        String type;
503        String name;
504
505        Resources r = c.getResources();
506        try {
507            return r.getResourceName(id);
508        } catch (Resources.NotFoundException e) {
509            return "<name unknown>";
510        }
511    }
512
513
514    /**
515     * Remove notification access for any services that no longer exist.
516     */
517    void disableNonexistentListeners() {
518        int[] userIds = getCurrentProfileIds();
519        final int N = userIds.length;
520        for (int i = 0 ; i < N; ++i) {
521            disableNonexistentListeners(userIds[i]);
522        }
523    }
524
525    void disableNonexistentListeners(int userId) {
526        String flatIn = Settings.Secure.getStringForUser(
527                getContext().getContentResolver(),
528                Settings.Secure.ENABLED_NOTIFICATION_LISTENERS,
529                userId);
530        if (!TextUtils.isEmpty(flatIn)) {
531            if (DBG) Slog.v(TAG, "flat before: " + flatIn);
532            PackageManager pm = getContext().getPackageManager();
533            List<ResolveInfo> installedServices = pm.queryIntentServicesAsUser(
534                    new Intent(NotificationListenerService.SERVICE_INTERFACE),
535                    PackageManager.GET_SERVICES | PackageManager.GET_META_DATA,
536                    userId);
537
538            Set<ComponentName> installed = new HashSet<ComponentName>();
539            for (int i = 0, count = installedServices.size(); i < count; i++) {
540                ResolveInfo resolveInfo = installedServices.get(i);
541                ServiceInfo info = resolveInfo.serviceInfo;
542
543                if (!android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE.equals(
544                                info.permission)) {
545                    Slog.w(TAG, "Skipping notification listener service "
546                            + info.packageName + "/" + info.name
547                            + ": it does not require the permission "
548                            + android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE);
549                    continue;
550                }
551                installed.add(new ComponentName(info.packageName, info.name));
552            }
553
554            String flatOut = "";
555            if (!installed.isEmpty()) {
556                String[] enabled = flatIn.split(ENABLED_NOTIFICATION_LISTENERS_SEPARATOR);
557                ArrayList<String> remaining = new ArrayList<String>(enabled.length);
558                for (int i = 0; i < enabled.length; i++) {
559                    ComponentName enabledComponent = ComponentName.unflattenFromString(enabled[i]);
560                    if (installed.contains(enabledComponent)) {
561                        remaining.add(enabled[i]);
562                    }
563                }
564                flatOut = TextUtils.join(ENABLED_NOTIFICATION_LISTENERS_SEPARATOR, remaining);
565            }
566            if (DBG) Slog.v(TAG, "flat after: " + flatOut);
567            if (!flatIn.equals(flatOut)) {
568                Settings.Secure.putStringForUser(getContext().getContentResolver(),
569                        Settings.Secure.ENABLED_NOTIFICATION_LISTENERS,
570                        flatOut, userId);
571            }
572        }
573    }
574
575    /**
576     * Called whenever packages change, the user switches, or ENABLED_NOTIFICATION_LISTENERS
577     * is altered. (For example in response to USER_SWITCHED in our broadcast receiver)
578     */
579    void rebindListenerServices() {
580        final int[] userIds = getCurrentProfileIds();
581        final int nUserIds = userIds.length;
582
583        final SparseArray<String> flat = new SparseArray<String>();
584
585        for (int i = 0; i < nUserIds; ++i) {
586            flat.put(userIds[i], Settings.Secure.getStringForUser(
587                    getContext().getContentResolver(),
588                    Settings.Secure.ENABLED_NOTIFICATION_LISTENERS,
589                    userIds[i]));
590        }
591
592        NotificationListenerInfo[] toRemove = new NotificationListenerInfo[mListeners.size()];
593        final SparseArray<ArrayList<ComponentName>> toAdd
594                = new SparseArray<ArrayList<ComponentName>>();
595
596        synchronized (mNotificationList) {
597            // unbind and remove all existing listeners
598            toRemove = mListeners.toArray(toRemove);
599
600            final HashSet<ComponentName> newEnabled = new HashSet<ComponentName>();
601            final HashSet<String> newPackages = new HashSet<String>();
602
603            for (int i = 0; i < nUserIds; ++i) {
604                final ArrayList<ComponentName> add = new ArrayList<ComponentName>();
605                toAdd.put(userIds[i], add);
606
607                // decode the list of components
608                String toDecode = flat.get(userIds[i]);
609                if (toDecode != null) {
610                    String[] components = toDecode.split(ENABLED_NOTIFICATION_LISTENERS_SEPARATOR);
611                    for (int j = 0; j < components.length; j++) {
612                        final ComponentName component
613                                = ComponentName.unflattenFromString(components[j]);
614                        if (component != null) {
615                            newEnabled.add(component);
616                            add.add(component);
617                            newPackages.add(component.getPackageName());
618                        }
619                    }
620
621                }
622            }
623            mEnabledListenersForCurrentProfiles = newEnabled;
624            mEnabledListenerPackageNames = newPackages;
625        }
626
627        for (NotificationListenerInfo info : toRemove) {
628            final ComponentName component = info.component;
629            final int oldUser = info.userid;
630            Slog.v(TAG, "disabling notification listener for user "
631                    + oldUser + ": " + component);
632            unregisterListenerService(component, info.userid);
633        }
634
635        for (int i = 0; i < nUserIds; ++i) {
636            final ArrayList<ComponentName> add = toAdd.get(userIds[i]);
637            final int N = add.size();
638            for (int j = 0; j < N; j++) {
639                final ComponentName component = add.get(j);
640                Slog.v(TAG, "enabling notification listener for user " + userIds[i] + ": "
641                        + component);
642                registerListenerService(component, userIds[i]);
643            }
644        }
645    }
646
647
648    /**
649     * Version of registerListener that takes the name of a
650     * {@link android.service.notification.NotificationListenerService} to bind to.
651     *
652     * This is the mechanism by which third parties may subscribe to notifications.
653     */
654    private void registerListenerService(final ComponentName name, final int userid) {
655        checkCallerIsSystem();
656
657        if (DBG) Slog.v(TAG, "registerListenerService: " + name + " u=" + userid);
658
659        synchronized (mNotificationList) {
660            final String servicesBindingTag = name.toString() + "/" + userid;
661            if (mServicesBinding.contains(servicesBindingTag)) {
662                // stop registering this thing already! we're working on it
663                return;
664            }
665            mServicesBinding.add(servicesBindingTag);
666
667            final int N = mListeners.size();
668            for (int i=N-1; i>=0; i--) {
669                final NotificationListenerInfo info = mListeners.get(i);
670                if (name.equals(info.component)
671                        && info.userid == userid) {
672                    // cut old connections
673                    if (DBG) Slog.v(TAG, "    disconnecting old listener: " + info.listener);
674                    mListeners.remove(i);
675                    if (info.connection != null) {
676                        getContext().unbindService(info.connection);
677                    }
678                }
679            }
680
681            Intent intent = new Intent(NotificationListenerService.SERVICE_INTERFACE);
682            intent.setComponent(name);
683
684            intent.putExtra(Intent.EXTRA_CLIENT_LABEL,
685                    R.string.notification_listener_binding_label);
686
687            final PendingIntent pendingIntent = PendingIntent.getActivity(
688                    getContext(), 0, new Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS), 0);
689            intent.putExtra(Intent.EXTRA_CLIENT_INTENT, pendingIntent);
690
691            ApplicationInfo appInfo = null;
692            try {
693                appInfo = getContext().getPackageManager().getApplicationInfo(
694                        name.getPackageName(), 0);
695            } catch (NameNotFoundException e) {
696                // Ignore if the package doesn't exist we won't be able to bind to the service.
697            }
698            final int targetSdkVersion =
699                    appInfo != null ? appInfo.targetSdkVersion : Build.VERSION_CODES.BASE;
700
701            try {
702                if (DBG) Slog.v(TAG, "binding: " + intent);
703                if (!getContext().bindServiceAsUser(intent,
704                        new ServiceConnection() {
705                            INotificationListener mListener;
706
707                            @Override
708                            public void onServiceConnected(ComponentName name, IBinder service) {
709                                boolean added = false;
710                                synchronized (mNotificationList) {
711                                    mServicesBinding.remove(servicesBindingTag);
712                                    try {
713                                        mListener = INotificationListener.Stub.asInterface(service);
714                                        NotificationListenerInfo info
715                                                = new NotificationListenerInfo(
716                                                        mListener, name, userid, this,
717                                                        targetSdkVersion);
718                                        service.linkToDeath(info, 0);
719                                        added = mListeners.add(info);
720                                    } catch (RemoteException e) {
721                                        // already dead
722                                    }
723                                }
724                                if (added) {
725                                    final String[] keys =
726                                            getActiveNotificationKeysFromListener(mListener);
727                                    try {
728                                        mListener.onListenerConnected(keys);
729                                    } catch (RemoteException e) {
730                                        // we tried
731                                    }
732                                }
733                            }
734
735                            @Override
736                            public void onServiceDisconnected(ComponentName name) {
737                                Slog.v(TAG, "notification listener connection lost: " + name);
738                            }
739                        },
740                        Context.BIND_AUTO_CREATE,
741                        new UserHandle(userid)))
742                {
743                    mServicesBinding.remove(servicesBindingTag);
744                    Slog.w(TAG, "Unable to bind listener service: " + intent);
745                    return;
746                }
747            } catch (SecurityException ex) {
748                Slog.e(TAG, "Unable to bind listener service: " + intent, ex);
749                return;
750            }
751        }
752    }
753
754
755    /**
756     * Remove a listener service for the given user by ComponentName
757     */
758    private void unregisterListenerService(ComponentName name, int userid) {
759        checkCallerIsSystem();
760
761        synchronized (mNotificationList) {
762            final int N = mListeners.size();
763            for (int i=N-1; i>=0; i--) {
764                final NotificationListenerInfo info = mListeners.get(i);
765                if (name.equals(info.component)
766                        && info.userid == userid) {
767                    mListeners.remove(i);
768                    if (info.connection != null) {
769                        try {
770                            getContext().unbindService(info.connection);
771                        } catch (IllegalArgumentException ex) {
772                            // something happened to the service: we think we have a connection
773                            // but it's bogus.
774                            Slog.e(TAG, "Listener " + name + " could not be unbound: " + ex);
775                        }
776                    }
777                }
778            }
779        }
780    }
781
782    /**
783     * asynchronously notify all listeners about a new notification
784     */
785    void notifyPostedLocked(NotificationRecord n) {
786        // make a copy in case changes are made to the underlying Notification object
787        final StatusBarNotification sbn = n.sbn.clone();
788        for (final NotificationListenerInfo info : mListeners) {
789            mHandler.post(new Runnable() {
790                @Override
791                public void run() {
792                    info.notifyPostedIfUserMatch(sbn);
793                }});
794        }
795    }
796
797    /**
798     * asynchronously notify all listeners about a removed notification
799     */
800    void notifyRemovedLocked(NotificationRecord n) {
801        // make a copy in case changes are made to the underlying Notification object
802        // NOTE: this copy is lightweight: it doesn't include heavyweight parts of the notification
803        final StatusBarNotification sbn_light = n.sbn.cloneLight();
804
805        for (final NotificationListenerInfo info : mListeners) {
806            mHandler.post(new Runnable() {
807                @Override
808                public void run() {
809                    info.notifyRemovedIfUserMatch(sbn_light);
810                }});
811        }
812    }
813
814    // -- APIs to support listeners clicking/clearing notifications --
815
816    private void checkNullListener(INotificationListener listener) {
817        if (listener == null) {
818            throw new IllegalArgumentException("Listener must not be null");
819        }
820    }
821
822    private NotificationListenerInfo checkListenerTokenLocked(INotificationListener listener) {
823        checkNullListener(listener);
824        final IBinder token = listener.asBinder();
825        final int N = mListeners.size();
826        for (int i=0; i<N; i++) {
827            final NotificationListenerInfo info = mListeners.get(i);
828            if (info.listener.asBinder() == token) return info;
829        }
830        throw new SecurityException("Disallowed call from unknown listener: " + listener);
831    }
832
833
834
835    // -- end of listener APIs --
836
837    public static final class NotificationRecord
838    {
839        final StatusBarNotification sbn;
840        final SingleNotificationStats stats = new SingleNotificationStats();
841        IBinder statusBarKey;
842
843        NotificationRecord(StatusBarNotification sbn)
844        {
845            this.sbn = sbn;
846        }
847
848        public Notification getNotification() { return sbn.getNotification(); }
849        public int getFlags() { return sbn.getNotification().flags; }
850        public int getUserId() { return sbn.getUserId(); }
851
852        void dump(PrintWriter pw, String prefix, Context baseContext) {
853            final Notification notification = sbn.getNotification();
854            pw.println(prefix + this);
855            pw.println(prefix + "  uid=" + sbn.getUid() + " userId=" + sbn.getUserId());
856            pw.println(prefix + "  icon=0x" + Integer.toHexString(notification.icon)
857                    + " / " + idDebugString(baseContext, sbn.getPackageName(), notification.icon));
858            pw.println(prefix + "  pri=" + notification.priority + " score=" + sbn.getScore());
859            pw.println(prefix + "  key=" + sbn.getKey());
860            pw.println(prefix + "  contentIntent=" + notification.contentIntent);
861            pw.println(prefix + "  deleteIntent=" + notification.deleteIntent);
862            pw.println(prefix + "  tickerText=" + notification.tickerText);
863            pw.println(prefix + "  contentView=" + notification.contentView);
864            pw.println(prefix + String.format("  defaults=0x%08x flags=0x%08x",
865                    notification.defaults, notification.flags));
866            pw.println(prefix + "  sound=" + notification.sound);
867            pw.println(prefix + "  vibrate=" + Arrays.toString(notification.vibrate));
868            pw.println(prefix + String.format("  led=0x%08x onMs=%d offMs=%d",
869                    notification.ledARGB, notification.ledOnMS, notification.ledOffMS));
870            if (notification.actions != null && notification.actions.length > 0) {
871                pw.println(prefix + "  actions={");
872                final int N = notification.actions.length;
873                for (int i=0; i<N; i++) {
874                    final Notification.Action action = notification.actions[i];
875                    pw.println(String.format("%s    [%d] \"%s\" -> %s",
876                            prefix,
877                            i,
878                            action.title,
879                            action.actionIntent.toString()
880                            ));
881                }
882                pw.println(prefix + "  }");
883            }
884            if (notification.extras != null && notification.extras.size() > 0) {
885                pw.println(prefix + "  extras={");
886                for (String key : notification.extras.keySet()) {
887                    pw.print(prefix + "    " + key + "=");
888                    Object val = notification.extras.get(key);
889                    if (val == null) {
890                        pw.println("null");
891                    } else {
892                        pw.print(val.toString());
893                        if (val instanceof Bitmap) {
894                            pw.print(String.format(" (%dx%d)",
895                                    ((Bitmap) val).getWidth(),
896                                    ((Bitmap) val).getHeight()));
897                        } else if (val.getClass().isArray()) {
898                            pw.println(" {");
899                            final int N = Array.getLength(val);
900                            for (int i=0; i<N; i++) {
901                                if (i > 0) pw.println(",");
902                                pw.print(prefix + "      " + Array.get(val, i));
903                            }
904                            pw.print("\n" + prefix + "    }");
905                        }
906                        pw.println();
907                    }
908                }
909                pw.println(prefix + "  }");
910            }
911            pw.println(prefix + "  stats=" + stats.toString());
912        }
913
914        @Override
915        public final String toString() {
916            return String.format(
917                    "NotificationRecord(0x%08x: pkg=%s user=%s id=%d tag=%s score=%d key=%s: %s)",
918                    System.identityHashCode(this),
919                    this.sbn.getPackageName(), this.sbn.getUser(), this.sbn.getId(),
920                    this.sbn.getTag(), this.sbn.getScore(), this.sbn.getKey(),
921                    this.sbn.getNotification());
922        }
923    }
924
925    private static final class ToastRecord
926    {
927        final int pid;
928        final String pkg;
929        final ITransientNotification callback;
930        int duration;
931
932        ToastRecord(int pid, String pkg, ITransientNotification callback, int duration)
933        {
934            this.pid = pid;
935            this.pkg = pkg;
936            this.callback = callback;
937            this.duration = duration;
938        }
939
940        void update(int duration) {
941            this.duration = duration;
942        }
943
944        void dump(PrintWriter pw, String prefix) {
945            pw.println(prefix + this);
946        }
947
948        @Override
949        public final String toString()
950        {
951            return "ToastRecord{"
952                + Integer.toHexString(System.identityHashCode(this))
953                + " pkg=" + pkg
954                + " callback=" + callback
955                + " duration=" + duration;
956        }
957    }
958
959    private final NotificationDelegate mNotificationDelegate = new NotificationDelegate() {
960
961        @Override
962        public void onSetDisabled(int status) {
963            synchronized (mNotificationList) {
964                mDisableNotificationAlerts = (status & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0;
965                if (mDisableNotificationAlerts) {
966                    // cancel whatever's going on
967                    long identity = Binder.clearCallingIdentity();
968                    try {
969                        final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
970                        if (player != null) {
971                            player.stopAsync();
972                        }
973                    } catch (RemoteException e) {
974                    } finally {
975                        Binder.restoreCallingIdentity(identity);
976                    }
977
978                    identity = Binder.clearCallingIdentity();
979                    try {
980                        mVibrator.cancel();
981                    } finally {
982                        Binder.restoreCallingIdentity(identity);
983                    }
984                }
985            }
986        }
987
988        @Override
989        public void onClearAll(int callingUid, int callingPid, int userId) {
990            synchronized (mNotificationList) {
991                cancelAllLocked(callingUid, callingPid, userId, REASON_DELEGATE_CANCEL_ALL, null,
992                        /*includeCurrentProfiles*/ true);
993            }
994        }
995
996        @Override
997        public void onNotificationClick(int callingUid, int callingPid,
998                String pkg, String tag, int id, int userId) {
999            cancelNotification(callingUid, callingPid, pkg, tag, id, Notification.FLAG_AUTO_CANCEL,
1000                    Notification.FLAG_FOREGROUND_SERVICE, false, userId, REASON_DELEGATE_CLICK, null);
1001        }
1002
1003        @Override
1004        public void onNotificationClear(int callingUid, int callingPid,
1005                String pkg, String tag, int id, int userId) {
1006            cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
1007                    Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
1008                    true, userId, REASON_DELEGATE_CANCEL, null);
1009        }
1010
1011        @Override
1012        public void onPanelRevealed() {
1013            synchronized (mNotificationList) {
1014                // sound
1015                mSoundNotification = null;
1016
1017                long identity = Binder.clearCallingIdentity();
1018                try {
1019                    final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
1020                    if (player != null) {
1021                        player.stopAsync();
1022                    }
1023                } catch (RemoteException e) {
1024                } finally {
1025                    Binder.restoreCallingIdentity(identity);
1026                }
1027
1028                // vibrate
1029                mVibrateNotification = null;
1030                identity = Binder.clearCallingIdentity();
1031                try {
1032                    mVibrator.cancel();
1033                } finally {
1034                    Binder.restoreCallingIdentity(identity);
1035                }
1036
1037                // light
1038                mLights.clear();
1039                mLedNotification = null;
1040                updateLightsLocked();
1041            }
1042        }
1043
1044        @Override
1045        public void onNotificationError(int callingUid, int callingPid, String pkg, String tag, int id,
1046                int uid, int initialPid, String message, int userId) {
1047            Slog.d(TAG, "onNotification error pkg=" + pkg + " tag=" + tag + " id=" + id
1048                    + "; will crashApplication(uid=" + uid + ", pid=" + initialPid + ")");
1049            cancelNotification(callingUid, callingPid, pkg, tag, id, 0, 0, false, userId,
1050                    REASON_DELEGATE_ERROR, null);
1051            long ident = Binder.clearCallingIdentity();
1052            try {
1053                ActivityManagerNative.getDefault().crashApplication(uid, initialPid, pkg,
1054                        "Bad notification posted from package " + pkg
1055                        + ": " + message);
1056            } catch (RemoteException e) {
1057            }
1058            Binder.restoreCallingIdentity(ident);
1059        }
1060
1061        @Override
1062        public boolean allowDisable(int what, IBinder token, String pkg) {
1063            if (isCall(pkg, null)) {
1064                return mZenMode == Settings.Global.ZEN_MODE_OFF;
1065            }
1066            return true;
1067        }
1068    };
1069
1070    private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
1071        @Override
1072        public void onReceive(Context context, Intent intent) {
1073            String action = intent.getAction();
1074
1075            boolean queryRestart = false;
1076            boolean queryRemove = false;
1077            boolean packageChanged = false;
1078            boolean cancelNotifications = true;
1079
1080            if (action.equals(Intent.ACTION_PACKAGE_ADDED)
1081                    || (queryRemove=action.equals(Intent.ACTION_PACKAGE_REMOVED))
1082                    || action.equals(Intent.ACTION_PACKAGE_RESTARTED)
1083                    || (packageChanged=action.equals(Intent.ACTION_PACKAGE_CHANGED))
1084                    || (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART))
1085                    || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
1086                String pkgList[] = null;
1087                boolean queryReplace = queryRemove &&
1088                        intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
1089                if (DBG) Slog.i(TAG, "queryReplace=" + queryReplace);
1090                if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
1091                    pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
1092                } else if (queryRestart) {
1093                    pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
1094                } else {
1095                    Uri uri = intent.getData();
1096                    if (uri == null) {
1097                        return;
1098                    }
1099                    String pkgName = uri.getSchemeSpecificPart();
1100                    if (pkgName == null) {
1101                        return;
1102                    }
1103                    if (packageChanged) {
1104                        // We cancel notifications for packages which have just been disabled
1105                        try {
1106                            final int enabled = getContext().getPackageManager()
1107                                    .getApplicationEnabledSetting(pkgName);
1108                            if (enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED
1109                                    || enabled == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
1110                                cancelNotifications = false;
1111                            }
1112                        } catch (IllegalArgumentException e) {
1113                            // Package doesn't exist; probably racing with uninstall.
1114                            // cancelNotifications is already true, so nothing to do here.
1115                            if (DBG) {
1116                                Slog.i(TAG, "Exception trying to look up app enabled setting", e);
1117                            }
1118                        }
1119                    }
1120                    pkgList = new String[]{pkgName};
1121                }
1122
1123                boolean anyListenersInvolved = false;
1124                if (pkgList != null && (pkgList.length > 0)) {
1125                    for (String pkgName : pkgList) {
1126                        if (cancelNotifications) {
1127                            cancelAllNotificationsInt(MY_UID, MY_PID, pkgName, 0, 0, !queryRestart,
1128                                    UserHandle.USER_ALL, REASON_PACKAGE_CHANGED, null);
1129                        }
1130                        if (mEnabledListenerPackageNames.contains(pkgName)) {
1131                            anyListenersInvolved = true;
1132                        }
1133                    }
1134                }
1135
1136                if (anyListenersInvolved) {
1137                    // if we're not replacing a package, clean up orphaned bits
1138                    if (!queryReplace) {
1139                        disableNonexistentListeners();
1140                    }
1141                    // make sure we're still bound to any of our
1142                    // listeners who may have just upgraded
1143                    rebindListenerServices();
1144                }
1145            } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
1146                // Keep track of screen on/off state, but do not turn off the notification light
1147                // until user passes through the lock screen or views the notification.
1148                mScreenOn = true;
1149            } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
1150                mScreenOn = false;
1151            } else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) {
1152                mInCall = TelephonyManager.EXTRA_STATE_OFFHOOK
1153                        .equals(intent.getStringExtra(TelephonyManager.EXTRA_STATE));
1154                updateNotificationPulse();
1155            } else if (action.equals(Intent.ACTION_USER_STOPPED)) {
1156                int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
1157                if (userHandle >= 0) {
1158                    cancelAllNotificationsInt(MY_UID, MY_PID, null, 0, 0, true, userHandle,
1159                            REASON_USER_STOPPED, null);
1160                }
1161            } else if (action.equals(Intent.ACTION_USER_PRESENT)) {
1162                // turn off LED when user passes through lock screen
1163                mNotificationLight.turnOff();
1164            } else if (action.equals(Intent.ACTION_USER_SWITCHED)) {
1165                // reload per-user settings
1166                mSettingsObserver.update(null);
1167                updateCurrentProfilesCache(context);
1168            } else if (action.equals(Intent.ACTION_USER_ADDED)) {
1169                updateCurrentProfilesCache(context);
1170            }
1171        }
1172    };
1173
1174    class SettingsObserver extends ContentObserver {
1175        private final Uri NOTIFICATION_LIGHT_PULSE_URI
1176                = Settings.System.getUriFor(Settings.System.NOTIFICATION_LIGHT_PULSE);
1177
1178        private final Uri ENABLED_NOTIFICATION_LISTENERS_URI
1179                = Settings.Secure.getUriFor(Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
1180
1181        private final Uri ZEN_MODE
1182                = Settings.Global.getUriFor(Settings.Global.ZEN_MODE);
1183
1184        SettingsObserver(Handler handler) {
1185            super(handler);
1186        }
1187
1188        void observe() {
1189            ContentResolver resolver = getContext().getContentResolver();
1190            resolver.registerContentObserver(NOTIFICATION_LIGHT_PULSE_URI,
1191                    false, this, UserHandle.USER_ALL);
1192            resolver.registerContentObserver(ENABLED_NOTIFICATION_LISTENERS_URI,
1193                    false, this, UserHandle.USER_ALL);
1194            resolver.registerContentObserver(ZEN_MODE,
1195                    false, this);
1196            update(null);
1197        }
1198
1199        @Override public void onChange(boolean selfChange, Uri uri) {
1200            update(uri);
1201        }
1202
1203        public void update(Uri uri) {
1204            ContentResolver resolver = getContext().getContentResolver();
1205            if (uri == null || NOTIFICATION_LIGHT_PULSE_URI.equals(uri)) {
1206                boolean pulseEnabled = Settings.System.getInt(resolver,
1207                            Settings.System.NOTIFICATION_LIGHT_PULSE, 0) != 0;
1208                if (mNotificationPulseEnabled != pulseEnabled) {
1209                    mNotificationPulseEnabled = pulseEnabled;
1210                    updateNotificationPulse();
1211                }
1212            }
1213            if (uri == null || ENABLED_NOTIFICATION_LISTENERS_URI.equals(uri)) {
1214                rebindListenerServices();
1215            }
1216            if (ZEN_MODE.equals(uri)) {
1217                updateZenMode();
1218            }
1219        }
1220    }
1221
1222    private SettingsObserver mSettingsObserver;
1223
1224    static long[] getLongArray(Resources r, int resid, int maxlen, long[] def) {
1225        int[] ar = r.getIntArray(resid);
1226        if (ar == null) {
1227            return def;
1228        }
1229        final int len = ar.length > maxlen ? maxlen : ar.length;
1230        long[] out = new long[len];
1231        for (int i=0; i<len; i++) {
1232            out[i] = ar[i];
1233        }
1234        return out;
1235    }
1236
1237    public NotificationManagerService(Context context) {
1238        super(context);
1239    }
1240
1241    @Override
1242    public void onStart() {
1243        mAm = ActivityManagerNative.getDefault();
1244        mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
1245        mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
1246
1247        mHandler = new WorkerHandler();
1248
1249        importOldBlockDb();
1250
1251        mStatusBar = getLocalService(StatusBarManagerInternal.class);
1252        mStatusBar.setNotificationDelegate(mNotificationDelegate);
1253
1254        final LightsManager lights = getLocalService(LightsManager.class);
1255        mNotificationLight = lights.getLight(LightsManager.LIGHT_ID_NOTIFICATIONS);
1256        mAttentionLight = lights.getLight(LightsManager.LIGHT_ID_ATTENTION);
1257
1258        Resources resources = getContext().getResources();
1259        mDefaultNotificationColor = resources.getColor(
1260                R.color.config_defaultNotificationColor);
1261        mDefaultNotificationLedOn = resources.getInteger(
1262                R.integer.config_defaultNotificationLedOn);
1263        mDefaultNotificationLedOff = resources.getInteger(
1264                R.integer.config_defaultNotificationLedOff);
1265
1266        mDefaultVibrationPattern = getLongArray(resources,
1267                R.array.config_defaultNotificationVibePattern,
1268                VIBRATE_PATTERN_MAXLEN,
1269                DEFAULT_VIBRATE_PATTERN);
1270
1271        mFallbackVibrationPattern = getLongArray(resources,
1272                R.array.config_notificationFallbackVibePattern,
1273                VIBRATE_PATTERN_MAXLEN,
1274                DEFAULT_VIBRATE_PATTERN);
1275
1276        // Don't start allowing notifications until the setup wizard has run once.
1277        // After that, including subsequent boots, init with notifications turned on.
1278        // This works on the first boot because the setup wizard will toggle this
1279        // flag at least once and we'll go back to 0 after that.
1280        if (0 == Settings.Global.getInt(getContext().getContentResolver(),
1281                    Settings.Global.DEVICE_PROVISIONED, 0)) {
1282            mDisableNotificationAlerts = true;
1283        }
1284        updateZenMode();
1285
1286        updateCurrentProfilesCache(getContext());
1287
1288        // register for various Intents
1289        IntentFilter filter = new IntentFilter();
1290        filter.addAction(Intent.ACTION_SCREEN_ON);
1291        filter.addAction(Intent.ACTION_SCREEN_OFF);
1292        filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
1293        filter.addAction(Intent.ACTION_USER_PRESENT);
1294        filter.addAction(Intent.ACTION_USER_STOPPED);
1295        filter.addAction(Intent.ACTION_USER_SWITCHED);
1296        filter.addAction(Intent.ACTION_USER_ADDED);
1297        getContext().registerReceiver(mIntentReceiver, filter);
1298        IntentFilter pkgFilter = new IntentFilter();
1299        pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
1300        pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
1301        pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
1302        pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
1303        pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
1304        pkgFilter.addDataScheme("package");
1305        getContext().registerReceiver(mIntentReceiver, pkgFilter);
1306        IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
1307        getContext().registerReceiver(mIntentReceiver, sdFilter);
1308
1309        mSettingsObserver = new SettingsObserver(mHandler);
1310
1311        // spin up NotificationScorers
1312        String[] notificationScorerNames = resources.getStringArray(
1313                R.array.config_notificationScorers);
1314        for (String scorerName : notificationScorerNames) {
1315            try {
1316                Class<?> scorerClass = getContext().getClassLoader().loadClass(scorerName);
1317                NotificationScorer scorer = (NotificationScorer) scorerClass.newInstance();
1318                scorer.initialize(getContext());
1319                mScorers.add(scorer);
1320            } catch (ClassNotFoundException e) {
1321                Slog.w(TAG, "Couldn't find scorer " + scorerName + ".", e);
1322            } catch (InstantiationException e) {
1323                Slog.w(TAG, "Couldn't instantiate scorer " + scorerName + ".", e);
1324            } catch (IllegalAccessException e) {
1325                Slog.w(TAG, "Problem accessing scorer " + scorerName + ".", e);
1326            }
1327        }
1328
1329        publishBinderService(Context.NOTIFICATION_SERVICE, mService);
1330        publishLocalService(NotificationManagerInternal.class, mInternalService);
1331    }
1332
1333    /**
1334     * Read the old XML-based app block database and import those blockages into the AppOps system.
1335     */
1336    private void importOldBlockDb() {
1337        loadBlockDb();
1338
1339        PackageManager pm = getContext().getPackageManager();
1340        for (String pkg : mBlockedPackages) {
1341            PackageInfo info = null;
1342            try {
1343                info = pm.getPackageInfo(pkg, 0);
1344                setNotificationsEnabledForPackageImpl(pkg, info.applicationInfo.uid, false);
1345            } catch (NameNotFoundException e) {
1346                // forget you
1347            }
1348        }
1349        mBlockedPackages.clear();
1350        if (mPolicyFile != null) {
1351            mPolicyFile.delete();
1352        }
1353    }
1354
1355    @Override
1356    public void onBootPhase(int phase) {
1357        if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
1358            // no beeping until we're basically done booting
1359            mSystemReady = true;
1360
1361            // Grab our optional AudioService
1362            mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
1363
1364        } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
1365            // This observer will force an update when observe is called, causing us to
1366            // bind to listener services.
1367            mSettingsObserver.observe();
1368        }
1369    }
1370
1371    void setNotificationsEnabledForPackageImpl(String pkg, int uid, boolean enabled) {
1372        Slog.v(TAG, (enabled?"en":"dis") + "abling notifications for " + pkg);
1373
1374        mAppOps.setMode(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg,
1375                enabled ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED);
1376
1377        // Now, cancel any outstanding notifications that are part of a just-disabled app
1378        if (ENABLE_BLOCKED_NOTIFICATIONS && !enabled) {
1379            cancelAllNotificationsInt(MY_UID, MY_PID, pkg, 0, 0, true, UserHandle.getUserId(uid),
1380                    REASON_PACKAGE_BANNED, null);
1381        }
1382    }
1383
1384    private final IBinder mService = new INotificationManager.Stub() {
1385        // Toasts
1386        // ============================================================================
1387
1388        @Override
1389        public void enqueueToast(String pkg, ITransientNotification callback, int duration)
1390        {
1391            if (DBG) {
1392                Slog.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback
1393                        + " duration=" + duration);
1394            }
1395
1396            if (pkg == null || callback == null) {
1397                Slog.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback);
1398                return ;
1399            }
1400
1401            final boolean isSystemToast = isCallerSystem() || ("android".equals(pkg));
1402
1403            if (ENABLE_BLOCKED_TOASTS && !noteNotificationOp(pkg, Binder.getCallingUid())) {
1404                if (!isSystemToast) {
1405                    Slog.e(TAG, "Suppressing toast from package " + pkg + " by user request.");
1406                    return;
1407                }
1408            }
1409
1410            synchronized (mToastQueue) {
1411                int callingPid = Binder.getCallingPid();
1412                long callingId = Binder.clearCallingIdentity();
1413                try {
1414                    ToastRecord record;
1415                    int index = indexOfToastLocked(pkg, callback);
1416                    // If it's already in the queue, we update it in place, we don't
1417                    // move it to the end of the queue.
1418                    if (index >= 0) {
1419                        record = mToastQueue.get(index);
1420                        record.update(duration);
1421                    } else {
1422                        // Limit the number of toasts that any given package except the android
1423                        // package can enqueue.  Prevents DOS attacks and deals with leaks.
1424                        if (!isSystemToast) {
1425                            int count = 0;
1426                            final int N = mToastQueue.size();
1427                            for (int i=0; i<N; i++) {
1428                                 final ToastRecord r = mToastQueue.get(i);
1429                                 if (r.pkg.equals(pkg)) {
1430                                     count++;
1431                                     if (count >= MAX_PACKAGE_NOTIFICATIONS) {
1432                                         Slog.e(TAG, "Package has already posted " + count
1433                                                + " toasts. Not showing more. Package=" + pkg);
1434                                         return;
1435                                     }
1436                                 }
1437                            }
1438                        }
1439
1440                        record = new ToastRecord(callingPid, pkg, callback, duration);
1441                        mToastQueue.add(record);
1442                        index = mToastQueue.size() - 1;
1443                        keepProcessAliveLocked(callingPid);
1444                    }
1445                    // If it's at index 0, it's the current toast.  It doesn't matter if it's
1446                    // new or just been updated.  Call back and tell it to show itself.
1447                    // If the callback fails, this will remove it from the list, so don't
1448                    // assume that it's valid after this.
1449                    if (index == 0) {
1450                        showNextToastLocked();
1451                    }
1452                } finally {
1453                    Binder.restoreCallingIdentity(callingId);
1454                }
1455            }
1456        }
1457
1458        @Override
1459        public void cancelToast(String pkg, ITransientNotification callback) {
1460            Slog.i(TAG, "cancelToast pkg=" + pkg + " callback=" + callback);
1461
1462            if (pkg == null || callback == null) {
1463                Slog.e(TAG, "Not cancelling notification. pkg=" + pkg + " callback=" + callback);
1464                return ;
1465            }
1466
1467            synchronized (mToastQueue) {
1468                long callingId = Binder.clearCallingIdentity();
1469                try {
1470                    int index = indexOfToastLocked(pkg, callback);
1471                    if (index >= 0) {
1472                        cancelToastLocked(index);
1473                    } else {
1474                        Slog.w(TAG, "Toast already cancelled. pkg=" + pkg
1475                                + " callback=" + callback);
1476                    }
1477                } finally {
1478                    Binder.restoreCallingIdentity(callingId);
1479                }
1480            }
1481        }
1482
1483        @Override
1484        public void enqueueNotificationWithTag(String pkg, String basePkg, String tag, int id,
1485                Notification notification, int[] idOut, int userId) throws RemoteException {
1486            enqueueNotificationInternal(pkg, basePkg, Binder.getCallingUid(),
1487                    Binder.getCallingPid(), tag, id, notification, idOut, userId);
1488        }
1489
1490        @Override
1491        public void cancelNotificationWithTag(String pkg, String tag, int id, int userId) {
1492            checkCallerIsSystemOrSameApp(pkg);
1493            userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1494                    Binder.getCallingUid(), userId, true, false, "cancelNotificationWithTag", pkg);
1495            // Don't allow client applications to cancel foreground service notis.
1496            cancelNotification(Binder.getCallingUid(), Binder.getCallingPid(), pkg, tag, id, 0,
1497                    Binder.getCallingUid() == Process.SYSTEM_UID
1498                    ? 0 : Notification.FLAG_FOREGROUND_SERVICE, false, userId, REASON_NOMAN_CANCEL,
1499                    null);
1500        }
1501
1502        @Override
1503        public void cancelAllNotifications(String pkg, int userId) {
1504            checkCallerIsSystemOrSameApp(pkg);
1505
1506            userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1507                    Binder.getCallingUid(), userId, true, false, "cancelAllNotifications", pkg);
1508
1509            // Calling from user space, don't allow the canceling of actively
1510            // running foreground services.
1511            cancelAllNotificationsInt(Binder.getCallingUid(), Binder.getCallingPid(),
1512                    pkg, 0, Notification.FLAG_FOREGROUND_SERVICE, true, userId,
1513                    REASON_NOMAN_CANCEL_ALL, null);
1514        }
1515
1516        @Override
1517        public void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) {
1518            checkCallerIsSystem();
1519
1520            setNotificationsEnabledForPackageImpl(pkg, uid, enabled);
1521        }
1522
1523        /**
1524         * Use this when you just want to know if notifications are OK for this package.
1525         */
1526        @Override
1527        public boolean areNotificationsEnabledForPackage(String pkg, int uid) {
1528            checkCallerIsSystem();
1529            return (mAppOps.checkOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
1530                    == AppOpsManager.MODE_ALLOWED);
1531        }
1532
1533        /**
1534         * System-only API for getting a list of current (i.e. not cleared) notifications.
1535         *
1536         * Requires ACCESS_NOTIFICATIONS which is signature|system.
1537         */
1538        @Override
1539        public StatusBarNotification[] getActiveNotifications(String callingPkg) {
1540            // enforce() will ensure the calling uid has the correct permission
1541            getContext().enforceCallingOrSelfPermission(
1542                    android.Manifest.permission.ACCESS_NOTIFICATIONS,
1543                    "NotificationManagerService.getActiveNotifications");
1544
1545            StatusBarNotification[] tmp = null;
1546            int uid = Binder.getCallingUid();
1547
1548            // noteOp will check to make sure the callingPkg matches the uid
1549            if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
1550                    == AppOpsManager.MODE_ALLOWED) {
1551                synchronized (mNotificationList) {
1552                    tmp = new StatusBarNotification[mNotificationList.size()];
1553                    final int N = mNotificationList.size();
1554                    for (int i=0; i<N; i++) {
1555                        tmp[i] = mNotificationList.get(i).sbn;
1556                    }
1557                }
1558            }
1559            return tmp;
1560        }
1561
1562        /**
1563         * System-only API for getting a list of recent (cleared, no longer shown) notifications.
1564         *
1565         * Requires ACCESS_NOTIFICATIONS which is signature|system.
1566         */
1567        @Override
1568        public StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count) {
1569            // enforce() will ensure the calling uid has the correct permission
1570            getContext().enforceCallingOrSelfPermission(
1571                    android.Manifest.permission.ACCESS_NOTIFICATIONS,
1572                    "NotificationManagerService.getHistoricalNotifications");
1573
1574            StatusBarNotification[] tmp = null;
1575            int uid = Binder.getCallingUid();
1576
1577            // noteOp will check to make sure the callingPkg matches the uid
1578            if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
1579                    == AppOpsManager.MODE_ALLOWED) {
1580                synchronized (mArchive) {
1581                    tmp = mArchive.getArray(count);
1582                }
1583            }
1584            return tmp;
1585        }
1586
1587        /**
1588         * Register a listener binder directly with the notification manager.
1589         *
1590         * Only works with system callers. Apps should extend
1591         * {@link android.service.notification.NotificationListenerService}.
1592         */
1593        @Override
1594        public void registerListener(final INotificationListener listener,
1595                final ComponentName component, final int userid) {
1596            checkCallerIsSystem();
1597            checkNullListener(listener);
1598            registerListenerImpl(listener, component, userid);
1599        }
1600
1601        /**
1602         * Remove a listener binder directly
1603         */
1604        @Override
1605        public void unregisterListener(INotificationListener listener, int userid) {
1606            checkNullListener(listener);
1607            // no need to check permissions; if your listener binder is in the list,
1608            // that's proof that you had permission to add it in the first place
1609            unregisterListenerImpl(listener, userid);
1610        }
1611
1612        /**
1613         * Allow an INotificationListener to simulate a "clear all" operation.
1614         *
1615         * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onClearAllNotifications}
1616         *
1617         * @param token The binder for the listener, to check that the caller is allowed
1618         */
1619        @Override
1620        public void cancelNotificationsFromListener(INotificationListener token, String[] keys) {
1621            final int callingUid = Binder.getCallingUid();
1622            final int callingPid = Binder.getCallingPid();
1623            long identity = Binder.clearCallingIdentity();
1624            try {
1625                synchronized (mNotificationList) {
1626                    final NotificationListenerInfo info = checkListenerTokenLocked(token);
1627                    if (keys != null) {
1628                        final int N = keys.length;
1629                        for (int i = 0; i < N; i++) {
1630                            NotificationRecord r = mNotificationsByKey.get(keys[i]);
1631                            final int userId = r.sbn.getUserId();
1632                            if (userId != info.userid && userId != UserHandle.USER_ALL &&
1633                                    !isCurrentProfile(userId)) {
1634                                throw new SecurityException("Disallowed call from listener: "
1635                                        + info.listener);
1636                            }
1637                            if (r != null) {
1638                                cancelNotificationFromListenerLocked(info, callingUid, callingPid,
1639                                        r.sbn.getPackageName(), r.sbn.getTag(), r.sbn.getId(),
1640                                        userId);
1641                            }
1642                        }
1643                    } else {
1644                        cancelAllLocked(callingUid, callingPid, info.userid,
1645                                REASON_LISTENER_CANCEL_ALL, info, info.supportsProfiles());
1646                    }
1647                }
1648            } finally {
1649                Binder.restoreCallingIdentity(identity);
1650            }
1651        }
1652
1653        private void cancelNotificationFromListenerLocked(NotificationListenerInfo info,
1654                int callingUid, int callingPid, String pkg, String tag, int id, int userId) {
1655            cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
1656                    Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
1657                    true,
1658                    userId, REASON_LISTENER_CANCEL, info);
1659        }
1660
1661        /**
1662         * Allow an INotificationListener to simulate clearing (dismissing) a single notification.
1663         *
1664         * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onNotificationClear}
1665         *
1666         * @param token The binder for the listener, to check that the caller is allowed
1667         */
1668        @Override
1669        public void cancelNotificationFromListener(INotificationListener token, String pkg,
1670                String tag, int id) {
1671            final int callingUid = Binder.getCallingUid();
1672            final int callingPid = Binder.getCallingPid();
1673            long identity = Binder.clearCallingIdentity();
1674            try {
1675                synchronized (mNotificationList) {
1676                    final NotificationListenerInfo info = checkListenerTokenLocked(token);
1677                    if (info.supportsProfiles()) {
1678                        Log.e(TAG, "Ignoring deprecated cancelNotification(pkg, tag, id) "
1679                                + "from " + info.component
1680                                + " use cancelNotification(key) instead.");
1681                    } else {
1682                        cancelNotificationFromListenerLocked(info, callingUid, callingPid,
1683                                pkg, tag, id, info.userid);
1684                    }
1685                }
1686            } finally {
1687                Binder.restoreCallingIdentity(identity);
1688            }
1689        }
1690
1691        /**
1692         * Allow an INotificationListener to request the list of outstanding notifications seen by
1693         * the current user. Useful when starting up, after which point the listener callbacks
1694         * should be used.
1695         *
1696         * @param token The binder for the listener, to check that the caller is allowed
1697         */
1698        @Override
1699        public StatusBarNotification[] getActiveNotificationsFromListener(
1700                INotificationListener token, String[] keys) {
1701            synchronized (mNotificationList) {
1702                final NotificationListenerInfo info = checkListenerTokenLocked(token);
1703                final ArrayList<StatusBarNotification> list
1704                        = new ArrayList<StatusBarNotification>();
1705                if (keys == null) {
1706                    final int N = mNotificationList.size();
1707                    for (int i=0; i<N; i++) {
1708                        StatusBarNotification sbn = mNotificationList.get(i).sbn;
1709                        if (info.enabledAndUserMatches(sbn)) {
1710                            list.add(sbn);
1711                        }
1712                    }
1713                } else {
1714                    final int N = keys.length;
1715                    for (int i=0; i<N; i++) {
1716                        NotificationRecord r = mNotificationsByKey.get(keys[i]);
1717                        if (r != null && info.enabledAndUserMatches(r.sbn)) {
1718                            list.add(r.sbn);
1719                        }
1720                    }
1721                }
1722                return list.toArray(new StatusBarNotification[list.size()]);
1723            }
1724        }
1725
1726        @Override
1727        public String[] getActiveNotificationKeysFromListener(INotificationListener token) {
1728            return NotificationManagerService.this.getActiveNotificationKeysFromListener(token);
1729        }
1730
1731        @Override
1732        protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1733            if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
1734                    != PackageManager.PERMISSION_GRANTED) {
1735                pw.println("Permission Denial: can't dump NotificationManager from from pid="
1736                        + Binder.getCallingPid()
1737                        + ", uid=" + Binder.getCallingUid());
1738                return;
1739            }
1740
1741            dumpImpl(pw);
1742        }
1743    };
1744
1745    private String[] getActiveNotificationKeysFromListener(INotificationListener token) {
1746        synchronized (mNotificationList) {
1747            final NotificationListenerInfo info = checkListenerTokenLocked(token);
1748            final ArrayList<String> keys = new ArrayList<String>();
1749            final int N = mNotificationList.size();
1750            for (int i=0; i<N; i++) {
1751                final StatusBarNotification sbn = mNotificationList.get(i).sbn;
1752                if (info.enabledAndUserMatches(sbn)) {
1753                    keys.add(sbn.getKey());
1754                }
1755            }
1756            return keys.toArray(new String[keys.size()]);
1757        }
1758    }
1759
1760    void dumpImpl(PrintWriter pw) {
1761        pw.println("Current Notification Manager state:");
1762
1763        pw.println("  Listeners (" + mEnabledListenersForCurrentProfiles.size()
1764                + ") enabled for current profiles:");
1765        for (ComponentName cmpt : mEnabledListenersForCurrentProfiles) {
1766            pw.println("    " + cmpt);
1767        }
1768
1769        pw.println("  Live listeners (" + mListeners.size() + "):");
1770        for (NotificationListenerInfo info : mListeners) {
1771            pw.println("    " + info.component
1772                    + " (user " + info.userid + "): " + info.listener
1773                    + (info.isSystem?" SYSTEM":""));
1774        }
1775
1776        int N;
1777
1778        synchronized (mToastQueue) {
1779            N = mToastQueue.size();
1780            if (N > 0) {
1781                pw.println("  Toast Queue:");
1782                for (int i=0; i<N; i++) {
1783                    mToastQueue.get(i).dump(pw, "    ");
1784                }
1785                pw.println("  ");
1786            }
1787
1788        }
1789
1790        synchronized (mNotificationList) {
1791            N = mNotificationList.size();
1792            if (N > 0) {
1793                pw.println("  Notification List:");
1794                for (int i=0; i<N; i++) {
1795                    mNotificationList.get(i).dump(pw, "    ", getContext());
1796                }
1797                pw.println("  ");
1798            }
1799
1800            N = mLights.size();
1801            if (N > 0) {
1802                pw.println("  Lights List:");
1803                for (int i=0; i<N; i++) {
1804                    pw.println("    " + mLights.get(i));
1805                }
1806                pw.println("  ");
1807            }
1808
1809            pw.println("  mSoundNotification=" + mSoundNotification);
1810            pw.println("  mVibrateNotification=" + mVibrateNotification);
1811            pw.println("  mDisableNotificationAlerts=" + mDisableNotificationAlerts);
1812            pw.println("  mZenMode=" + Settings.Global.zenModeToString(mZenMode));
1813            pw.println("  mSystemReady=" + mSystemReady);
1814            pw.println("  mArchive=" + mArchive.toString());
1815            Iterator<StatusBarNotification> iter = mArchive.descendingIterator();
1816            int i=0;
1817            while (iter.hasNext()) {
1818                pw.println("    " + iter.next());
1819                if (++i >= 5) {
1820                    if (iter.hasNext()) pw.println("    ...");
1821                    break;
1822                }
1823            }
1824
1825            pw.println("\n  Usage Stats:");
1826            mUsageStats.dump(pw, "    ");
1827
1828        }
1829    }
1830
1831    /**
1832     * The private API only accessible to the system process.
1833     */
1834    private final NotificationManagerInternal mInternalService = new NotificationManagerInternal() {
1835        @Override
1836        public void enqueueNotification(String pkg, String basePkg, int callingUid, int callingPid,
1837                String tag, int id, Notification notification, int[] idReceived, int userId) {
1838            enqueueNotificationInternal(pkg, basePkg, callingUid, callingPid, tag, id, notification,
1839                    idReceived, userId);
1840        }
1841    };
1842
1843    void enqueueNotificationInternal(final String pkg, String basePkg, final int callingUid,
1844            final int callingPid, final String tag, final int id, final Notification notification,
1845            int[] idOut, int incomingUserId) {
1846        if (DBG) {
1847            Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id
1848                    + " notification=" + notification);
1849        }
1850        checkCallerIsSystemOrSameApp(pkg);
1851        final boolean isSystemNotification = isUidSystem(callingUid) || ("android".equals(pkg));
1852
1853        final int userId = ActivityManager.handleIncomingUser(callingPid,
1854                callingUid, incomingUserId, true, false, "enqueueNotification", pkg);
1855        final UserHandle user = new UserHandle(userId);
1856
1857        // Limit the number of notifications that any given package except the android
1858        // package can enqueue.  Prevents DOS attacks and deals with leaks.
1859        if (!isSystemNotification) {
1860            synchronized (mNotificationList) {
1861                int count = 0;
1862                final int N = mNotificationList.size();
1863                for (int i=0; i<N; i++) {
1864                    final NotificationRecord r = mNotificationList.get(i);
1865                    if (r.sbn.getPackageName().equals(pkg) && r.sbn.getUserId() == userId) {
1866                        count++;
1867                        if (count >= MAX_PACKAGE_NOTIFICATIONS) {
1868                            Slog.e(TAG, "Package has already posted " + count
1869                                    + " notifications.  Not showing more.  package=" + pkg);
1870                            return;
1871                        }
1872                    }
1873                }
1874            }
1875        }
1876
1877        // This conditional is a dirty hack to limit the logging done on
1878        //     behalf of the download manager without affecting other apps.
1879        if (!pkg.equals("com.android.providers.downloads")
1880                || Log.isLoggable("DownloadManager", Log.VERBOSE)) {
1881            EventLogTags.writeNotificationEnqueue(callingUid, callingPid,
1882                    pkg, id, tag, userId, notification.toString());
1883        }
1884
1885        if (pkg == null || notification == null) {
1886            throw new IllegalArgumentException("null not allowed: pkg=" + pkg
1887                    + " id=" + id + " notification=" + notification);
1888        }
1889        if (notification.icon != 0) {
1890            if (notification.contentView == null) {
1891                throw new IllegalArgumentException("contentView required: pkg=" + pkg
1892                        + " id=" + id + " notification=" + notification);
1893            }
1894        }
1895
1896        mHandler.post(new Runnable() {
1897            @Override
1898            public void run() {
1899
1900                // === Scoring ===
1901
1902                // 0. Sanitize inputs
1903                notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN,
1904                        Notification.PRIORITY_MAX);
1905                // Migrate notification flags to scores
1906                if (0 != (notification.flags & Notification.FLAG_HIGH_PRIORITY)) {
1907                    if (notification.priority < Notification.PRIORITY_MAX) {
1908                        notification.priority = Notification.PRIORITY_MAX;
1909                    }
1910                } else if (SCORE_ONGOING_HIGHER &&
1911                        0 != (notification.flags & Notification.FLAG_ONGOING_EVENT)) {
1912                    if (notification.priority < Notification.PRIORITY_HIGH) {
1913                        notification.priority = Notification.PRIORITY_HIGH;
1914                    }
1915                }
1916
1917                // 1. initial score: buckets of 10, around the app
1918                int score = notification.priority * NOTIFICATION_PRIORITY_MULTIPLIER; //[-20..20]
1919
1920                // 2. Consult external heuristics (TBD)
1921
1922                // 3. Apply local rules
1923
1924                int initialScore = score;
1925                if (!mScorers.isEmpty()) {
1926                    if (DBG) Slog.v(TAG, "Initial score is " + score + ".");
1927                    for (NotificationScorer scorer : mScorers) {
1928                        try {
1929                            score = scorer.getScore(notification, score);
1930                        } catch (Throwable t) {
1931                            Slog.w(TAG, "Scorer threw on .getScore.", t);
1932                        }
1933                    }
1934                    if (DBG) Slog.v(TAG, "Final score is " + score + ".");
1935                }
1936
1937                // add extra to indicate score modified by NotificationScorer
1938                notification.extras.putBoolean(Notification.EXTRA_SCORE_MODIFIED,
1939                        score != initialScore);
1940
1941                // blocked apps
1942                if (ENABLE_BLOCKED_NOTIFICATIONS && !noteNotificationOp(pkg, callingUid)) {
1943                    if (!isSystemNotification) {
1944                        score = JUNK_SCORE;
1945                        Slog.e(TAG, "Suppressing notification from package " + pkg
1946                                + " by user request.");
1947                    }
1948                }
1949
1950                if (DBG) {
1951                    Slog.v(TAG, "Assigned score=" + score + " to " + notification);
1952                }
1953
1954                if (score < SCORE_DISPLAY_THRESHOLD) {
1955                    // Notification will be blocked because the score is too low.
1956                    return;
1957                }
1958
1959                // Is this notification intercepted by zen mode?
1960                final boolean intercept = shouldIntercept(pkg, notification);
1961                notification.extras.putBoolean(EXTRA_INTERCEPT, intercept);
1962
1963                // Should this notification make noise, vibe, or use the LED?
1964                final boolean canInterrupt = (score >= SCORE_INTERRUPTION_THRESHOLD) && !intercept;
1965                if (DBG) Slog.v(TAG, "canInterrupt=" + canInterrupt + " intercept=" + intercept);
1966                synchronized (mNotificationList) {
1967                    final StatusBarNotification n = new StatusBarNotification(
1968                            pkg, id, tag, callingUid, callingPid, score, notification, user);
1969                    NotificationRecord r = new NotificationRecord(n);
1970                    NotificationRecord old = null;
1971
1972                    int index = indexOfNotificationLocked(pkg, tag, id, userId);
1973                    if (index < 0) {
1974                        mNotificationList.add(r);
1975                        mUsageStats.registerPostedByApp(r);
1976                    } else {
1977                        old = mNotificationList.remove(index);
1978                        mNotificationList.add(index, r);
1979                        mUsageStats.registerUpdatedByApp(r);
1980                        // Make sure we don't lose the foreground service state.
1981                        if (old != null) {
1982                            notification.flags |=
1983                                old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;
1984                        }
1985                    }
1986                    if (old != null) {
1987                        mNotificationsByKey.remove(old.sbn.getKey());
1988                    }
1989                    mNotificationsByKey.put(n.getKey(), r);
1990
1991                    // Ensure if this is a foreground service that the proper additional
1992                    // flags are set.
1993                    if ((notification.flags&Notification.FLAG_FOREGROUND_SERVICE) != 0) {
1994                        notification.flags |= Notification.FLAG_ONGOING_EVENT
1995                                | Notification.FLAG_NO_CLEAR;
1996                    }
1997
1998                    final int currentUser;
1999                    final long token = Binder.clearCallingIdentity();
2000                    try {
2001                        currentUser = ActivityManager.getCurrentUser();
2002                    } finally {
2003                        Binder.restoreCallingIdentity(token);
2004                    }
2005
2006                    if (notification.icon != 0) {
2007                        if (old != null && old.statusBarKey != null) {
2008                            r.statusBarKey = old.statusBarKey;
2009                            final long identity = Binder.clearCallingIdentity();
2010                            try {
2011                                mStatusBar.updateNotification(r.statusBarKey, n);
2012                            } finally {
2013                                Binder.restoreCallingIdentity(identity);
2014                            }
2015                        } else {
2016                            final long identity = Binder.clearCallingIdentity();
2017                            try {
2018                                r.statusBarKey = mStatusBar.addNotification(n);
2019                                if ((n.getNotification().flags & Notification.FLAG_SHOW_LIGHTS) != 0
2020                                        && canInterrupt) {
2021                                    mAttentionLight.pulse();
2022                                }
2023                            } finally {
2024                                Binder.restoreCallingIdentity(identity);
2025                            }
2026                        }
2027                        // Send accessibility events only for the current user.
2028                        if (currentUser == userId) {
2029                            sendAccessibilityEvent(notification, pkg);
2030                        }
2031
2032                        notifyPostedLocked(r);
2033                    } else {
2034                        Slog.e(TAG, "Not posting notification with icon==0: " + notification);
2035                        if (old != null && old.statusBarKey != null) {
2036                            final long identity = Binder.clearCallingIdentity();
2037                            try {
2038                                mStatusBar.removeNotification(old.statusBarKey);
2039                            } finally {
2040                                Binder.restoreCallingIdentity(identity);
2041                            }
2042
2043                            notifyRemovedLocked(r);
2044                        }
2045                        // ATTENTION: in a future release we will bail out here
2046                        // so that we do not play sounds, show lights, etc. for invalid
2047                        // notifications
2048                        Slog.e(TAG, "WARNING: In a future release this will crash the app: "
2049                                + n.getPackageName());
2050                    }
2051
2052                    // If we're not supposed to beep, vibrate, etc. then don't.
2053                    if (!mDisableNotificationAlerts
2054                            && (!(old != null
2055                                && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 ))
2056                            && (r.getUserId() == UserHandle.USER_ALL ||
2057                                (r.getUserId() == userId && r.getUserId() == currentUser) ||
2058                                isCurrentProfile(r.getUserId()))
2059                            && canInterrupt
2060                            && mSystemReady
2061                            && mAudioManager != null) {
2062                        if (DBG) Slog.v(TAG, "Interrupting!");
2063                        // sound
2064
2065                        // should we use the default notification sound? (indicated either by
2066                        // DEFAULT_SOUND or because notification.sound is pointing at
2067                        // Settings.System.NOTIFICATION_SOUND)
2068                        final boolean useDefaultSound =
2069                               (notification.defaults & Notification.DEFAULT_SOUND) != 0 ||
2070                                       Settings.System.DEFAULT_NOTIFICATION_URI
2071                                               .equals(notification.sound);
2072
2073                        Uri soundUri = null;
2074                        boolean hasValidSound = false;
2075
2076                        if (useDefaultSound) {
2077                            soundUri = Settings.System.DEFAULT_NOTIFICATION_URI;
2078
2079                            // check to see if the default notification sound is silent
2080                            ContentResolver resolver = getContext().getContentResolver();
2081                            hasValidSound = Settings.System.getString(resolver,
2082                                   Settings.System.NOTIFICATION_SOUND) != null;
2083                        } else if (notification.sound != null) {
2084                            soundUri = notification.sound;
2085                            hasValidSound = (soundUri != null);
2086                        }
2087
2088                        if (hasValidSound) {
2089                            boolean looping =
2090                                    (notification.flags & Notification.FLAG_INSISTENT) != 0;
2091                            int audioStreamType;
2092                            if (notification.audioStreamType >= 0) {
2093                                audioStreamType = notification.audioStreamType;
2094                            } else {
2095                                audioStreamType = DEFAULT_STREAM_TYPE;
2096                            }
2097                            mSoundNotification = r;
2098                            // do not play notifications if stream volume is 0 (typically because
2099                            // ringer mode is silent) or if there is a user of exclusive audio focus
2100                            if ((mAudioManager.getStreamVolume(audioStreamType) != 0)
2101                                    && !mAudioManager.isAudioFocusExclusive()) {
2102                                final long identity = Binder.clearCallingIdentity();
2103                                try {
2104                                    final IRingtonePlayer player =
2105                                            mAudioManager.getRingtonePlayer();
2106                                    if (player != null) {
2107                                        if (DBG) Slog.v(TAG, "Playing sound " + soundUri
2108                                                + " on stream " + audioStreamType);
2109                                        player.playAsync(soundUri, user, looping, audioStreamType);
2110                                    }
2111                                } catch (RemoteException e) {
2112                                } finally {
2113                                    Binder.restoreCallingIdentity(identity);
2114                                }
2115                            }
2116                        }
2117
2118                        // vibrate
2119                        // Does the notification want to specify its own vibration?
2120                        final boolean hasCustomVibrate = notification.vibrate != null;
2121
2122                        // new in 4.2: if there was supposed to be a sound and we're in vibrate
2123                        // mode, and no other vibration is specified, we fall back to vibration
2124                        final boolean convertSoundToVibration =
2125                                   !hasCustomVibrate
2126                                && hasValidSound
2127                                && (mAudioManager.getRingerMode()
2128                                           == AudioManager.RINGER_MODE_VIBRATE);
2129
2130                        // The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback.
2131                        final boolean useDefaultVibrate =
2132                                (notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
2133
2134                        if ((useDefaultVibrate || convertSoundToVibration || hasCustomVibrate)
2135                                && !(mAudioManager.getRingerMode()
2136                                        == AudioManager.RINGER_MODE_SILENT)) {
2137                            mVibrateNotification = r;
2138
2139                            if (useDefaultVibrate || convertSoundToVibration) {
2140                                // Escalate privileges so we can use the vibrator even if the
2141                                // notifying app does not have the VIBRATE permission.
2142                                long identity = Binder.clearCallingIdentity();
2143                                try {
2144                                    mVibrator.vibrate(r.sbn.getUid(), r.sbn.getBasePkg(),
2145                                        useDefaultVibrate ? mDefaultVibrationPattern
2146                                            : mFallbackVibrationPattern,
2147                                        ((notification.flags & Notification.FLAG_INSISTENT) != 0)
2148                                                ? 0: -1, notification.audioStreamType);
2149                                } finally {
2150                                    Binder.restoreCallingIdentity(identity);
2151                                }
2152                            } else if (notification.vibrate.length > 1) {
2153                                // If you want your own vibration pattern, you need the VIBRATE
2154                                // permission
2155                                mVibrator.vibrate(r.sbn.getUid(), r.sbn.getBasePkg(),
2156                                        notification.vibrate,
2157                                    ((notification.flags & Notification.FLAG_INSISTENT) != 0)
2158                                            ? 0: -1, notification.audioStreamType);
2159                            }
2160                        }
2161                    }
2162
2163                    // light
2164                    // the most recent thing gets the light
2165                    mLights.remove(old);
2166                    if (mLedNotification == old) {
2167                        mLedNotification = null;
2168                    }
2169                    //Slog.i(TAG, "notification.lights="
2170                    //        + ((old.notification.lights.flags & Notification.FLAG_SHOW_LIGHTS)
2171                    //                  != 0));
2172                    if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0
2173                            && canInterrupt) {
2174                        mLights.add(r);
2175                        updateLightsLocked();
2176                    } else {
2177                        if (old != null
2178                                && ((old.getFlags() & Notification.FLAG_SHOW_LIGHTS) != 0)) {
2179                            updateLightsLocked();
2180                        }
2181                    }
2182                }
2183            }
2184        });
2185
2186        idOut[0] = id;
2187    }
2188
2189     void registerListenerImpl(final INotificationListener listener,
2190            final ComponentName component, final int userid) {
2191        synchronized (mNotificationList) {
2192            try {
2193                NotificationListenerInfo info
2194                        = new NotificationListenerInfo(listener, component, userid,
2195                        /*isSystem*/ true, Build.VERSION_CODES.L);
2196                listener.asBinder().linkToDeath(info, 0);
2197                mListeners.add(info);
2198            } catch (RemoteException e) {
2199                // already dead
2200            }
2201        }
2202    }
2203
2204    /**
2205     * Removes a listener from the list and unbinds from its service.
2206     */
2207    void unregisterListenerImpl(final INotificationListener listener, final int userid) {
2208        NotificationListenerInfo info = removeListenerImpl(listener, userid);
2209        if (info != null && info.connection != null) {
2210            getContext().unbindService(info.connection);
2211        }
2212    }
2213
2214    /**
2215     * Removes a listener from the list but does not unbind from the listener's service.
2216     *
2217     * @return the removed listener.
2218     */
2219    NotificationListenerInfo removeListenerImpl(
2220            final INotificationListener listener, final int userid) {
2221        NotificationListenerInfo listenerInfo = null;
2222        synchronized (mNotificationList) {
2223            final int N = mListeners.size();
2224            for (int i=N-1; i>=0; i--) {
2225                final NotificationListenerInfo info = mListeners.get(i);
2226                if (info.listener.asBinder() == listener.asBinder()
2227                        && info.userid == userid) {
2228                    listenerInfo = mListeners.remove(i);
2229                }
2230            }
2231        }
2232        return listenerInfo;
2233    }
2234
2235    void showNextToastLocked() {
2236        ToastRecord record = mToastQueue.get(0);
2237        while (record != null) {
2238            if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback);
2239            try {
2240                record.callback.show();
2241                scheduleTimeoutLocked(record);
2242                return;
2243            } catch (RemoteException e) {
2244                Slog.w(TAG, "Object died trying to show notification " + record.callback
2245                        + " in package " + record.pkg);
2246                // remove it from the list and let the process die
2247                int index = mToastQueue.indexOf(record);
2248                if (index >= 0) {
2249                    mToastQueue.remove(index);
2250                }
2251                keepProcessAliveLocked(record.pid);
2252                if (mToastQueue.size() > 0) {
2253                    record = mToastQueue.get(0);
2254                } else {
2255                    record = null;
2256                }
2257            }
2258        }
2259    }
2260
2261    void cancelToastLocked(int index) {
2262        ToastRecord record = mToastQueue.get(index);
2263        try {
2264            record.callback.hide();
2265        } catch (RemoteException e) {
2266            Slog.w(TAG, "Object died trying to hide notification " + record.callback
2267                    + " in package " + record.pkg);
2268            // don't worry about this, we're about to remove it from
2269            // the list anyway
2270        }
2271        mToastQueue.remove(index);
2272        keepProcessAliveLocked(record.pid);
2273        if (mToastQueue.size() > 0) {
2274            // Show the next one. If the callback fails, this will remove
2275            // it from the list, so don't assume that the list hasn't changed
2276            // after this point.
2277            showNextToastLocked();
2278        }
2279    }
2280
2281    private void scheduleTimeoutLocked(ToastRecord r)
2282    {
2283        mHandler.removeCallbacksAndMessages(r);
2284        Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r);
2285        long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY;
2286        mHandler.sendMessageDelayed(m, delay);
2287    }
2288
2289    private void handleTimeout(ToastRecord record)
2290    {
2291        if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback);
2292        synchronized (mToastQueue) {
2293            int index = indexOfToastLocked(record.pkg, record.callback);
2294            if (index >= 0) {
2295                cancelToastLocked(index);
2296            }
2297        }
2298    }
2299
2300    // lock on mToastQueue
2301    int indexOfToastLocked(String pkg, ITransientNotification callback)
2302    {
2303        IBinder cbak = callback.asBinder();
2304        ArrayList<ToastRecord> list = mToastQueue;
2305        int len = list.size();
2306        for (int i=0; i<len; i++) {
2307            ToastRecord r = list.get(i);
2308            if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) {
2309                return i;
2310            }
2311        }
2312        return -1;
2313    }
2314
2315    // lock on mToastQueue
2316    void keepProcessAliveLocked(int pid)
2317    {
2318        int toastCount = 0; // toasts from this pid
2319        ArrayList<ToastRecord> list = mToastQueue;
2320        int N = list.size();
2321        for (int i=0; i<N; i++) {
2322            ToastRecord r = list.get(i);
2323            if (r.pid == pid) {
2324                toastCount++;
2325            }
2326        }
2327        try {
2328            mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0);
2329        } catch (RemoteException e) {
2330            // Shouldn't happen.
2331        }
2332    }
2333
2334    private final class WorkerHandler extends Handler
2335    {
2336        @Override
2337        public void handleMessage(Message msg)
2338        {
2339            switch (msg.what)
2340            {
2341                case MESSAGE_TIMEOUT:
2342                    handleTimeout((ToastRecord)msg.obj);
2343                    break;
2344            }
2345        }
2346    }
2347
2348
2349    // Notifications
2350    // ============================================================================
2351    static int clamp(int x, int low, int high) {
2352        return (x < low) ? low : ((x > high) ? high : x);
2353    }
2354
2355    void sendAccessibilityEvent(Notification notification, CharSequence packageName) {
2356        AccessibilityManager manager = AccessibilityManager.getInstance(getContext());
2357        if (!manager.isEnabled()) {
2358            return;
2359        }
2360
2361        AccessibilityEvent event =
2362            AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
2363        event.setPackageName(packageName);
2364        event.setClassName(Notification.class.getName());
2365        event.setParcelableData(notification);
2366        CharSequence tickerText = notification.tickerText;
2367        if (!TextUtils.isEmpty(tickerText)) {
2368            event.getText().add(tickerText);
2369        }
2370
2371        manager.sendAccessibilityEvent(event);
2372    }
2373
2374    private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason) {
2375        // tell the app
2376        if (sendDelete) {
2377            if (r.getNotification().deleteIntent != null) {
2378                try {
2379                    r.getNotification().deleteIntent.send();
2380                } catch (PendingIntent.CanceledException ex) {
2381                    // do nothing - there's no relevant way to recover, and
2382                    //     no reason to let this propagate
2383                    Slog.w(TAG, "canceled PendingIntent for " + r.sbn.getPackageName(), ex);
2384                }
2385            }
2386        }
2387
2388        // status bar
2389        if (r.getNotification().icon != 0) {
2390            final long identity = Binder.clearCallingIdentity();
2391            try {
2392                mStatusBar.removeNotification(r.statusBarKey);
2393            } finally {
2394                Binder.restoreCallingIdentity(identity);
2395            }
2396            r.statusBarKey = null;
2397            notifyRemovedLocked(r);
2398        }
2399
2400        // sound
2401        if (mSoundNotification == r) {
2402            mSoundNotification = null;
2403            final long identity = Binder.clearCallingIdentity();
2404            try {
2405                final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
2406                if (player != null) {
2407                    player.stopAsync();
2408                }
2409            } catch (RemoteException e) {
2410            } finally {
2411                Binder.restoreCallingIdentity(identity);
2412            }
2413        }
2414
2415        // vibrate
2416        if (mVibrateNotification == r) {
2417            mVibrateNotification = null;
2418            long identity = Binder.clearCallingIdentity();
2419            try {
2420                mVibrator.cancel();
2421            }
2422            finally {
2423                Binder.restoreCallingIdentity(identity);
2424            }
2425        }
2426
2427        // light
2428        mLights.remove(r);
2429        if (mLedNotification == r) {
2430            mLedNotification = null;
2431        }
2432
2433        // Record usage stats
2434        switch (reason) {
2435            case REASON_DELEGATE_CANCEL:
2436            case REASON_DELEGATE_CANCEL_ALL:
2437            case REASON_LISTENER_CANCEL:
2438            case REASON_LISTENER_CANCEL_ALL:
2439                mUsageStats.registerDismissedByUser(r);
2440                break;
2441            case REASON_NOMAN_CANCEL:
2442            case REASON_NOMAN_CANCEL_ALL:
2443                mUsageStats.registerRemovedByApp(r);
2444                break;
2445            case REASON_DELEGATE_CLICK:
2446                mUsageStats.registerCancelDueToClick(r);
2447                break;
2448            default:
2449                mUsageStats.registerCancelUnknown(r);
2450                break;
2451        }
2452
2453        // Save it for users of getHistoricalNotifications()
2454        mArchive.record(r.sbn);
2455    }
2456
2457    /**
2458     * Cancels a notification ONLY if it has all of the {@code mustHaveFlags}
2459     * and none of the {@code mustNotHaveFlags}.
2460     */
2461    void cancelNotification(final int callingUid, final int callingPid,
2462            final String pkg, final String tag, final int id,
2463            final int mustHaveFlags, final int mustNotHaveFlags, final boolean sendDelete,
2464            final int userId, final int reason, final NotificationListenerInfo listener) {
2465        // In enqueueNotificationInternal notifications are added by scheduling the
2466        // work on the worker handler. Hence, we also schedule the cancel on this
2467        // handler to avoid a scenario where an add notification call followed by a
2468        // remove notification call ends up in not removing the notification.
2469        mHandler.post(new Runnable() {
2470            @Override
2471            public void run() {
2472                EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, id, tag, userId,
2473                        mustHaveFlags, mustNotHaveFlags, reason,
2474                        listener == null ? null : listener.component.toShortString());
2475
2476                synchronized (mNotificationList) {
2477                    int index = indexOfNotificationLocked(pkg, tag, id, userId);
2478                    if (index >= 0) {
2479                        NotificationRecord r = mNotificationList.get(index);
2480
2481                        // Ideally we'd do this in the caller of this method. However, that would
2482                        // require the caller to also find the notification.
2483                        if (reason == REASON_DELEGATE_CLICK) {
2484                            mUsageStats.registerClickedByUser(r);
2485                        }
2486
2487                        if ((r.getNotification().flags & mustHaveFlags) != mustHaveFlags) {
2488                            return;
2489                        }
2490                        if ((r.getNotification().flags & mustNotHaveFlags) != 0) {
2491                            return;
2492                        }
2493
2494                        mNotificationList.remove(index);
2495                        mNotificationsByKey.remove(r.sbn.getKey());
2496
2497                        cancelNotificationLocked(r, sendDelete, reason);
2498                        updateLightsLocked();
2499                    }
2500                }
2501            }
2502        });
2503    }
2504
2505    /**
2506     * Determine whether the userId applies to the notification in question, either because
2507     * they match exactly, or one of them is USER_ALL (which is treated as a wildcard).
2508     */
2509    private boolean notificationMatchesUserId(NotificationRecord r, int userId) {
2510        return
2511                // looking for USER_ALL notifications? match everything
2512                   userId == UserHandle.USER_ALL
2513                // a notification sent to USER_ALL matches any query
2514                || r.getUserId() == UserHandle.USER_ALL
2515                // an exact user match
2516                || r.getUserId() == userId;
2517    }
2518
2519    /**
2520     * Determine whether the userId applies to the notification in question, either because
2521     * they match exactly, or one of them is USER_ALL (which is treated as a wildcard) or
2522     * because it matches one of the users profiles.
2523     */
2524    private boolean notificationMatchesCurrentProfiles(NotificationRecord r, int userId) {
2525        return notificationMatchesUserId(r, userId)
2526                || isCurrentProfile(r.getUserId());
2527    }
2528
2529    /**
2530     * Cancels all notifications from a given package that have all of the
2531     * {@code mustHaveFlags}.
2532     */
2533    boolean cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, int mustHaveFlags,
2534            int mustNotHaveFlags, boolean doit, int userId, int reason,
2535            NotificationListenerInfo listener) {
2536        EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
2537                pkg, userId, mustHaveFlags, mustNotHaveFlags, reason,
2538                listener == null ? null : listener.component.toShortString());
2539
2540        synchronized (mNotificationList) {
2541            final int N = mNotificationList.size();
2542            boolean canceledSomething = false;
2543            for (int i = N-1; i >= 0; --i) {
2544                NotificationRecord r = mNotificationList.get(i);
2545                if (!notificationMatchesUserId(r, userId)) {
2546                    continue;
2547                }
2548                // Don't remove notifications to all, if there's no package name specified
2549                if (r.getUserId() == UserHandle.USER_ALL && pkg == null) {
2550                    continue;
2551                }
2552                if ((r.getFlags() & mustHaveFlags) != mustHaveFlags) {
2553                    continue;
2554                }
2555                if ((r.getFlags() & mustNotHaveFlags) != 0) {
2556                    continue;
2557                }
2558                if (pkg != null && !r.sbn.getPackageName().equals(pkg)) {
2559                    continue;
2560                }
2561                canceledSomething = true;
2562                if (!doit) {
2563                    return true;
2564                }
2565                mNotificationList.remove(i);
2566                mNotificationsByKey.remove(r.sbn.getKey());
2567                cancelNotificationLocked(r, false, reason);
2568            }
2569            if (canceledSomething) {
2570                updateLightsLocked();
2571            }
2572            return canceledSomething;
2573        }
2574    }
2575
2576
2577
2578    // Return true if the UID is a system or phone UID and therefore should not have
2579    // any notifications or toasts blocked.
2580    boolean isUidSystem(int uid) {
2581        final int appid = UserHandle.getAppId(uid);
2582        return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0);
2583    }
2584
2585    // same as isUidSystem(int, int) for the Binder caller's UID.
2586    boolean isCallerSystem() {
2587        return isUidSystem(Binder.getCallingUid());
2588    }
2589
2590    void checkCallerIsSystem() {
2591        if (isCallerSystem()) {
2592            return;
2593        }
2594        throw new SecurityException("Disallowed call for uid " + Binder.getCallingUid());
2595    }
2596
2597    void checkCallerIsSystemOrSameApp(String pkg) {
2598        if (isCallerSystem()) {
2599            return;
2600        }
2601        final int uid = Binder.getCallingUid();
2602        try {
2603            ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo(
2604                    pkg, 0, UserHandle.getCallingUserId());
2605            if (!UserHandle.isSameApp(ai.uid, uid)) {
2606                throw new SecurityException("Calling uid " + uid + " gave package"
2607                        + pkg + " which is owned by uid " + ai.uid);
2608            }
2609        } catch (RemoteException re) {
2610            throw new SecurityException("Unknown package " + pkg + "\n" + re);
2611        }
2612    }
2613
2614    void cancelAllLocked(int callingUid, int callingPid, int userId, int reason,
2615            NotificationListenerInfo listener, boolean includeCurrentProfiles) {
2616        EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
2617                null, userId, 0, 0, reason,
2618                listener == null ? null : listener.component.toShortString());
2619
2620        final int N = mNotificationList.size();
2621        for (int i=N-1; i>=0; i--) {
2622            NotificationRecord r = mNotificationList.get(i);
2623            if (includeCurrentProfiles) {
2624                if (!notificationMatchesCurrentProfiles(r, userId)) {
2625                    continue;
2626                }
2627            } else {
2628                if (!notificationMatchesUserId(r, userId)) {
2629                    continue;
2630                }
2631            }
2632
2633            if ((r.getFlags() & (Notification.FLAG_ONGOING_EVENT
2634                            | Notification.FLAG_NO_CLEAR)) == 0) {
2635                mNotificationList.remove(i);
2636                mNotificationsByKey.remove(r.sbn.getKey());
2637                cancelNotificationLocked(r, true, reason);
2638            }
2639        }
2640        updateLightsLocked();
2641    }
2642
2643    // lock on mNotificationList
2644    void updateLightsLocked()
2645    {
2646        // handle notification lights
2647        if (mLedNotification == null) {
2648            // get next notification, if any
2649            int n = mLights.size();
2650            if (n > 0) {
2651                mLedNotification = mLights.get(n-1);
2652            }
2653        }
2654
2655        // Don't flash while we are in a call or screen is on
2656        if (mLedNotification == null || mInCall || mScreenOn) {
2657            mNotificationLight.turnOff();
2658        } else {
2659            final Notification ledno = mLedNotification.sbn.getNotification();
2660            int ledARGB = ledno.ledARGB;
2661            int ledOnMS = ledno.ledOnMS;
2662            int ledOffMS = ledno.ledOffMS;
2663            if ((ledno.defaults & Notification.DEFAULT_LIGHTS) != 0) {
2664                ledARGB = mDefaultNotificationColor;
2665                ledOnMS = mDefaultNotificationLedOn;
2666                ledOffMS = mDefaultNotificationLedOff;
2667            }
2668            if (mNotificationPulseEnabled) {
2669                // pulse repeatedly
2670                mNotificationLight.setFlashing(ledARGB, Light.LIGHT_FLASH_TIMED,
2671                        ledOnMS, ledOffMS);
2672            }
2673        }
2674    }
2675
2676    // lock on mNotificationList
2677    int indexOfNotificationLocked(String pkg, String tag, int id, int userId)
2678    {
2679        ArrayList<NotificationRecord> list = mNotificationList;
2680        final int len = list.size();
2681        for (int i=0; i<len; i++) {
2682            NotificationRecord r = list.get(i);
2683            if (!notificationMatchesUserId(r, userId) || r.sbn.getId() != id) {
2684                continue;
2685            }
2686            if (tag == null) {
2687                if (r.sbn.getTag() != null) {
2688                    continue;
2689                }
2690            } else {
2691                if (!tag.equals(r.sbn.getTag())) {
2692                    continue;
2693                }
2694            }
2695            if (r.sbn.getPackageName().equals(pkg)) {
2696                return i;
2697            }
2698        }
2699        return -1;
2700    }
2701
2702    private void updateNotificationPulse() {
2703        synchronized (mNotificationList) {
2704            updateLightsLocked();
2705        }
2706    }
2707
2708    private void updateZenMode() {
2709        final int mode = Settings.Global.getInt(getContext().getContentResolver(),
2710                Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF);
2711        if (mode != mZenMode) {
2712            Slog.d(TAG, String.format("updateZenMode: %s -> %s",
2713                    Settings.Global.zenModeToString(mZenMode),
2714                    Settings.Global.zenModeToString(mode)));
2715        }
2716        mZenMode = mode;
2717
2718        final String[] exceptionPackages = null; // none (for now)
2719
2720        // call restrictions
2721        final boolean muteCalls = mZenMode != Settings.Global.ZEN_MODE_OFF;
2722        mAppOps.setRestriction(AppOpsManager.OP_VIBRATE, AudioManager.STREAM_RING,
2723                muteCalls ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
2724                exceptionPackages);
2725        mAppOps.setRestriction(AppOpsManager.OP_PLAY_AUDIO, AudioManager.STREAM_RING,
2726                muteCalls ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
2727                exceptionPackages);
2728
2729        // alarm restrictions
2730        final boolean muteAlarms = false; // TODO until we save user config
2731        mAppOps.setRestriction(AppOpsManager.OP_VIBRATE, AudioManager.STREAM_ALARM,
2732                muteAlarms ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
2733                exceptionPackages);
2734        mAppOps.setRestriction(AppOpsManager.OP_PLAY_AUDIO, AudioManager.STREAM_ALARM,
2735                muteAlarms ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
2736                exceptionPackages);
2737
2738        // restrict vibrations with no hints
2739        mAppOps.setRestriction(AppOpsManager.OP_VIBRATE, AudioManager.USE_DEFAULT_STREAM_TYPE,
2740                (muteAlarms || muteCalls) ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
2741                exceptionPackages);
2742    }
2743
2744    private void updateCurrentProfilesCache(Context context) {
2745        UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
2746        if (userManager != null) {
2747            int currentUserId = ActivityManager.getCurrentUser();
2748            List<UserInfo> profiles = userManager.getProfiles(currentUserId);
2749            synchronized (mCurrentProfiles) {
2750                mCurrentProfiles.clear();
2751                for (UserInfo user : profiles) {
2752                    mCurrentProfiles.put(user.id, user);
2753                }
2754            }
2755        }
2756    }
2757
2758    private int[] getCurrentProfileIds() {
2759        synchronized (mCurrentProfiles) {
2760            int[] users = new int[mCurrentProfiles.size()];
2761            final int N = mCurrentProfiles.size();
2762            for (int i = 0; i < N; ++i) {
2763                users[i] = mCurrentProfiles.keyAt(i);
2764            }
2765            return users;
2766        }
2767    }
2768
2769    private boolean isCurrentProfile(int userId) {
2770        synchronized (mCurrentProfiles) {
2771            return mCurrentProfiles.get(userId) != null;
2772        }
2773    }
2774
2775    private boolean isCall(String pkg, Notification n) {
2776        return CALL_PACKAGES.contains(pkg);
2777    }
2778
2779    private boolean isAlarm(String pkg, Notification n) {
2780        return ALARM_PACKAGES.contains(pkg);
2781    }
2782
2783    private boolean shouldIntercept(String pkg, Notification n) {
2784        if (mZenMode != Settings.Global.ZEN_MODE_OFF) {
2785            return !isAlarm(pkg, n);
2786        }
2787        return false;
2788    }
2789}
2790