NotificationManagerService.java revision 92b389da2a60ac51e804031494fea177fc1c47be
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            EventLogTags.writeNotificationPanelRevealed();
1014            synchronized (mNotificationList) {
1015                // sound
1016                mSoundNotification = null;
1017
1018                long identity = Binder.clearCallingIdentity();
1019                try {
1020                    final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
1021                    if (player != null) {
1022                        player.stopAsync();
1023                    }
1024                } catch (RemoteException e) {
1025                } finally {
1026                    Binder.restoreCallingIdentity(identity);
1027                }
1028
1029                // vibrate
1030                mVibrateNotification = null;
1031                identity = Binder.clearCallingIdentity();
1032                try {
1033                    mVibrator.cancel();
1034                } finally {
1035                    Binder.restoreCallingIdentity(identity);
1036                }
1037
1038                // light
1039                mLights.clear();
1040                mLedNotification = null;
1041                updateLightsLocked();
1042            }
1043        }
1044
1045        @Override
1046        public void onPanelHidden() {
1047            EventLogTags.writeNotificationPanelHidden();
1048        }
1049
1050        @Override
1051        public void onNotificationError(int callingUid, int callingPid, String pkg, String tag, int id,
1052                int uid, int initialPid, String message, int userId) {
1053            Slog.d(TAG, "onNotification error pkg=" + pkg + " tag=" + tag + " id=" + id
1054                    + "; will crashApplication(uid=" + uid + ", pid=" + initialPid + ")");
1055            cancelNotification(callingUid, callingPid, pkg, tag, id, 0, 0, false, userId,
1056                    REASON_DELEGATE_ERROR, null);
1057            long ident = Binder.clearCallingIdentity();
1058            try {
1059                ActivityManagerNative.getDefault().crashApplication(uid, initialPid, pkg,
1060                        "Bad notification posted from package " + pkg
1061                        + ": " + message);
1062            } catch (RemoteException e) {
1063            }
1064            Binder.restoreCallingIdentity(ident);
1065        }
1066
1067        @Override
1068        public boolean allowDisable(int what, IBinder token, String pkg) {
1069            if (isCall(pkg, null)) {
1070                return mZenMode == Settings.Global.ZEN_MODE_OFF;
1071            }
1072            return true;
1073        }
1074
1075        @Override
1076        public void onNotificationVisibilityChanged(
1077                String[] newlyVisibleKeys, String[] noLongerVisibleKeys) {
1078            // Using ';' as separator since eventlogs uses ',' to separate
1079            // args.
1080            EventLogTags.writeNotificationVisibilityChanged(
1081                    TextUtils.join(";", newlyVisibleKeys),
1082                    TextUtils.join(";", noLongerVisibleKeys));
1083        }
1084    };
1085
1086    private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
1087        @Override
1088        public void onReceive(Context context, Intent intent) {
1089            String action = intent.getAction();
1090
1091            boolean queryRestart = false;
1092            boolean queryRemove = false;
1093            boolean packageChanged = false;
1094            boolean cancelNotifications = true;
1095
1096            if (action.equals(Intent.ACTION_PACKAGE_ADDED)
1097                    || (queryRemove=action.equals(Intent.ACTION_PACKAGE_REMOVED))
1098                    || action.equals(Intent.ACTION_PACKAGE_RESTARTED)
1099                    || (packageChanged=action.equals(Intent.ACTION_PACKAGE_CHANGED))
1100                    || (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART))
1101                    || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
1102                String pkgList[] = null;
1103                boolean queryReplace = queryRemove &&
1104                        intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
1105                if (DBG) Slog.i(TAG, "queryReplace=" + queryReplace);
1106                if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
1107                    pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
1108                } else if (queryRestart) {
1109                    pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
1110                } else {
1111                    Uri uri = intent.getData();
1112                    if (uri == null) {
1113                        return;
1114                    }
1115                    String pkgName = uri.getSchemeSpecificPart();
1116                    if (pkgName == null) {
1117                        return;
1118                    }
1119                    if (packageChanged) {
1120                        // We cancel notifications for packages which have just been disabled
1121                        try {
1122                            final int enabled = getContext().getPackageManager()
1123                                    .getApplicationEnabledSetting(pkgName);
1124                            if (enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED
1125                                    || enabled == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
1126                                cancelNotifications = false;
1127                            }
1128                        } catch (IllegalArgumentException e) {
1129                            // Package doesn't exist; probably racing with uninstall.
1130                            // cancelNotifications is already true, so nothing to do here.
1131                            if (DBG) {
1132                                Slog.i(TAG, "Exception trying to look up app enabled setting", e);
1133                            }
1134                        }
1135                    }
1136                    pkgList = new String[]{pkgName};
1137                }
1138
1139                boolean anyListenersInvolved = false;
1140                if (pkgList != null && (pkgList.length > 0)) {
1141                    for (String pkgName : pkgList) {
1142                        if (cancelNotifications) {
1143                            cancelAllNotificationsInt(MY_UID, MY_PID, pkgName, 0, 0, !queryRestart,
1144                                    UserHandle.USER_ALL, REASON_PACKAGE_CHANGED, null);
1145                        }
1146                        if (mEnabledListenerPackageNames.contains(pkgName)) {
1147                            anyListenersInvolved = true;
1148                        }
1149                    }
1150                }
1151
1152                if (anyListenersInvolved) {
1153                    // if we're not replacing a package, clean up orphaned bits
1154                    if (!queryReplace) {
1155                        disableNonexistentListeners();
1156                    }
1157                    // make sure we're still bound to any of our
1158                    // listeners who may have just upgraded
1159                    rebindListenerServices();
1160                }
1161            } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
1162                // Keep track of screen on/off state, but do not turn off the notification light
1163                // until user passes through the lock screen or views the notification.
1164                mScreenOn = true;
1165            } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
1166                mScreenOn = false;
1167            } else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) {
1168                mInCall = TelephonyManager.EXTRA_STATE_OFFHOOK
1169                        .equals(intent.getStringExtra(TelephonyManager.EXTRA_STATE));
1170                updateNotificationPulse();
1171            } else if (action.equals(Intent.ACTION_USER_STOPPED)) {
1172                int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
1173                if (userHandle >= 0) {
1174                    cancelAllNotificationsInt(MY_UID, MY_PID, null, 0, 0, true, userHandle,
1175                            REASON_USER_STOPPED, null);
1176                }
1177            } else if (action.equals(Intent.ACTION_USER_PRESENT)) {
1178                // turn off LED when user passes through lock screen
1179                mNotificationLight.turnOff();
1180            } else if (action.equals(Intent.ACTION_USER_SWITCHED)) {
1181                // reload per-user settings
1182                mSettingsObserver.update(null);
1183                updateCurrentProfilesCache(context);
1184            } else if (action.equals(Intent.ACTION_USER_ADDED)) {
1185                updateCurrentProfilesCache(context);
1186            }
1187        }
1188    };
1189
1190    class SettingsObserver extends ContentObserver {
1191        private final Uri NOTIFICATION_LIGHT_PULSE_URI
1192                = Settings.System.getUriFor(Settings.System.NOTIFICATION_LIGHT_PULSE);
1193
1194        private final Uri ENABLED_NOTIFICATION_LISTENERS_URI
1195                = Settings.Secure.getUriFor(Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
1196
1197        private final Uri ZEN_MODE
1198                = Settings.Global.getUriFor(Settings.Global.ZEN_MODE);
1199
1200        SettingsObserver(Handler handler) {
1201            super(handler);
1202        }
1203
1204        void observe() {
1205            ContentResolver resolver = getContext().getContentResolver();
1206            resolver.registerContentObserver(NOTIFICATION_LIGHT_PULSE_URI,
1207                    false, this, UserHandle.USER_ALL);
1208            resolver.registerContentObserver(ENABLED_NOTIFICATION_LISTENERS_URI,
1209                    false, this, UserHandle.USER_ALL);
1210            resolver.registerContentObserver(ZEN_MODE,
1211                    false, this);
1212            update(null);
1213        }
1214
1215        @Override public void onChange(boolean selfChange, Uri uri) {
1216            update(uri);
1217        }
1218
1219        public void update(Uri uri) {
1220            ContentResolver resolver = getContext().getContentResolver();
1221            if (uri == null || NOTIFICATION_LIGHT_PULSE_URI.equals(uri)) {
1222                boolean pulseEnabled = Settings.System.getInt(resolver,
1223                            Settings.System.NOTIFICATION_LIGHT_PULSE, 0) != 0;
1224                if (mNotificationPulseEnabled != pulseEnabled) {
1225                    mNotificationPulseEnabled = pulseEnabled;
1226                    updateNotificationPulse();
1227                }
1228            }
1229            if (uri == null || ENABLED_NOTIFICATION_LISTENERS_URI.equals(uri)) {
1230                rebindListenerServices();
1231            }
1232            if (ZEN_MODE.equals(uri)) {
1233                updateZenMode();
1234            }
1235        }
1236    }
1237
1238    private SettingsObserver mSettingsObserver;
1239
1240    static long[] getLongArray(Resources r, int resid, int maxlen, long[] def) {
1241        int[] ar = r.getIntArray(resid);
1242        if (ar == null) {
1243            return def;
1244        }
1245        final int len = ar.length > maxlen ? maxlen : ar.length;
1246        long[] out = new long[len];
1247        for (int i=0; i<len; i++) {
1248            out[i] = ar[i];
1249        }
1250        return out;
1251    }
1252
1253    public NotificationManagerService(Context context) {
1254        super(context);
1255    }
1256
1257    @Override
1258    public void onStart() {
1259        mAm = ActivityManagerNative.getDefault();
1260        mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
1261        mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
1262
1263        mHandler = new WorkerHandler();
1264
1265        importOldBlockDb();
1266
1267        mStatusBar = getLocalService(StatusBarManagerInternal.class);
1268        mStatusBar.setNotificationDelegate(mNotificationDelegate);
1269
1270        final LightsManager lights = getLocalService(LightsManager.class);
1271        mNotificationLight = lights.getLight(LightsManager.LIGHT_ID_NOTIFICATIONS);
1272        mAttentionLight = lights.getLight(LightsManager.LIGHT_ID_ATTENTION);
1273
1274        Resources resources = getContext().getResources();
1275        mDefaultNotificationColor = resources.getColor(
1276                R.color.config_defaultNotificationColor);
1277        mDefaultNotificationLedOn = resources.getInteger(
1278                R.integer.config_defaultNotificationLedOn);
1279        mDefaultNotificationLedOff = resources.getInteger(
1280                R.integer.config_defaultNotificationLedOff);
1281
1282        mDefaultVibrationPattern = getLongArray(resources,
1283                R.array.config_defaultNotificationVibePattern,
1284                VIBRATE_PATTERN_MAXLEN,
1285                DEFAULT_VIBRATE_PATTERN);
1286
1287        mFallbackVibrationPattern = getLongArray(resources,
1288                R.array.config_notificationFallbackVibePattern,
1289                VIBRATE_PATTERN_MAXLEN,
1290                DEFAULT_VIBRATE_PATTERN);
1291
1292        // Don't start allowing notifications until the setup wizard has run once.
1293        // After that, including subsequent boots, init with notifications turned on.
1294        // This works on the first boot because the setup wizard will toggle this
1295        // flag at least once and we'll go back to 0 after that.
1296        if (0 == Settings.Global.getInt(getContext().getContentResolver(),
1297                    Settings.Global.DEVICE_PROVISIONED, 0)) {
1298            mDisableNotificationAlerts = true;
1299        }
1300        updateZenMode();
1301
1302        updateCurrentProfilesCache(getContext());
1303
1304        // register for various Intents
1305        IntentFilter filter = new IntentFilter();
1306        filter.addAction(Intent.ACTION_SCREEN_ON);
1307        filter.addAction(Intent.ACTION_SCREEN_OFF);
1308        filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
1309        filter.addAction(Intent.ACTION_USER_PRESENT);
1310        filter.addAction(Intent.ACTION_USER_STOPPED);
1311        filter.addAction(Intent.ACTION_USER_SWITCHED);
1312        filter.addAction(Intent.ACTION_USER_ADDED);
1313        getContext().registerReceiver(mIntentReceiver, filter);
1314        IntentFilter pkgFilter = new IntentFilter();
1315        pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
1316        pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
1317        pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
1318        pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
1319        pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
1320        pkgFilter.addDataScheme("package");
1321        getContext().registerReceiver(mIntentReceiver, pkgFilter);
1322        IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
1323        getContext().registerReceiver(mIntentReceiver, sdFilter);
1324
1325        mSettingsObserver = new SettingsObserver(mHandler);
1326
1327        // spin up NotificationScorers
1328        String[] notificationScorerNames = resources.getStringArray(
1329                R.array.config_notificationScorers);
1330        for (String scorerName : notificationScorerNames) {
1331            try {
1332                Class<?> scorerClass = getContext().getClassLoader().loadClass(scorerName);
1333                NotificationScorer scorer = (NotificationScorer) scorerClass.newInstance();
1334                scorer.initialize(getContext());
1335                mScorers.add(scorer);
1336            } catch (ClassNotFoundException e) {
1337                Slog.w(TAG, "Couldn't find scorer " + scorerName + ".", e);
1338            } catch (InstantiationException e) {
1339                Slog.w(TAG, "Couldn't instantiate scorer " + scorerName + ".", e);
1340            } catch (IllegalAccessException e) {
1341                Slog.w(TAG, "Problem accessing scorer " + scorerName + ".", e);
1342            }
1343        }
1344
1345        publishBinderService(Context.NOTIFICATION_SERVICE, mService);
1346        publishLocalService(NotificationManagerInternal.class, mInternalService);
1347    }
1348
1349    /**
1350     * Read the old XML-based app block database and import those blockages into the AppOps system.
1351     */
1352    private void importOldBlockDb() {
1353        loadBlockDb();
1354
1355        PackageManager pm = getContext().getPackageManager();
1356        for (String pkg : mBlockedPackages) {
1357            PackageInfo info = null;
1358            try {
1359                info = pm.getPackageInfo(pkg, 0);
1360                setNotificationsEnabledForPackageImpl(pkg, info.applicationInfo.uid, false);
1361            } catch (NameNotFoundException e) {
1362                // forget you
1363            }
1364        }
1365        mBlockedPackages.clear();
1366        if (mPolicyFile != null) {
1367            mPolicyFile.delete();
1368        }
1369    }
1370
1371    @Override
1372    public void onBootPhase(int phase) {
1373        if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
1374            // no beeping until we're basically done booting
1375            mSystemReady = true;
1376
1377            // Grab our optional AudioService
1378            mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
1379
1380        } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
1381            // This observer will force an update when observe is called, causing us to
1382            // bind to listener services.
1383            mSettingsObserver.observe();
1384        }
1385    }
1386
1387    void setNotificationsEnabledForPackageImpl(String pkg, int uid, boolean enabled) {
1388        Slog.v(TAG, (enabled?"en":"dis") + "abling notifications for " + pkg);
1389
1390        mAppOps.setMode(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg,
1391                enabled ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED);
1392
1393        // Now, cancel any outstanding notifications that are part of a just-disabled app
1394        if (ENABLE_BLOCKED_NOTIFICATIONS && !enabled) {
1395            cancelAllNotificationsInt(MY_UID, MY_PID, pkg, 0, 0, true, UserHandle.getUserId(uid),
1396                    REASON_PACKAGE_BANNED, null);
1397        }
1398    }
1399
1400    private final IBinder mService = new INotificationManager.Stub() {
1401        // Toasts
1402        // ============================================================================
1403
1404        @Override
1405        public void enqueueToast(String pkg, ITransientNotification callback, int duration)
1406        {
1407            if (DBG) {
1408                Slog.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback
1409                        + " duration=" + duration);
1410            }
1411
1412            if (pkg == null || callback == null) {
1413                Slog.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback);
1414                return ;
1415            }
1416
1417            final boolean isSystemToast = isCallerSystem() || ("android".equals(pkg));
1418
1419            if (ENABLE_BLOCKED_TOASTS && !noteNotificationOp(pkg, Binder.getCallingUid())) {
1420                if (!isSystemToast) {
1421                    Slog.e(TAG, "Suppressing toast from package " + pkg + " by user request.");
1422                    return;
1423                }
1424            }
1425
1426            synchronized (mToastQueue) {
1427                int callingPid = Binder.getCallingPid();
1428                long callingId = Binder.clearCallingIdentity();
1429                try {
1430                    ToastRecord record;
1431                    int index = indexOfToastLocked(pkg, callback);
1432                    // If it's already in the queue, we update it in place, we don't
1433                    // move it to the end of the queue.
1434                    if (index >= 0) {
1435                        record = mToastQueue.get(index);
1436                        record.update(duration);
1437                    } else {
1438                        // Limit the number of toasts that any given package except the android
1439                        // package can enqueue.  Prevents DOS attacks and deals with leaks.
1440                        if (!isSystemToast) {
1441                            int count = 0;
1442                            final int N = mToastQueue.size();
1443                            for (int i=0; i<N; i++) {
1444                                 final ToastRecord r = mToastQueue.get(i);
1445                                 if (r.pkg.equals(pkg)) {
1446                                     count++;
1447                                     if (count >= MAX_PACKAGE_NOTIFICATIONS) {
1448                                         Slog.e(TAG, "Package has already posted " + count
1449                                                + " toasts. Not showing more. Package=" + pkg);
1450                                         return;
1451                                     }
1452                                 }
1453                            }
1454                        }
1455
1456                        record = new ToastRecord(callingPid, pkg, callback, duration);
1457                        mToastQueue.add(record);
1458                        index = mToastQueue.size() - 1;
1459                        keepProcessAliveLocked(callingPid);
1460                    }
1461                    // If it's at index 0, it's the current toast.  It doesn't matter if it's
1462                    // new or just been updated.  Call back and tell it to show itself.
1463                    // If the callback fails, this will remove it from the list, so don't
1464                    // assume that it's valid after this.
1465                    if (index == 0) {
1466                        showNextToastLocked();
1467                    }
1468                } finally {
1469                    Binder.restoreCallingIdentity(callingId);
1470                }
1471            }
1472        }
1473
1474        @Override
1475        public void cancelToast(String pkg, ITransientNotification callback) {
1476            Slog.i(TAG, "cancelToast pkg=" + pkg + " callback=" + callback);
1477
1478            if (pkg == null || callback == null) {
1479                Slog.e(TAG, "Not cancelling notification. pkg=" + pkg + " callback=" + callback);
1480                return ;
1481            }
1482
1483            synchronized (mToastQueue) {
1484                long callingId = Binder.clearCallingIdentity();
1485                try {
1486                    int index = indexOfToastLocked(pkg, callback);
1487                    if (index >= 0) {
1488                        cancelToastLocked(index);
1489                    } else {
1490                        Slog.w(TAG, "Toast already cancelled. pkg=" + pkg
1491                                + " callback=" + callback);
1492                    }
1493                } finally {
1494                    Binder.restoreCallingIdentity(callingId);
1495                }
1496            }
1497        }
1498
1499        @Override
1500        public void enqueueNotificationWithTag(String pkg, String basePkg, String tag, int id,
1501                Notification notification, int[] idOut, int userId) throws RemoteException {
1502            enqueueNotificationInternal(pkg, basePkg, Binder.getCallingUid(),
1503                    Binder.getCallingPid(), tag, id, notification, idOut, userId);
1504        }
1505
1506        @Override
1507        public void cancelNotificationWithTag(String pkg, String tag, int id, int userId) {
1508            checkCallerIsSystemOrSameApp(pkg);
1509            userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1510                    Binder.getCallingUid(), userId, true, false, "cancelNotificationWithTag", pkg);
1511            // Don't allow client applications to cancel foreground service notis.
1512            cancelNotification(Binder.getCallingUid(), Binder.getCallingPid(), pkg, tag, id, 0,
1513                    Binder.getCallingUid() == Process.SYSTEM_UID
1514                    ? 0 : Notification.FLAG_FOREGROUND_SERVICE, false, userId, REASON_NOMAN_CANCEL,
1515                    null);
1516        }
1517
1518        @Override
1519        public void cancelAllNotifications(String pkg, int userId) {
1520            checkCallerIsSystemOrSameApp(pkg);
1521
1522            userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1523                    Binder.getCallingUid(), userId, true, false, "cancelAllNotifications", pkg);
1524
1525            // Calling from user space, don't allow the canceling of actively
1526            // running foreground services.
1527            cancelAllNotificationsInt(Binder.getCallingUid(), Binder.getCallingPid(),
1528                    pkg, 0, Notification.FLAG_FOREGROUND_SERVICE, true, userId,
1529                    REASON_NOMAN_CANCEL_ALL, null);
1530        }
1531
1532        @Override
1533        public void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) {
1534            checkCallerIsSystem();
1535
1536            setNotificationsEnabledForPackageImpl(pkg, uid, enabled);
1537        }
1538
1539        /**
1540         * Use this when you just want to know if notifications are OK for this package.
1541         */
1542        @Override
1543        public boolean areNotificationsEnabledForPackage(String pkg, int uid) {
1544            checkCallerIsSystem();
1545            return (mAppOps.checkOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
1546                    == AppOpsManager.MODE_ALLOWED);
1547        }
1548
1549        /**
1550         * System-only API for getting a list of current (i.e. not cleared) notifications.
1551         *
1552         * Requires ACCESS_NOTIFICATIONS which is signature|system.
1553         */
1554        @Override
1555        public StatusBarNotification[] getActiveNotifications(String callingPkg) {
1556            // enforce() will ensure the calling uid has the correct permission
1557            getContext().enforceCallingOrSelfPermission(
1558                    android.Manifest.permission.ACCESS_NOTIFICATIONS,
1559                    "NotificationManagerService.getActiveNotifications");
1560
1561            StatusBarNotification[] tmp = null;
1562            int uid = Binder.getCallingUid();
1563
1564            // noteOp will check to make sure the callingPkg matches the uid
1565            if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
1566                    == AppOpsManager.MODE_ALLOWED) {
1567                synchronized (mNotificationList) {
1568                    tmp = new StatusBarNotification[mNotificationList.size()];
1569                    final int N = mNotificationList.size();
1570                    for (int i=0; i<N; i++) {
1571                        tmp[i] = mNotificationList.get(i).sbn;
1572                    }
1573                }
1574            }
1575            return tmp;
1576        }
1577
1578        /**
1579         * System-only API for getting a list of recent (cleared, no longer shown) notifications.
1580         *
1581         * Requires ACCESS_NOTIFICATIONS which is signature|system.
1582         */
1583        @Override
1584        public StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count) {
1585            // enforce() will ensure the calling uid has the correct permission
1586            getContext().enforceCallingOrSelfPermission(
1587                    android.Manifest.permission.ACCESS_NOTIFICATIONS,
1588                    "NotificationManagerService.getHistoricalNotifications");
1589
1590            StatusBarNotification[] tmp = null;
1591            int uid = Binder.getCallingUid();
1592
1593            // noteOp will check to make sure the callingPkg matches the uid
1594            if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
1595                    == AppOpsManager.MODE_ALLOWED) {
1596                synchronized (mArchive) {
1597                    tmp = mArchive.getArray(count);
1598                }
1599            }
1600            return tmp;
1601        }
1602
1603        /**
1604         * Register a listener binder directly with the notification manager.
1605         *
1606         * Only works with system callers. Apps should extend
1607         * {@link android.service.notification.NotificationListenerService}.
1608         */
1609        @Override
1610        public void registerListener(final INotificationListener listener,
1611                final ComponentName component, final int userid) {
1612            checkCallerIsSystem();
1613            checkNullListener(listener);
1614            registerListenerImpl(listener, component, userid);
1615        }
1616
1617        /**
1618         * Remove a listener binder directly
1619         */
1620        @Override
1621        public void unregisterListener(INotificationListener listener, int userid) {
1622            checkNullListener(listener);
1623            // no need to check permissions; if your listener binder is in the list,
1624            // that's proof that you had permission to add it in the first place
1625            unregisterListenerImpl(listener, userid);
1626        }
1627
1628        /**
1629         * Allow an INotificationListener to simulate a "clear all" operation.
1630         *
1631         * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onClearAllNotifications}
1632         *
1633         * @param token The binder for the listener, to check that the caller is allowed
1634         */
1635        @Override
1636        public void cancelNotificationsFromListener(INotificationListener token, String[] keys) {
1637            final int callingUid = Binder.getCallingUid();
1638            final int callingPid = Binder.getCallingPid();
1639            long identity = Binder.clearCallingIdentity();
1640            try {
1641                synchronized (mNotificationList) {
1642                    final NotificationListenerInfo info = checkListenerTokenLocked(token);
1643                    if (keys != null) {
1644                        final int N = keys.length;
1645                        for (int i = 0; i < N; i++) {
1646                            NotificationRecord r = mNotificationsByKey.get(keys[i]);
1647                            final int userId = r.sbn.getUserId();
1648                            if (userId != info.userid && userId != UserHandle.USER_ALL &&
1649                                    !isCurrentProfile(userId)) {
1650                                throw new SecurityException("Disallowed call from listener: "
1651                                        + info.listener);
1652                            }
1653                            if (r != null) {
1654                                cancelNotificationFromListenerLocked(info, callingUid, callingPid,
1655                                        r.sbn.getPackageName(), r.sbn.getTag(), r.sbn.getId(),
1656                                        userId);
1657                            }
1658                        }
1659                    } else {
1660                        cancelAllLocked(callingUid, callingPid, info.userid,
1661                                REASON_LISTENER_CANCEL_ALL, info, info.supportsProfiles());
1662                    }
1663                }
1664            } finally {
1665                Binder.restoreCallingIdentity(identity);
1666            }
1667        }
1668
1669        private void cancelNotificationFromListenerLocked(NotificationListenerInfo info,
1670                int callingUid, int callingPid, String pkg, String tag, int id, int userId) {
1671            cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
1672                    Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
1673                    true,
1674                    userId, REASON_LISTENER_CANCEL, info);
1675        }
1676
1677        /**
1678         * Allow an INotificationListener to simulate clearing (dismissing) a single notification.
1679         *
1680         * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onNotificationClear}
1681         *
1682         * @param token The binder for the listener, to check that the caller is allowed
1683         */
1684        @Override
1685        public void cancelNotificationFromListener(INotificationListener token, String pkg,
1686                String tag, int id) {
1687            final int callingUid = Binder.getCallingUid();
1688            final int callingPid = Binder.getCallingPid();
1689            long identity = Binder.clearCallingIdentity();
1690            try {
1691                synchronized (mNotificationList) {
1692                    final NotificationListenerInfo info = checkListenerTokenLocked(token);
1693                    if (info.supportsProfiles()) {
1694                        Log.e(TAG, "Ignoring deprecated cancelNotification(pkg, tag, id) "
1695                                + "from " + info.component
1696                                + " use cancelNotification(key) instead.");
1697                    } else {
1698                        cancelNotificationFromListenerLocked(info, callingUid, callingPid,
1699                                pkg, tag, id, info.userid);
1700                    }
1701                }
1702            } finally {
1703                Binder.restoreCallingIdentity(identity);
1704            }
1705        }
1706
1707        /**
1708         * Allow an INotificationListener to request the list of outstanding notifications seen by
1709         * the current user. Useful when starting up, after which point the listener callbacks
1710         * should be used.
1711         *
1712         * @param token The binder for the listener, to check that the caller is allowed
1713         */
1714        @Override
1715        public StatusBarNotification[] getActiveNotificationsFromListener(
1716                INotificationListener token, String[] keys) {
1717            synchronized (mNotificationList) {
1718                final NotificationListenerInfo info = checkListenerTokenLocked(token);
1719                final ArrayList<StatusBarNotification> list
1720                        = new ArrayList<StatusBarNotification>();
1721                if (keys == null) {
1722                    final int N = mNotificationList.size();
1723                    for (int i=0; i<N; i++) {
1724                        StatusBarNotification sbn = mNotificationList.get(i).sbn;
1725                        if (info.enabledAndUserMatches(sbn)) {
1726                            list.add(sbn);
1727                        }
1728                    }
1729                } else {
1730                    final int N = keys.length;
1731                    for (int i=0; i<N; i++) {
1732                        NotificationRecord r = mNotificationsByKey.get(keys[i]);
1733                        if (r != null && info.enabledAndUserMatches(r.sbn)) {
1734                            list.add(r.sbn);
1735                        }
1736                    }
1737                }
1738                return list.toArray(new StatusBarNotification[list.size()]);
1739            }
1740        }
1741
1742        @Override
1743        public String[] getActiveNotificationKeysFromListener(INotificationListener token) {
1744            return NotificationManagerService.this.getActiveNotificationKeysFromListener(token);
1745        }
1746
1747        @Override
1748        protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1749            if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
1750                    != PackageManager.PERMISSION_GRANTED) {
1751                pw.println("Permission Denial: can't dump NotificationManager from from pid="
1752                        + Binder.getCallingPid()
1753                        + ", uid=" + Binder.getCallingUid());
1754                return;
1755            }
1756
1757            dumpImpl(pw);
1758        }
1759    };
1760
1761    private String[] getActiveNotificationKeysFromListener(INotificationListener token) {
1762        synchronized (mNotificationList) {
1763            final NotificationListenerInfo info = checkListenerTokenLocked(token);
1764            final ArrayList<String> keys = new ArrayList<String>();
1765            final int N = mNotificationList.size();
1766            for (int i=0; i<N; i++) {
1767                final StatusBarNotification sbn = mNotificationList.get(i).sbn;
1768                if (info.enabledAndUserMatches(sbn)) {
1769                    keys.add(sbn.getKey());
1770                }
1771            }
1772            return keys.toArray(new String[keys.size()]);
1773        }
1774    }
1775
1776    void dumpImpl(PrintWriter pw) {
1777        pw.println("Current Notification Manager state:");
1778
1779        pw.println("  Listeners (" + mEnabledListenersForCurrentProfiles.size()
1780                + ") enabled for current profiles:");
1781        for (ComponentName cmpt : mEnabledListenersForCurrentProfiles) {
1782            pw.println("    " + cmpt);
1783        }
1784
1785        pw.println("  Live listeners (" + mListeners.size() + "):");
1786        for (NotificationListenerInfo info : mListeners) {
1787            pw.println("    " + info.component
1788                    + " (user " + info.userid + "): " + info.listener
1789                    + (info.isSystem?" SYSTEM":""));
1790        }
1791
1792        int N;
1793
1794        synchronized (mToastQueue) {
1795            N = mToastQueue.size();
1796            if (N > 0) {
1797                pw.println("  Toast Queue:");
1798                for (int i=0; i<N; i++) {
1799                    mToastQueue.get(i).dump(pw, "    ");
1800                }
1801                pw.println("  ");
1802            }
1803
1804        }
1805
1806        synchronized (mNotificationList) {
1807            N = mNotificationList.size();
1808            if (N > 0) {
1809                pw.println("  Notification List:");
1810                for (int i=0; i<N; i++) {
1811                    mNotificationList.get(i).dump(pw, "    ", getContext());
1812                }
1813                pw.println("  ");
1814            }
1815
1816            N = mLights.size();
1817            if (N > 0) {
1818                pw.println("  Lights List:");
1819                for (int i=0; i<N; i++) {
1820                    pw.println("    " + mLights.get(i));
1821                }
1822                pw.println("  ");
1823            }
1824
1825            pw.println("  mSoundNotification=" + mSoundNotification);
1826            pw.println("  mVibrateNotification=" + mVibrateNotification);
1827            pw.println("  mDisableNotificationAlerts=" + mDisableNotificationAlerts);
1828            pw.println("  mZenMode=" + Settings.Global.zenModeToString(mZenMode));
1829            pw.println("  mSystemReady=" + mSystemReady);
1830            pw.println("  mArchive=" + mArchive.toString());
1831            Iterator<StatusBarNotification> iter = mArchive.descendingIterator();
1832            int i=0;
1833            while (iter.hasNext()) {
1834                pw.println("    " + iter.next());
1835                if (++i >= 5) {
1836                    if (iter.hasNext()) pw.println("    ...");
1837                    break;
1838                }
1839            }
1840
1841            pw.println("\n  Usage Stats:");
1842            mUsageStats.dump(pw, "    ");
1843
1844        }
1845    }
1846
1847    /**
1848     * The private API only accessible to the system process.
1849     */
1850    private final NotificationManagerInternal mInternalService = new NotificationManagerInternal() {
1851        @Override
1852        public void enqueueNotification(String pkg, String basePkg, int callingUid, int callingPid,
1853                String tag, int id, Notification notification, int[] idReceived, int userId) {
1854            enqueueNotificationInternal(pkg, basePkg, callingUid, callingPid, tag, id, notification,
1855                    idReceived, userId);
1856        }
1857    };
1858
1859    void enqueueNotificationInternal(final String pkg, String basePkg, final int callingUid,
1860            final int callingPid, final String tag, final int id, final Notification notification,
1861            int[] idOut, int incomingUserId) {
1862        if (DBG) {
1863            Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id
1864                    + " notification=" + notification);
1865        }
1866        checkCallerIsSystemOrSameApp(pkg);
1867        final boolean isSystemNotification = isUidSystem(callingUid) || ("android".equals(pkg));
1868
1869        final int userId = ActivityManager.handleIncomingUser(callingPid,
1870                callingUid, incomingUserId, true, false, "enqueueNotification", pkg);
1871        final UserHandle user = new UserHandle(userId);
1872
1873        // Limit the number of notifications that any given package except the android
1874        // package can enqueue.  Prevents DOS attacks and deals with leaks.
1875        if (!isSystemNotification) {
1876            synchronized (mNotificationList) {
1877                int count = 0;
1878                final int N = mNotificationList.size();
1879                for (int i=0; i<N; i++) {
1880                    final NotificationRecord r = mNotificationList.get(i);
1881                    if (r.sbn.getPackageName().equals(pkg) && r.sbn.getUserId() == userId) {
1882                        count++;
1883                        if (count >= MAX_PACKAGE_NOTIFICATIONS) {
1884                            Slog.e(TAG, "Package has already posted " + count
1885                                    + " notifications.  Not showing more.  package=" + pkg);
1886                            return;
1887                        }
1888                    }
1889                }
1890            }
1891        }
1892
1893        // This conditional is a dirty hack to limit the logging done on
1894        //     behalf of the download manager without affecting other apps.
1895        if (!pkg.equals("com.android.providers.downloads")
1896                || Log.isLoggable("DownloadManager", Log.VERBOSE)) {
1897            EventLogTags.writeNotificationEnqueue(callingUid, callingPid,
1898                    pkg, id, tag, userId, notification.toString());
1899        }
1900
1901        if (pkg == null || notification == null) {
1902            throw new IllegalArgumentException("null not allowed: pkg=" + pkg
1903                    + " id=" + id + " notification=" + notification);
1904        }
1905        if (notification.icon != 0) {
1906            if (notification.contentView == null) {
1907                throw new IllegalArgumentException("contentView required: pkg=" + pkg
1908                        + " id=" + id + " notification=" + notification);
1909            }
1910        }
1911
1912        mHandler.post(new Runnable() {
1913            @Override
1914            public void run() {
1915
1916                // === Scoring ===
1917
1918                // 0. Sanitize inputs
1919                notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN,
1920                        Notification.PRIORITY_MAX);
1921                // Migrate notification flags to scores
1922                if (0 != (notification.flags & Notification.FLAG_HIGH_PRIORITY)) {
1923                    if (notification.priority < Notification.PRIORITY_MAX) {
1924                        notification.priority = Notification.PRIORITY_MAX;
1925                    }
1926                } else if (SCORE_ONGOING_HIGHER &&
1927                        0 != (notification.flags & Notification.FLAG_ONGOING_EVENT)) {
1928                    if (notification.priority < Notification.PRIORITY_HIGH) {
1929                        notification.priority = Notification.PRIORITY_HIGH;
1930                    }
1931                }
1932
1933                // 1. initial score: buckets of 10, around the app
1934                int score = notification.priority * NOTIFICATION_PRIORITY_MULTIPLIER; //[-20..20]
1935
1936                // 2. Consult external heuristics (TBD)
1937
1938                // 3. Apply local rules
1939
1940                int initialScore = score;
1941                if (!mScorers.isEmpty()) {
1942                    if (DBG) Slog.v(TAG, "Initial score is " + score + ".");
1943                    for (NotificationScorer scorer : mScorers) {
1944                        try {
1945                            score = scorer.getScore(notification, score);
1946                        } catch (Throwable t) {
1947                            Slog.w(TAG, "Scorer threw on .getScore.", t);
1948                        }
1949                    }
1950                    if (DBG) Slog.v(TAG, "Final score is " + score + ".");
1951                }
1952
1953                // add extra to indicate score modified by NotificationScorer
1954                notification.extras.putBoolean(Notification.EXTRA_SCORE_MODIFIED,
1955                        score != initialScore);
1956
1957                // blocked apps
1958                if (ENABLE_BLOCKED_NOTIFICATIONS && !noteNotificationOp(pkg, callingUid)) {
1959                    if (!isSystemNotification) {
1960                        score = JUNK_SCORE;
1961                        Slog.e(TAG, "Suppressing notification from package " + pkg
1962                                + " by user request.");
1963                    }
1964                }
1965
1966                if (DBG) {
1967                    Slog.v(TAG, "Assigned score=" + score + " to " + notification);
1968                }
1969
1970                if (score < SCORE_DISPLAY_THRESHOLD) {
1971                    // Notification will be blocked because the score is too low.
1972                    return;
1973                }
1974
1975                // Is this notification intercepted by zen mode?
1976                final boolean intercept = shouldIntercept(pkg, notification);
1977                notification.extras.putBoolean(EXTRA_INTERCEPT, intercept);
1978
1979                // Should this notification make noise, vibe, or use the LED?
1980                final boolean canInterrupt = (score >= SCORE_INTERRUPTION_THRESHOLD) && !intercept;
1981                if (DBG) Slog.v(TAG, "canInterrupt=" + canInterrupt + " intercept=" + intercept);
1982                synchronized (mNotificationList) {
1983                    final StatusBarNotification n = new StatusBarNotification(
1984                            pkg, id, tag, callingUid, callingPid, score, notification, user);
1985                    NotificationRecord r = new NotificationRecord(n);
1986                    NotificationRecord old = null;
1987
1988                    int index = indexOfNotificationLocked(pkg, tag, id, userId);
1989                    if (index < 0) {
1990                        mNotificationList.add(r);
1991                        mUsageStats.registerPostedByApp(r);
1992                    } else {
1993                        old = mNotificationList.get(index);
1994                        mNotificationList.set(index, r);
1995                        mUsageStats.registerUpdatedByApp(r);
1996                        // Make sure we don't lose the foreground service state.
1997                        if (old != null) {
1998                            notification.flags |=
1999                                old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;
2000                        }
2001                    }
2002                    if (old != null) {
2003                        mNotificationsByKey.remove(old.sbn.getKey());
2004                    }
2005                    mNotificationsByKey.put(n.getKey(), r);
2006
2007                    // Ensure if this is a foreground service that the proper additional
2008                    // flags are set.
2009                    if ((notification.flags&Notification.FLAG_FOREGROUND_SERVICE) != 0) {
2010                        notification.flags |= Notification.FLAG_ONGOING_EVENT
2011                                | Notification.FLAG_NO_CLEAR;
2012                    }
2013
2014                    final int currentUser;
2015                    final long token = Binder.clearCallingIdentity();
2016                    try {
2017                        currentUser = ActivityManager.getCurrentUser();
2018                    } finally {
2019                        Binder.restoreCallingIdentity(token);
2020                    }
2021
2022                    if (notification.icon != 0) {
2023                        if (old != null && old.statusBarKey != null) {
2024                            r.statusBarKey = old.statusBarKey;
2025                            final long identity = Binder.clearCallingIdentity();
2026                            try {
2027                                mStatusBar.updateNotification(r.statusBarKey, n);
2028                            } finally {
2029                                Binder.restoreCallingIdentity(identity);
2030                            }
2031                        } else {
2032                            final long identity = Binder.clearCallingIdentity();
2033                            try {
2034                                r.statusBarKey = mStatusBar.addNotification(n);
2035                                if ((n.getNotification().flags & Notification.FLAG_SHOW_LIGHTS) != 0
2036                                        && canInterrupt) {
2037                                    mAttentionLight.pulse();
2038                                }
2039                            } finally {
2040                                Binder.restoreCallingIdentity(identity);
2041                            }
2042                        }
2043                        // Send accessibility events only for the current user.
2044                        if (currentUser == userId) {
2045                            sendAccessibilityEvent(notification, pkg);
2046                        }
2047
2048                        notifyPostedLocked(r);
2049                    } else {
2050                        Slog.e(TAG, "Not posting notification with icon==0: " + notification);
2051                        if (old != null && old.statusBarKey != null) {
2052                            final long identity = Binder.clearCallingIdentity();
2053                            try {
2054                                mStatusBar.removeNotification(old.statusBarKey);
2055                            } finally {
2056                                Binder.restoreCallingIdentity(identity);
2057                            }
2058
2059                            notifyRemovedLocked(r);
2060                        }
2061                        // ATTENTION: in a future release we will bail out here
2062                        // so that we do not play sounds, show lights, etc. for invalid
2063                        // notifications
2064                        Slog.e(TAG, "WARNING: In a future release this will crash the app: "
2065                                + n.getPackageName());
2066                    }
2067
2068                    // If we're not supposed to beep, vibrate, etc. then don't.
2069                    if (!mDisableNotificationAlerts
2070                            && (!(old != null
2071                                && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 ))
2072                            && (r.getUserId() == UserHandle.USER_ALL ||
2073                                (r.getUserId() == userId && r.getUserId() == currentUser) ||
2074                                isCurrentProfile(r.getUserId()))
2075                            && canInterrupt
2076                            && mSystemReady
2077                            && mAudioManager != null) {
2078                        if (DBG) Slog.v(TAG, "Interrupting!");
2079                        // sound
2080
2081                        // should we use the default notification sound? (indicated either by
2082                        // DEFAULT_SOUND or because notification.sound is pointing at
2083                        // Settings.System.NOTIFICATION_SOUND)
2084                        final boolean useDefaultSound =
2085                               (notification.defaults & Notification.DEFAULT_SOUND) != 0 ||
2086                                       Settings.System.DEFAULT_NOTIFICATION_URI
2087                                               .equals(notification.sound);
2088
2089                        Uri soundUri = null;
2090                        boolean hasValidSound = false;
2091
2092                        if (useDefaultSound) {
2093                            soundUri = Settings.System.DEFAULT_NOTIFICATION_URI;
2094
2095                            // check to see if the default notification sound is silent
2096                            ContentResolver resolver = getContext().getContentResolver();
2097                            hasValidSound = Settings.System.getString(resolver,
2098                                   Settings.System.NOTIFICATION_SOUND) != null;
2099                        } else if (notification.sound != null) {
2100                            soundUri = notification.sound;
2101                            hasValidSound = (soundUri != null);
2102                        }
2103
2104                        if (hasValidSound) {
2105                            boolean looping =
2106                                    (notification.flags & Notification.FLAG_INSISTENT) != 0;
2107                            int audioStreamType;
2108                            if (notification.audioStreamType >= 0) {
2109                                audioStreamType = notification.audioStreamType;
2110                            } else {
2111                                audioStreamType = DEFAULT_STREAM_TYPE;
2112                            }
2113                            mSoundNotification = r;
2114                            // do not play notifications if stream volume is 0 (typically because
2115                            // ringer mode is silent) or if there is a user of exclusive audio focus
2116                            if ((mAudioManager.getStreamVolume(audioStreamType) != 0)
2117                                    && !mAudioManager.isAudioFocusExclusive()) {
2118                                final long identity = Binder.clearCallingIdentity();
2119                                try {
2120                                    final IRingtonePlayer player =
2121                                            mAudioManager.getRingtonePlayer();
2122                                    if (player != null) {
2123                                        if (DBG) Slog.v(TAG, "Playing sound " + soundUri
2124                                                + " on stream " + audioStreamType);
2125                                        player.playAsync(soundUri, user, looping, audioStreamType);
2126                                    }
2127                                } catch (RemoteException e) {
2128                                } finally {
2129                                    Binder.restoreCallingIdentity(identity);
2130                                }
2131                            }
2132                        }
2133
2134                        // vibrate
2135                        // Does the notification want to specify its own vibration?
2136                        final boolean hasCustomVibrate = notification.vibrate != null;
2137
2138                        // new in 4.2: if there was supposed to be a sound and we're in vibrate
2139                        // mode, and no other vibration is specified, we fall back to vibration
2140                        final boolean convertSoundToVibration =
2141                                   !hasCustomVibrate
2142                                && hasValidSound
2143                                && (mAudioManager.getRingerMode()
2144                                           == AudioManager.RINGER_MODE_VIBRATE);
2145
2146                        // The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback.
2147                        final boolean useDefaultVibrate =
2148                                (notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
2149
2150                        if ((useDefaultVibrate || convertSoundToVibration || hasCustomVibrate)
2151                                && !(mAudioManager.getRingerMode()
2152                                        == AudioManager.RINGER_MODE_SILENT)) {
2153                            mVibrateNotification = r;
2154
2155                            if (useDefaultVibrate || convertSoundToVibration) {
2156                                // Escalate privileges so we can use the vibrator even if the
2157                                // notifying app does not have the VIBRATE permission.
2158                                long identity = Binder.clearCallingIdentity();
2159                                try {
2160                                    mVibrator.vibrate(r.sbn.getUid(), r.sbn.getBasePkg(),
2161                                        useDefaultVibrate ? mDefaultVibrationPattern
2162                                            : mFallbackVibrationPattern,
2163                                        ((notification.flags & Notification.FLAG_INSISTENT) != 0)
2164                                                ? 0: -1, notification.audioStreamType);
2165                                } finally {
2166                                    Binder.restoreCallingIdentity(identity);
2167                                }
2168                            } else if (notification.vibrate.length > 1) {
2169                                // If you want your own vibration pattern, you need the VIBRATE
2170                                // permission
2171                                mVibrator.vibrate(r.sbn.getUid(), r.sbn.getBasePkg(),
2172                                        notification.vibrate,
2173                                    ((notification.flags & Notification.FLAG_INSISTENT) != 0)
2174                                            ? 0: -1, notification.audioStreamType);
2175                            }
2176                        }
2177                    }
2178
2179                    // light
2180                    // the most recent thing gets the light
2181                    mLights.remove(old);
2182                    if (mLedNotification == old) {
2183                        mLedNotification = null;
2184                    }
2185                    //Slog.i(TAG, "notification.lights="
2186                    //        + ((old.notification.lights.flags & Notification.FLAG_SHOW_LIGHTS)
2187                    //                  != 0));
2188                    if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0
2189                            && canInterrupt) {
2190                        mLights.add(r);
2191                        updateLightsLocked();
2192                    } else {
2193                        if (old != null
2194                                && ((old.getFlags() & Notification.FLAG_SHOW_LIGHTS) != 0)) {
2195                            updateLightsLocked();
2196                        }
2197                    }
2198                }
2199            }
2200        });
2201
2202        idOut[0] = id;
2203    }
2204
2205     void registerListenerImpl(final INotificationListener listener,
2206            final ComponentName component, final int userid) {
2207        synchronized (mNotificationList) {
2208            try {
2209                NotificationListenerInfo info
2210                        = new NotificationListenerInfo(listener, component, userid,
2211                        /*isSystem*/ true, Build.VERSION_CODES.L);
2212                listener.asBinder().linkToDeath(info, 0);
2213                mListeners.add(info);
2214            } catch (RemoteException e) {
2215                // already dead
2216            }
2217        }
2218    }
2219
2220    /**
2221     * Removes a listener from the list and unbinds from its service.
2222     */
2223    void unregisterListenerImpl(final INotificationListener listener, final int userid) {
2224        NotificationListenerInfo info = removeListenerImpl(listener, userid);
2225        if (info != null && info.connection != null) {
2226            getContext().unbindService(info.connection);
2227        }
2228    }
2229
2230    /**
2231     * Removes a listener from the list but does not unbind from the listener's service.
2232     *
2233     * @return the removed listener.
2234     */
2235    NotificationListenerInfo removeListenerImpl(
2236            final INotificationListener listener, final int userid) {
2237        NotificationListenerInfo listenerInfo = null;
2238        synchronized (mNotificationList) {
2239            final int N = mListeners.size();
2240            for (int i=N-1; i>=0; i--) {
2241                final NotificationListenerInfo info = mListeners.get(i);
2242                if (info.listener.asBinder() == listener.asBinder()
2243                        && info.userid == userid) {
2244                    listenerInfo = mListeners.remove(i);
2245                }
2246            }
2247        }
2248        return listenerInfo;
2249    }
2250
2251    void showNextToastLocked() {
2252        ToastRecord record = mToastQueue.get(0);
2253        while (record != null) {
2254            if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback);
2255            try {
2256                record.callback.show();
2257                scheduleTimeoutLocked(record);
2258                return;
2259            } catch (RemoteException e) {
2260                Slog.w(TAG, "Object died trying to show notification " + record.callback
2261                        + " in package " + record.pkg);
2262                // remove it from the list and let the process die
2263                int index = mToastQueue.indexOf(record);
2264                if (index >= 0) {
2265                    mToastQueue.remove(index);
2266                }
2267                keepProcessAliveLocked(record.pid);
2268                if (mToastQueue.size() > 0) {
2269                    record = mToastQueue.get(0);
2270                } else {
2271                    record = null;
2272                }
2273            }
2274        }
2275    }
2276
2277    void cancelToastLocked(int index) {
2278        ToastRecord record = mToastQueue.get(index);
2279        try {
2280            record.callback.hide();
2281        } catch (RemoteException e) {
2282            Slog.w(TAG, "Object died trying to hide notification " + record.callback
2283                    + " in package " + record.pkg);
2284            // don't worry about this, we're about to remove it from
2285            // the list anyway
2286        }
2287        mToastQueue.remove(index);
2288        keepProcessAliveLocked(record.pid);
2289        if (mToastQueue.size() > 0) {
2290            // Show the next one. If the callback fails, this will remove
2291            // it from the list, so don't assume that the list hasn't changed
2292            // after this point.
2293            showNextToastLocked();
2294        }
2295    }
2296
2297    private void scheduleTimeoutLocked(ToastRecord r)
2298    {
2299        mHandler.removeCallbacksAndMessages(r);
2300        Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r);
2301        long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY;
2302        mHandler.sendMessageDelayed(m, delay);
2303    }
2304
2305    private void handleTimeout(ToastRecord record)
2306    {
2307        if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback);
2308        synchronized (mToastQueue) {
2309            int index = indexOfToastLocked(record.pkg, record.callback);
2310            if (index >= 0) {
2311                cancelToastLocked(index);
2312            }
2313        }
2314    }
2315
2316    // lock on mToastQueue
2317    int indexOfToastLocked(String pkg, ITransientNotification callback)
2318    {
2319        IBinder cbak = callback.asBinder();
2320        ArrayList<ToastRecord> list = mToastQueue;
2321        int len = list.size();
2322        for (int i=0; i<len; i++) {
2323            ToastRecord r = list.get(i);
2324            if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) {
2325                return i;
2326            }
2327        }
2328        return -1;
2329    }
2330
2331    // lock on mToastQueue
2332    void keepProcessAliveLocked(int pid)
2333    {
2334        int toastCount = 0; // toasts from this pid
2335        ArrayList<ToastRecord> list = mToastQueue;
2336        int N = list.size();
2337        for (int i=0; i<N; i++) {
2338            ToastRecord r = list.get(i);
2339            if (r.pid == pid) {
2340                toastCount++;
2341            }
2342        }
2343        try {
2344            mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0);
2345        } catch (RemoteException e) {
2346            // Shouldn't happen.
2347        }
2348    }
2349
2350    private final class WorkerHandler extends Handler
2351    {
2352        @Override
2353        public void handleMessage(Message msg)
2354        {
2355            switch (msg.what)
2356            {
2357                case MESSAGE_TIMEOUT:
2358                    handleTimeout((ToastRecord)msg.obj);
2359                    break;
2360            }
2361        }
2362    }
2363
2364
2365    // Notifications
2366    // ============================================================================
2367    static int clamp(int x, int low, int high) {
2368        return (x < low) ? low : ((x > high) ? high : x);
2369    }
2370
2371    void sendAccessibilityEvent(Notification notification, CharSequence packageName) {
2372        AccessibilityManager manager = AccessibilityManager.getInstance(getContext());
2373        if (!manager.isEnabled()) {
2374            return;
2375        }
2376
2377        AccessibilityEvent event =
2378            AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
2379        event.setPackageName(packageName);
2380        event.setClassName(Notification.class.getName());
2381        event.setParcelableData(notification);
2382        CharSequence tickerText = notification.tickerText;
2383        if (!TextUtils.isEmpty(tickerText)) {
2384            event.getText().add(tickerText);
2385        }
2386
2387        manager.sendAccessibilityEvent(event);
2388    }
2389
2390    private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason) {
2391        // tell the app
2392        if (sendDelete) {
2393            if (r.getNotification().deleteIntent != null) {
2394                try {
2395                    r.getNotification().deleteIntent.send();
2396                } catch (PendingIntent.CanceledException ex) {
2397                    // do nothing - there's no relevant way to recover, and
2398                    //     no reason to let this propagate
2399                    Slog.w(TAG, "canceled PendingIntent for " + r.sbn.getPackageName(), ex);
2400                }
2401            }
2402        }
2403
2404        // status bar
2405        if (r.getNotification().icon != 0) {
2406            final long identity = Binder.clearCallingIdentity();
2407            try {
2408                mStatusBar.removeNotification(r.statusBarKey);
2409            } finally {
2410                Binder.restoreCallingIdentity(identity);
2411            }
2412            r.statusBarKey = null;
2413            notifyRemovedLocked(r);
2414        }
2415
2416        // sound
2417        if (mSoundNotification == r) {
2418            mSoundNotification = null;
2419            final long identity = Binder.clearCallingIdentity();
2420            try {
2421                final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
2422                if (player != null) {
2423                    player.stopAsync();
2424                }
2425            } catch (RemoteException e) {
2426            } finally {
2427                Binder.restoreCallingIdentity(identity);
2428            }
2429        }
2430
2431        // vibrate
2432        if (mVibrateNotification == r) {
2433            mVibrateNotification = null;
2434            long identity = Binder.clearCallingIdentity();
2435            try {
2436                mVibrator.cancel();
2437            }
2438            finally {
2439                Binder.restoreCallingIdentity(identity);
2440            }
2441        }
2442
2443        // light
2444        mLights.remove(r);
2445        if (mLedNotification == r) {
2446            mLedNotification = null;
2447        }
2448
2449        // Record usage stats
2450        switch (reason) {
2451            case REASON_DELEGATE_CANCEL:
2452            case REASON_DELEGATE_CANCEL_ALL:
2453            case REASON_LISTENER_CANCEL:
2454            case REASON_LISTENER_CANCEL_ALL:
2455                mUsageStats.registerDismissedByUser(r);
2456                break;
2457            case REASON_NOMAN_CANCEL:
2458            case REASON_NOMAN_CANCEL_ALL:
2459                mUsageStats.registerRemovedByApp(r);
2460                break;
2461            case REASON_DELEGATE_CLICK:
2462                mUsageStats.registerCancelDueToClick(r);
2463                break;
2464            default:
2465                mUsageStats.registerCancelUnknown(r);
2466                break;
2467        }
2468
2469        // Save it for users of getHistoricalNotifications()
2470        mArchive.record(r.sbn);
2471    }
2472
2473    /**
2474     * Cancels a notification ONLY if it has all of the {@code mustHaveFlags}
2475     * and none of the {@code mustNotHaveFlags}.
2476     */
2477    void cancelNotification(final int callingUid, final int callingPid,
2478            final String pkg, final String tag, final int id,
2479            final int mustHaveFlags, final int mustNotHaveFlags, final boolean sendDelete,
2480            final int userId, final int reason, final NotificationListenerInfo listener) {
2481        // In enqueueNotificationInternal notifications are added by scheduling the
2482        // work on the worker handler. Hence, we also schedule the cancel on this
2483        // handler to avoid a scenario where an add notification call followed by a
2484        // remove notification call ends up in not removing the notification.
2485        mHandler.post(new Runnable() {
2486            @Override
2487            public void run() {
2488                EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, id, tag, userId,
2489                        mustHaveFlags, mustNotHaveFlags, reason,
2490                        listener == null ? null : listener.component.toShortString());
2491
2492                synchronized (mNotificationList) {
2493                    int index = indexOfNotificationLocked(pkg, tag, id, userId);
2494                    if (index >= 0) {
2495                        NotificationRecord r = mNotificationList.get(index);
2496
2497                        // Ideally we'd do this in the caller of this method. However, that would
2498                        // require the caller to also find the notification.
2499                        if (reason == REASON_DELEGATE_CLICK) {
2500                            mUsageStats.registerClickedByUser(r);
2501                        }
2502
2503                        if ((r.getNotification().flags & mustHaveFlags) != mustHaveFlags) {
2504                            return;
2505                        }
2506                        if ((r.getNotification().flags & mustNotHaveFlags) != 0) {
2507                            return;
2508                        }
2509
2510                        mNotificationList.remove(index);
2511                        mNotificationsByKey.remove(r.sbn.getKey());
2512
2513                        cancelNotificationLocked(r, sendDelete, reason);
2514                        updateLightsLocked();
2515                    }
2516                }
2517            }
2518        });
2519    }
2520
2521    /**
2522     * Determine whether the userId applies to the notification in question, either because
2523     * they match exactly, or one of them is USER_ALL (which is treated as a wildcard).
2524     */
2525    private boolean notificationMatchesUserId(NotificationRecord r, int userId) {
2526        return
2527                // looking for USER_ALL notifications? match everything
2528                   userId == UserHandle.USER_ALL
2529                // a notification sent to USER_ALL matches any query
2530                || r.getUserId() == UserHandle.USER_ALL
2531                // an exact user match
2532                || r.getUserId() == userId;
2533    }
2534
2535    /**
2536     * Determine whether the userId applies to the notification in question, either because
2537     * they match exactly, or one of them is USER_ALL (which is treated as a wildcard) or
2538     * because it matches one of the users profiles.
2539     */
2540    private boolean notificationMatchesCurrentProfiles(NotificationRecord r, int userId) {
2541        return notificationMatchesUserId(r, userId)
2542                || isCurrentProfile(r.getUserId());
2543    }
2544
2545    /**
2546     * Cancels all notifications from a given package that have all of the
2547     * {@code mustHaveFlags}.
2548     */
2549    boolean cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, int mustHaveFlags,
2550            int mustNotHaveFlags, boolean doit, int userId, int reason,
2551            NotificationListenerInfo listener) {
2552        EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
2553                pkg, userId, mustHaveFlags, mustNotHaveFlags, reason,
2554                listener == null ? null : listener.component.toShortString());
2555
2556        synchronized (mNotificationList) {
2557            final int N = mNotificationList.size();
2558            boolean canceledSomething = false;
2559            for (int i = N-1; i >= 0; --i) {
2560                NotificationRecord r = mNotificationList.get(i);
2561                if (!notificationMatchesUserId(r, userId)) {
2562                    continue;
2563                }
2564                // Don't remove notifications to all, if there's no package name specified
2565                if (r.getUserId() == UserHandle.USER_ALL && pkg == null) {
2566                    continue;
2567                }
2568                if ((r.getFlags() & mustHaveFlags) != mustHaveFlags) {
2569                    continue;
2570                }
2571                if ((r.getFlags() & mustNotHaveFlags) != 0) {
2572                    continue;
2573                }
2574                if (pkg != null && !r.sbn.getPackageName().equals(pkg)) {
2575                    continue;
2576                }
2577                canceledSomething = true;
2578                if (!doit) {
2579                    return true;
2580                }
2581                mNotificationList.remove(i);
2582                mNotificationsByKey.remove(r.sbn.getKey());
2583                cancelNotificationLocked(r, false, reason);
2584            }
2585            if (canceledSomething) {
2586                updateLightsLocked();
2587            }
2588            return canceledSomething;
2589        }
2590    }
2591
2592
2593
2594    // Return true if the UID is a system or phone UID and therefore should not have
2595    // any notifications or toasts blocked.
2596    boolean isUidSystem(int uid) {
2597        final int appid = UserHandle.getAppId(uid);
2598        return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0);
2599    }
2600
2601    // same as isUidSystem(int, int) for the Binder caller's UID.
2602    boolean isCallerSystem() {
2603        return isUidSystem(Binder.getCallingUid());
2604    }
2605
2606    void checkCallerIsSystem() {
2607        if (isCallerSystem()) {
2608            return;
2609        }
2610        throw new SecurityException("Disallowed call for uid " + Binder.getCallingUid());
2611    }
2612
2613    void checkCallerIsSystemOrSameApp(String pkg) {
2614        if (isCallerSystem()) {
2615            return;
2616        }
2617        final int uid = Binder.getCallingUid();
2618        try {
2619            ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo(
2620                    pkg, 0, UserHandle.getCallingUserId());
2621            if (!UserHandle.isSameApp(ai.uid, uid)) {
2622                throw new SecurityException("Calling uid " + uid + " gave package"
2623                        + pkg + " which is owned by uid " + ai.uid);
2624            }
2625        } catch (RemoteException re) {
2626            throw new SecurityException("Unknown package " + pkg + "\n" + re);
2627        }
2628    }
2629
2630    void cancelAllLocked(int callingUid, int callingPid, int userId, int reason,
2631            NotificationListenerInfo listener, boolean includeCurrentProfiles) {
2632        EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
2633                null, userId, 0, 0, reason,
2634                listener == null ? null : listener.component.toShortString());
2635
2636        final int N = mNotificationList.size();
2637        for (int i=N-1; i>=0; i--) {
2638            NotificationRecord r = mNotificationList.get(i);
2639            if (includeCurrentProfiles) {
2640                if (!notificationMatchesCurrentProfiles(r, userId)) {
2641                    continue;
2642                }
2643            } else {
2644                if (!notificationMatchesUserId(r, userId)) {
2645                    continue;
2646                }
2647            }
2648
2649            if ((r.getFlags() & (Notification.FLAG_ONGOING_EVENT
2650                            | Notification.FLAG_NO_CLEAR)) == 0) {
2651                mNotificationList.remove(i);
2652                mNotificationsByKey.remove(r.sbn.getKey());
2653                cancelNotificationLocked(r, true, reason);
2654            }
2655        }
2656        updateLightsLocked();
2657    }
2658
2659    // lock on mNotificationList
2660    void updateLightsLocked()
2661    {
2662        // handle notification lights
2663        if (mLedNotification == null) {
2664            // get next notification, if any
2665            int n = mLights.size();
2666            if (n > 0) {
2667                mLedNotification = mLights.get(n-1);
2668            }
2669        }
2670
2671        // Don't flash while we are in a call or screen is on
2672        if (mLedNotification == null || mInCall || mScreenOn) {
2673            mNotificationLight.turnOff();
2674        } else {
2675            final Notification ledno = mLedNotification.sbn.getNotification();
2676            int ledARGB = ledno.ledARGB;
2677            int ledOnMS = ledno.ledOnMS;
2678            int ledOffMS = ledno.ledOffMS;
2679            if ((ledno.defaults & Notification.DEFAULT_LIGHTS) != 0) {
2680                ledARGB = mDefaultNotificationColor;
2681                ledOnMS = mDefaultNotificationLedOn;
2682                ledOffMS = mDefaultNotificationLedOff;
2683            }
2684            if (mNotificationPulseEnabled) {
2685                // pulse repeatedly
2686                mNotificationLight.setFlashing(ledARGB, Light.LIGHT_FLASH_TIMED,
2687                        ledOnMS, ledOffMS);
2688            }
2689        }
2690    }
2691
2692    // lock on mNotificationList
2693    int indexOfNotificationLocked(String pkg, String tag, int id, int userId)
2694    {
2695        ArrayList<NotificationRecord> list = mNotificationList;
2696        final int len = list.size();
2697        for (int i=0; i<len; i++) {
2698            NotificationRecord r = list.get(i);
2699            if (!notificationMatchesUserId(r, userId) || r.sbn.getId() != id) {
2700                continue;
2701            }
2702            if (tag == null) {
2703                if (r.sbn.getTag() != null) {
2704                    continue;
2705                }
2706            } else {
2707                if (!tag.equals(r.sbn.getTag())) {
2708                    continue;
2709                }
2710            }
2711            if (r.sbn.getPackageName().equals(pkg)) {
2712                return i;
2713            }
2714        }
2715        return -1;
2716    }
2717
2718    private void updateNotificationPulse() {
2719        synchronized (mNotificationList) {
2720            updateLightsLocked();
2721        }
2722    }
2723
2724    private void updateZenMode() {
2725        final int mode = Settings.Global.getInt(getContext().getContentResolver(),
2726                Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF);
2727        if (mode != mZenMode) {
2728            Slog.d(TAG, String.format("updateZenMode: %s -> %s",
2729                    Settings.Global.zenModeToString(mZenMode),
2730                    Settings.Global.zenModeToString(mode)));
2731        }
2732        mZenMode = mode;
2733
2734        final String[] exceptionPackages = null; // none (for now)
2735
2736        // call restrictions
2737        final boolean muteCalls = mZenMode != Settings.Global.ZEN_MODE_OFF;
2738        mAppOps.setRestriction(AppOpsManager.OP_VIBRATE, AudioManager.STREAM_RING,
2739                muteCalls ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
2740                exceptionPackages);
2741        mAppOps.setRestriction(AppOpsManager.OP_PLAY_AUDIO, AudioManager.STREAM_RING,
2742                muteCalls ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
2743                exceptionPackages);
2744
2745        // alarm restrictions
2746        final boolean muteAlarms = false; // TODO until we save user config
2747        mAppOps.setRestriction(AppOpsManager.OP_VIBRATE, AudioManager.STREAM_ALARM,
2748                muteAlarms ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
2749                exceptionPackages);
2750        mAppOps.setRestriction(AppOpsManager.OP_PLAY_AUDIO, AudioManager.STREAM_ALARM,
2751                muteAlarms ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
2752                exceptionPackages);
2753
2754        // restrict vibrations with no hints
2755        mAppOps.setRestriction(AppOpsManager.OP_VIBRATE, AudioManager.USE_DEFAULT_STREAM_TYPE,
2756                (muteAlarms || muteCalls) ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
2757                exceptionPackages);
2758    }
2759
2760    private void updateCurrentProfilesCache(Context context) {
2761        UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
2762        if (userManager != null) {
2763            int currentUserId = ActivityManager.getCurrentUser();
2764            List<UserInfo> profiles = userManager.getProfiles(currentUserId);
2765            synchronized (mCurrentProfiles) {
2766                mCurrentProfiles.clear();
2767                for (UserInfo user : profiles) {
2768                    mCurrentProfiles.put(user.id, user);
2769                }
2770            }
2771        }
2772    }
2773
2774    private int[] getCurrentProfileIds() {
2775        synchronized (mCurrentProfiles) {
2776            int[] users = new int[mCurrentProfiles.size()];
2777            final int N = mCurrentProfiles.size();
2778            for (int i = 0; i < N; ++i) {
2779                users[i] = mCurrentProfiles.keyAt(i);
2780            }
2781            return users;
2782        }
2783    }
2784
2785    private boolean isCurrentProfile(int userId) {
2786        synchronized (mCurrentProfiles) {
2787            return mCurrentProfiles.get(userId) != null;
2788        }
2789    }
2790
2791    private boolean isCall(String pkg, Notification n) {
2792        return CALL_PACKAGES.contains(pkg);
2793    }
2794
2795    private boolean isAlarm(String pkg, Notification n) {
2796        return ALARM_PACKAGES.contains(pkg);
2797    }
2798
2799    private boolean shouldIntercept(String pkg, Notification n) {
2800        if (mZenMode != Settings.Global.ZEN_MODE_OFF) {
2801            return !isAlarm(pkg, n);
2802        }
2803        return false;
2804    }
2805}
2806