NotificationManagerService.java revision 8fd7f1ed7c11d35b3f2a97878e68ee38a551dd15
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 opPkg, String tag, int id,
1501                Notification notification, int[] idOut, int userId) throws RemoteException {
1502            enqueueNotificationInternal(pkg, opPkg, 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 opPkg, int callingUid, int callingPid,
1853                String tag, int id, Notification notification, int[] idReceived, int userId) {
1854            enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, notification,
1855                    idReceived, userId);
1856        }
1857    };
1858
1859    void enqueueNotificationInternal(final String pkg, final String opPkg, 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, opPkg, id, tag, callingUid, callingPid, score, notification,
1985                            user);
1986                    NotificationRecord r = new NotificationRecord(n);
1987                    NotificationRecord old = null;
1988
1989                    int index = indexOfNotificationLocked(pkg, tag, id, userId);
1990                    if (index < 0) {
1991                        mNotificationList.add(r);
1992                        mUsageStats.registerPostedByApp(r);
1993                    } else {
1994                        old = mNotificationList.get(index);
1995                        mNotificationList.set(index, r);
1996                        mUsageStats.registerUpdatedByApp(r);
1997                        // Make sure we don't lose the foreground service state.
1998                        if (old != null) {
1999                            notification.flags |=
2000                                old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;
2001                        }
2002                    }
2003                    if (old != null) {
2004                        mNotificationsByKey.remove(old.sbn.getKey());
2005                    }
2006                    mNotificationsByKey.put(n.getKey(), r);
2007
2008                    // Ensure if this is a foreground service that the proper additional
2009                    // flags are set.
2010                    if ((notification.flags&Notification.FLAG_FOREGROUND_SERVICE) != 0) {
2011                        notification.flags |= Notification.FLAG_ONGOING_EVENT
2012                                | Notification.FLAG_NO_CLEAR;
2013                    }
2014
2015                    final int currentUser;
2016                    final long token = Binder.clearCallingIdentity();
2017                    try {
2018                        currentUser = ActivityManager.getCurrentUser();
2019                    } finally {
2020                        Binder.restoreCallingIdentity(token);
2021                    }
2022
2023                    if (notification.icon != 0) {
2024                        if (old != null && old.statusBarKey != null) {
2025                            r.statusBarKey = old.statusBarKey;
2026                            final long identity = Binder.clearCallingIdentity();
2027                            try {
2028                                mStatusBar.updateNotification(r.statusBarKey, n);
2029                            } finally {
2030                                Binder.restoreCallingIdentity(identity);
2031                            }
2032                        } else {
2033                            final long identity = Binder.clearCallingIdentity();
2034                            try {
2035                                r.statusBarKey = mStatusBar.addNotification(n);
2036                                if ((n.getNotification().flags & Notification.FLAG_SHOW_LIGHTS) != 0
2037                                        && canInterrupt) {
2038                                    mAttentionLight.pulse();
2039                                }
2040                            } finally {
2041                                Binder.restoreCallingIdentity(identity);
2042                            }
2043                        }
2044                        // Send accessibility events only for the current user.
2045                        if (currentUser == userId) {
2046                            sendAccessibilityEvent(notification, pkg);
2047                        }
2048
2049                        notifyPostedLocked(r);
2050                    } else {
2051                        Slog.e(TAG, "Not posting notification with icon==0: " + notification);
2052                        if (old != null && old.statusBarKey != null) {
2053                            final long identity = Binder.clearCallingIdentity();
2054                            try {
2055                                mStatusBar.removeNotification(old.statusBarKey);
2056                            } finally {
2057                                Binder.restoreCallingIdentity(identity);
2058                            }
2059
2060                            notifyRemovedLocked(r);
2061                        }
2062                        // ATTENTION: in a future release we will bail out here
2063                        // so that we do not play sounds, show lights, etc. for invalid
2064                        // notifications
2065                        Slog.e(TAG, "WARNING: In a future release this will crash the app: "
2066                                + n.getPackageName());
2067                    }
2068
2069                    // If we're not supposed to beep, vibrate, etc. then don't.
2070                    if (!mDisableNotificationAlerts
2071                            && (!(old != null
2072                                && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 ))
2073                            && (r.getUserId() == UserHandle.USER_ALL ||
2074                                (r.getUserId() == userId && r.getUserId() == currentUser) ||
2075                                isCurrentProfile(r.getUserId()))
2076                            && canInterrupt
2077                            && mSystemReady
2078                            && mAudioManager != null) {
2079                        if (DBG) Slog.v(TAG, "Interrupting!");
2080                        // sound
2081
2082                        // should we use the default notification sound? (indicated either by
2083                        // DEFAULT_SOUND or because notification.sound is pointing at
2084                        // Settings.System.NOTIFICATION_SOUND)
2085                        final boolean useDefaultSound =
2086                               (notification.defaults & Notification.DEFAULT_SOUND) != 0 ||
2087                                       Settings.System.DEFAULT_NOTIFICATION_URI
2088                                               .equals(notification.sound);
2089
2090                        Uri soundUri = null;
2091                        boolean hasValidSound = false;
2092
2093                        if (useDefaultSound) {
2094                            soundUri = Settings.System.DEFAULT_NOTIFICATION_URI;
2095
2096                            // check to see if the default notification sound is silent
2097                            ContentResolver resolver = getContext().getContentResolver();
2098                            hasValidSound = Settings.System.getString(resolver,
2099                                   Settings.System.NOTIFICATION_SOUND) != null;
2100                        } else if (notification.sound != null) {
2101                            soundUri = notification.sound;
2102                            hasValidSound = (soundUri != null);
2103                        }
2104
2105                        if (hasValidSound) {
2106                            boolean looping =
2107                                    (notification.flags & Notification.FLAG_INSISTENT) != 0;
2108                            int audioStreamType;
2109                            if (notification.audioStreamType >= 0) {
2110                                audioStreamType = notification.audioStreamType;
2111                            } else {
2112                                audioStreamType = DEFAULT_STREAM_TYPE;
2113                            }
2114                            mSoundNotification = r;
2115                            // do not play notifications if stream volume is 0 (typically because
2116                            // ringer mode is silent) or if there is a user of exclusive audio focus
2117                            if ((mAudioManager.getStreamVolume(audioStreamType) != 0)
2118                                    && !mAudioManager.isAudioFocusExclusive()) {
2119                                final long identity = Binder.clearCallingIdentity();
2120                                try {
2121                                    final IRingtonePlayer player =
2122                                            mAudioManager.getRingtonePlayer();
2123                                    if (player != null) {
2124                                        if (DBG) Slog.v(TAG, "Playing sound " + soundUri
2125                                                + " on stream " + audioStreamType);
2126                                        player.playAsync(soundUri, user, looping, audioStreamType);
2127                                    }
2128                                } catch (RemoteException e) {
2129                                } finally {
2130                                    Binder.restoreCallingIdentity(identity);
2131                                }
2132                            }
2133                        }
2134
2135                        // vibrate
2136                        // Does the notification want to specify its own vibration?
2137                        final boolean hasCustomVibrate = notification.vibrate != null;
2138
2139                        // new in 4.2: if there was supposed to be a sound and we're in vibrate
2140                        // mode, and no other vibration is specified, we fall back to vibration
2141                        final boolean convertSoundToVibration =
2142                                   !hasCustomVibrate
2143                                && hasValidSound
2144                                && (mAudioManager.getRingerMode()
2145                                           == AudioManager.RINGER_MODE_VIBRATE);
2146
2147                        // The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback.
2148                        final boolean useDefaultVibrate =
2149                                (notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
2150
2151                        if ((useDefaultVibrate || convertSoundToVibration || hasCustomVibrate)
2152                                && !(mAudioManager.getRingerMode()
2153                                        == AudioManager.RINGER_MODE_SILENT)) {
2154                            mVibrateNotification = r;
2155
2156                            if (useDefaultVibrate || convertSoundToVibration) {
2157                                // Escalate privileges so we can use the vibrator even if the
2158                                // notifying app does not have the VIBRATE permission.
2159                                long identity = Binder.clearCallingIdentity();
2160                                try {
2161                                    mVibrator.vibrate(r.sbn.getUid(), r.sbn.getOpPkg(),
2162                                        useDefaultVibrate ? mDefaultVibrationPattern
2163                                            : mFallbackVibrationPattern,
2164                                        ((notification.flags & Notification.FLAG_INSISTENT) != 0)
2165                                                ? 0: -1, notification.audioStreamType);
2166                                } finally {
2167                                    Binder.restoreCallingIdentity(identity);
2168                                }
2169                            } else if (notification.vibrate.length > 1) {
2170                                // If you want your own vibration pattern, you need the VIBRATE
2171                                // permission
2172                                mVibrator.vibrate(r.sbn.getUid(), r.sbn.getOpPkg(),
2173                                        notification.vibrate,
2174                                    ((notification.flags & Notification.FLAG_INSISTENT) != 0)
2175                                            ? 0: -1, notification.audioStreamType);
2176                            }
2177                        }
2178                    }
2179
2180                    // light
2181                    // the most recent thing gets the light
2182                    mLights.remove(old);
2183                    if (mLedNotification == old) {
2184                        mLedNotification = null;
2185                    }
2186                    //Slog.i(TAG, "notification.lights="
2187                    //        + ((old.notification.lights.flags & Notification.FLAG_SHOW_LIGHTS)
2188                    //                  != 0));
2189                    if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0
2190                            && canInterrupt) {
2191                        mLights.add(r);
2192                        updateLightsLocked();
2193                    } else {
2194                        if (old != null
2195                                && ((old.getFlags() & Notification.FLAG_SHOW_LIGHTS) != 0)) {
2196                            updateLightsLocked();
2197                        }
2198                    }
2199                }
2200            }
2201        });
2202
2203        idOut[0] = id;
2204    }
2205
2206     void registerListenerImpl(final INotificationListener listener,
2207            final ComponentName component, final int userid) {
2208        synchronized (mNotificationList) {
2209            try {
2210                NotificationListenerInfo info
2211                        = new NotificationListenerInfo(listener, component, userid,
2212                        /*isSystem*/ true, Build.VERSION_CODES.L);
2213                listener.asBinder().linkToDeath(info, 0);
2214                mListeners.add(info);
2215            } catch (RemoteException e) {
2216                // already dead
2217            }
2218        }
2219    }
2220
2221    /**
2222     * Removes a listener from the list and unbinds from its service.
2223     */
2224    void unregisterListenerImpl(final INotificationListener listener, final int userid) {
2225        NotificationListenerInfo info = removeListenerImpl(listener, userid);
2226        if (info != null && info.connection != null) {
2227            getContext().unbindService(info.connection);
2228        }
2229    }
2230
2231    /**
2232     * Removes a listener from the list but does not unbind from the listener's service.
2233     *
2234     * @return the removed listener.
2235     */
2236    NotificationListenerInfo removeListenerImpl(
2237            final INotificationListener listener, final int userid) {
2238        NotificationListenerInfo listenerInfo = null;
2239        synchronized (mNotificationList) {
2240            final int N = mListeners.size();
2241            for (int i=N-1; i>=0; i--) {
2242                final NotificationListenerInfo info = mListeners.get(i);
2243                if (info.listener.asBinder() == listener.asBinder()
2244                        && info.userid == userid) {
2245                    listenerInfo = mListeners.remove(i);
2246                }
2247            }
2248        }
2249        return listenerInfo;
2250    }
2251
2252    void showNextToastLocked() {
2253        ToastRecord record = mToastQueue.get(0);
2254        while (record != null) {
2255            if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback);
2256            try {
2257                record.callback.show();
2258                scheduleTimeoutLocked(record);
2259                return;
2260            } catch (RemoteException e) {
2261                Slog.w(TAG, "Object died trying to show notification " + record.callback
2262                        + " in package " + record.pkg);
2263                // remove it from the list and let the process die
2264                int index = mToastQueue.indexOf(record);
2265                if (index >= 0) {
2266                    mToastQueue.remove(index);
2267                }
2268                keepProcessAliveLocked(record.pid);
2269                if (mToastQueue.size() > 0) {
2270                    record = mToastQueue.get(0);
2271                } else {
2272                    record = null;
2273                }
2274            }
2275        }
2276    }
2277
2278    void cancelToastLocked(int index) {
2279        ToastRecord record = mToastQueue.get(index);
2280        try {
2281            record.callback.hide();
2282        } catch (RemoteException e) {
2283            Slog.w(TAG, "Object died trying to hide notification " + record.callback
2284                    + " in package " + record.pkg);
2285            // don't worry about this, we're about to remove it from
2286            // the list anyway
2287        }
2288        mToastQueue.remove(index);
2289        keepProcessAliveLocked(record.pid);
2290        if (mToastQueue.size() > 0) {
2291            // Show the next one. If the callback fails, this will remove
2292            // it from the list, so don't assume that the list hasn't changed
2293            // after this point.
2294            showNextToastLocked();
2295        }
2296    }
2297
2298    private void scheduleTimeoutLocked(ToastRecord r)
2299    {
2300        mHandler.removeCallbacksAndMessages(r);
2301        Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r);
2302        long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY;
2303        mHandler.sendMessageDelayed(m, delay);
2304    }
2305
2306    private void handleTimeout(ToastRecord record)
2307    {
2308        if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback);
2309        synchronized (mToastQueue) {
2310            int index = indexOfToastLocked(record.pkg, record.callback);
2311            if (index >= 0) {
2312                cancelToastLocked(index);
2313            }
2314        }
2315    }
2316
2317    // lock on mToastQueue
2318    int indexOfToastLocked(String pkg, ITransientNotification callback)
2319    {
2320        IBinder cbak = callback.asBinder();
2321        ArrayList<ToastRecord> list = mToastQueue;
2322        int len = list.size();
2323        for (int i=0; i<len; i++) {
2324            ToastRecord r = list.get(i);
2325            if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) {
2326                return i;
2327            }
2328        }
2329        return -1;
2330    }
2331
2332    // lock on mToastQueue
2333    void keepProcessAliveLocked(int pid)
2334    {
2335        int toastCount = 0; // toasts from this pid
2336        ArrayList<ToastRecord> list = mToastQueue;
2337        int N = list.size();
2338        for (int i=0; i<N; i++) {
2339            ToastRecord r = list.get(i);
2340            if (r.pid == pid) {
2341                toastCount++;
2342            }
2343        }
2344        try {
2345            mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0);
2346        } catch (RemoteException e) {
2347            // Shouldn't happen.
2348        }
2349    }
2350
2351    private final class WorkerHandler extends Handler
2352    {
2353        @Override
2354        public void handleMessage(Message msg)
2355        {
2356            switch (msg.what)
2357            {
2358                case MESSAGE_TIMEOUT:
2359                    handleTimeout((ToastRecord)msg.obj);
2360                    break;
2361            }
2362        }
2363    }
2364
2365
2366    // Notifications
2367    // ============================================================================
2368    static int clamp(int x, int low, int high) {
2369        return (x < low) ? low : ((x > high) ? high : x);
2370    }
2371
2372    void sendAccessibilityEvent(Notification notification, CharSequence packageName) {
2373        AccessibilityManager manager = AccessibilityManager.getInstance(getContext());
2374        if (!manager.isEnabled()) {
2375            return;
2376        }
2377
2378        AccessibilityEvent event =
2379            AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
2380        event.setPackageName(packageName);
2381        event.setClassName(Notification.class.getName());
2382        event.setParcelableData(notification);
2383        CharSequence tickerText = notification.tickerText;
2384        if (!TextUtils.isEmpty(tickerText)) {
2385            event.getText().add(tickerText);
2386        }
2387
2388        manager.sendAccessibilityEvent(event);
2389    }
2390
2391    private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason) {
2392        // tell the app
2393        if (sendDelete) {
2394            if (r.getNotification().deleteIntent != null) {
2395                try {
2396                    r.getNotification().deleteIntent.send();
2397                } catch (PendingIntent.CanceledException ex) {
2398                    // do nothing - there's no relevant way to recover, and
2399                    //     no reason to let this propagate
2400                    Slog.w(TAG, "canceled PendingIntent for " + r.sbn.getPackageName(), ex);
2401                }
2402            }
2403        }
2404
2405        // status bar
2406        if (r.getNotification().icon != 0) {
2407            final long identity = Binder.clearCallingIdentity();
2408            try {
2409                mStatusBar.removeNotification(r.statusBarKey);
2410            } finally {
2411                Binder.restoreCallingIdentity(identity);
2412            }
2413            r.statusBarKey = null;
2414            notifyRemovedLocked(r);
2415        }
2416
2417        // sound
2418        if (mSoundNotification == r) {
2419            mSoundNotification = null;
2420            final long identity = Binder.clearCallingIdentity();
2421            try {
2422                final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
2423                if (player != null) {
2424                    player.stopAsync();
2425                }
2426            } catch (RemoteException e) {
2427            } finally {
2428                Binder.restoreCallingIdentity(identity);
2429            }
2430        }
2431
2432        // vibrate
2433        if (mVibrateNotification == r) {
2434            mVibrateNotification = null;
2435            long identity = Binder.clearCallingIdentity();
2436            try {
2437                mVibrator.cancel();
2438            }
2439            finally {
2440                Binder.restoreCallingIdentity(identity);
2441            }
2442        }
2443
2444        // light
2445        mLights.remove(r);
2446        if (mLedNotification == r) {
2447            mLedNotification = null;
2448        }
2449
2450        // Record usage stats
2451        switch (reason) {
2452            case REASON_DELEGATE_CANCEL:
2453            case REASON_DELEGATE_CANCEL_ALL:
2454            case REASON_LISTENER_CANCEL:
2455            case REASON_LISTENER_CANCEL_ALL:
2456                mUsageStats.registerDismissedByUser(r);
2457                break;
2458            case REASON_NOMAN_CANCEL:
2459            case REASON_NOMAN_CANCEL_ALL:
2460                mUsageStats.registerRemovedByApp(r);
2461                break;
2462            case REASON_DELEGATE_CLICK:
2463                mUsageStats.registerCancelDueToClick(r);
2464                break;
2465            default:
2466                mUsageStats.registerCancelUnknown(r);
2467                break;
2468        }
2469
2470        // Save it for users of getHistoricalNotifications()
2471        mArchive.record(r.sbn);
2472    }
2473
2474    /**
2475     * Cancels a notification ONLY if it has all of the {@code mustHaveFlags}
2476     * and none of the {@code mustNotHaveFlags}.
2477     */
2478    void cancelNotification(final int callingUid, final int callingPid,
2479            final String pkg, final String tag, final int id,
2480            final int mustHaveFlags, final int mustNotHaveFlags, final boolean sendDelete,
2481            final int userId, final int reason, final NotificationListenerInfo listener) {
2482        // In enqueueNotificationInternal notifications are added by scheduling the
2483        // work on the worker handler. Hence, we also schedule the cancel on this
2484        // handler to avoid a scenario where an add notification call followed by a
2485        // remove notification call ends up in not removing the notification.
2486        mHandler.post(new Runnable() {
2487            @Override
2488            public void run() {
2489                EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, id, tag, userId,
2490                        mustHaveFlags, mustNotHaveFlags, reason,
2491                        listener == null ? null : listener.component.toShortString());
2492
2493                synchronized (mNotificationList) {
2494                    int index = indexOfNotificationLocked(pkg, tag, id, userId);
2495                    if (index >= 0) {
2496                        NotificationRecord r = mNotificationList.get(index);
2497
2498                        // Ideally we'd do this in the caller of this method. However, that would
2499                        // require the caller to also find the notification.
2500                        if (reason == REASON_DELEGATE_CLICK) {
2501                            mUsageStats.registerClickedByUser(r);
2502                        }
2503
2504                        if ((r.getNotification().flags & mustHaveFlags) != mustHaveFlags) {
2505                            return;
2506                        }
2507                        if ((r.getNotification().flags & mustNotHaveFlags) != 0) {
2508                            return;
2509                        }
2510
2511                        mNotificationList.remove(index);
2512                        mNotificationsByKey.remove(r.sbn.getKey());
2513
2514                        cancelNotificationLocked(r, sendDelete, reason);
2515                        updateLightsLocked();
2516                    }
2517                }
2518            }
2519        });
2520    }
2521
2522    /**
2523     * Determine whether the userId applies to the notification in question, either because
2524     * they match exactly, or one of them is USER_ALL (which is treated as a wildcard).
2525     */
2526    private boolean notificationMatchesUserId(NotificationRecord r, int userId) {
2527        return
2528                // looking for USER_ALL notifications? match everything
2529                   userId == UserHandle.USER_ALL
2530                // a notification sent to USER_ALL matches any query
2531                || r.getUserId() == UserHandle.USER_ALL
2532                // an exact user match
2533                || r.getUserId() == userId;
2534    }
2535
2536    /**
2537     * Determine whether the userId applies to the notification in question, either because
2538     * they match exactly, or one of them is USER_ALL (which is treated as a wildcard) or
2539     * because it matches one of the users profiles.
2540     */
2541    private boolean notificationMatchesCurrentProfiles(NotificationRecord r, int userId) {
2542        return notificationMatchesUserId(r, userId)
2543                || isCurrentProfile(r.getUserId());
2544    }
2545
2546    /**
2547     * Cancels all notifications from a given package that have all of the
2548     * {@code mustHaveFlags}.
2549     */
2550    boolean cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, int mustHaveFlags,
2551            int mustNotHaveFlags, boolean doit, int userId, int reason,
2552            NotificationListenerInfo listener) {
2553        EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
2554                pkg, userId, mustHaveFlags, mustNotHaveFlags, reason,
2555                listener == null ? null : listener.component.toShortString());
2556
2557        synchronized (mNotificationList) {
2558            final int N = mNotificationList.size();
2559            boolean canceledSomething = false;
2560            for (int i = N-1; i >= 0; --i) {
2561                NotificationRecord r = mNotificationList.get(i);
2562                if (!notificationMatchesUserId(r, userId)) {
2563                    continue;
2564                }
2565                // Don't remove notifications to all, if there's no package name specified
2566                if (r.getUserId() == UserHandle.USER_ALL && pkg == null) {
2567                    continue;
2568                }
2569                if ((r.getFlags() & mustHaveFlags) != mustHaveFlags) {
2570                    continue;
2571                }
2572                if ((r.getFlags() & mustNotHaveFlags) != 0) {
2573                    continue;
2574                }
2575                if (pkg != null && !r.sbn.getPackageName().equals(pkg)) {
2576                    continue;
2577                }
2578                canceledSomething = true;
2579                if (!doit) {
2580                    return true;
2581                }
2582                mNotificationList.remove(i);
2583                mNotificationsByKey.remove(r.sbn.getKey());
2584                cancelNotificationLocked(r, false, reason);
2585            }
2586            if (canceledSomething) {
2587                updateLightsLocked();
2588            }
2589            return canceledSomething;
2590        }
2591    }
2592
2593
2594
2595    // Return true if the UID is a system or phone UID and therefore should not have
2596    // any notifications or toasts blocked.
2597    boolean isUidSystem(int uid) {
2598        final int appid = UserHandle.getAppId(uid);
2599        return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0);
2600    }
2601
2602    // same as isUidSystem(int, int) for the Binder caller's UID.
2603    boolean isCallerSystem() {
2604        return isUidSystem(Binder.getCallingUid());
2605    }
2606
2607    void checkCallerIsSystem() {
2608        if (isCallerSystem()) {
2609            return;
2610        }
2611        throw new SecurityException("Disallowed call for uid " + Binder.getCallingUid());
2612    }
2613
2614    void checkCallerIsSystemOrSameApp(String pkg) {
2615        if (isCallerSystem()) {
2616            return;
2617        }
2618        final int uid = Binder.getCallingUid();
2619        try {
2620            ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo(
2621                    pkg, 0, UserHandle.getCallingUserId());
2622            if (!UserHandle.isSameApp(ai.uid, uid)) {
2623                throw new SecurityException("Calling uid " + uid + " gave package"
2624                        + pkg + " which is owned by uid " + ai.uid);
2625            }
2626        } catch (RemoteException re) {
2627            throw new SecurityException("Unknown package " + pkg + "\n" + re);
2628        }
2629    }
2630
2631    void cancelAllLocked(int callingUid, int callingPid, int userId, int reason,
2632            NotificationListenerInfo listener, boolean includeCurrentProfiles) {
2633        EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
2634                null, userId, 0, 0, reason,
2635                listener == null ? null : listener.component.toShortString());
2636
2637        final int N = mNotificationList.size();
2638        for (int i=N-1; i>=0; i--) {
2639            NotificationRecord r = mNotificationList.get(i);
2640            if (includeCurrentProfiles) {
2641                if (!notificationMatchesCurrentProfiles(r, userId)) {
2642                    continue;
2643                }
2644            } else {
2645                if (!notificationMatchesUserId(r, userId)) {
2646                    continue;
2647                }
2648            }
2649
2650            if ((r.getFlags() & (Notification.FLAG_ONGOING_EVENT
2651                            | Notification.FLAG_NO_CLEAR)) == 0) {
2652                mNotificationList.remove(i);
2653                mNotificationsByKey.remove(r.sbn.getKey());
2654                cancelNotificationLocked(r, true, reason);
2655            }
2656        }
2657        updateLightsLocked();
2658    }
2659
2660    // lock on mNotificationList
2661    void updateLightsLocked()
2662    {
2663        // handle notification lights
2664        if (mLedNotification == null) {
2665            // get next notification, if any
2666            int n = mLights.size();
2667            if (n > 0) {
2668                mLedNotification = mLights.get(n-1);
2669            }
2670        }
2671
2672        // Don't flash while we are in a call or screen is on
2673        if (mLedNotification == null || mInCall || mScreenOn) {
2674            mNotificationLight.turnOff();
2675        } else {
2676            final Notification ledno = mLedNotification.sbn.getNotification();
2677            int ledARGB = ledno.ledARGB;
2678            int ledOnMS = ledno.ledOnMS;
2679            int ledOffMS = ledno.ledOffMS;
2680            if ((ledno.defaults & Notification.DEFAULT_LIGHTS) != 0) {
2681                ledARGB = mDefaultNotificationColor;
2682                ledOnMS = mDefaultNotificationLedOn;
2683                ledOffMS = mDefaultNotificationLedOff;
2684            }
2685            if (mNotificationPulseEnabled) {
2686                // pulse repeatedly
2687                mNotificationLight.setFlashing(ledARGB, Light.LIGHT_FLASH_TIMED,
2688                        ledOnMS, ledOffMS);
2689            }
2690        }
2691    }
2692
2693    // lock on mNotificationList
2694    int indexOfNotificationLocked(String pkg, String tag, int id, int userId)
2695    {
2696        ArrayList<NotificationRecord> list = mNotificationList;
2697        final int len = list.size();
2698        for (int i=0; i<len; i++) {
2699            NotificationRecord r = list.get(i);
2700            if (!notificationMatchesUserId(r, userId) || r.sbn.getId() != id) {
2701                continue;
2702            }
2703            if (tag == null) {
2704                if (r.sbn.getTag() != null) {
2705                    continue;
2706                }
2707            } else {
2708                if (!tag.equals(r.sbn.getTag())) {
2709                    continue;
2710                }
2711            }
2712            if (r.sbn.getPackageName().equals(pkg)) {
2713                return i;
2714            }
2715        }
2716        return -1;
2717    }
2718
2719    private void updateNotificationPulse() {
2720        synchronized (mNotificationList) {
2721            updateLightsLocked();
2722        }
2723    }
2724
2725    private void updateZenMode() {
2726        final int mode = Settings.Global.getInt(getContext().getContentResolver(),
2727                Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF);
2728        if (mode != mZenMode) {
2729            Slog.d(TAG, String.format("updateZenMode: %s -> %s",
2730                    Settings.Global.zenModeToString(mZenMode),
2731                    Settings.Global.zenModeToString(mode)));
2732        }
2733        mZenMode = mode;
2734
2735        final String[] exceptionPackages = null; // none (for now)
2736
2737        // call restrictions
2738        final boolean muteCalls = mZenMode != Settings.Global.ZEN_MODE_OFF;
2739        mAppOps.setRestriction(AppOpsManager.OP_VIBRATE, AudioManager.STREAM_RING,
2740                muteCalls ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
2741                exceptionPackages);
2742        mAppOps.setRestriction(AppOpsManager.OP_PLAY_AUDIO, AudioManager.STREAM_RING,
2743                muteCalls ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
2744                exceptionPackages);
2745
2746        // alarm restrictions
2747        final boolean muteAlarms = false; // TODO until we save user config
2748        mAppOps.setRestriction(AppOpsManager.OP_VIBRATE, AudioManager.STREAM_ALARM,
2749                muteAlarms ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
2750                exceptionPackages);
2751        mAppOps.setRestriction(AppOpsManager.OP_PLAY_AUDIO, AudioManager.STREAM_ALARM,
2752                muteAlarms ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
2753                exceptionPackages);
2754
2755        // restrict vibrations with no hints
2756        mAppOps.setRestriction(AppOpsManager.OP_VIBRATE, AudioManager.USE_DEFAULT_STREAM_TYPE,
2757                (muteAlarms || muteCalls) ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
2758                exceptionPackages);
2759    }
2760
2761    private void updateCurrentProfilesCache(Context context) {
2762        UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
2763        if (userManager != null) {
2764            int currentUserId = ActivityManager.getCurrentUser();
2765            List<UserInfo> profiles = userManager.getProfiles(currentUserId);
2766            synchronized (mCurrentProfiles) {
2767                mCurrentProfiles.clear();
2768                for (UserInfo user : profiles) {
2769                    mCurrentProfiles.put(user.id, user);
2770                }
2771            }
2772        }
2773    }
2774
2775    private int[] getCurrentProfileIds() {
2776        synchronized (mCurrentProfiles) {
2777            int[] users = new int[mCurrentProfiles.size()];
2778            final int N = mCurrentProfiles.size();
2779            for (int i = 0; i < N; ++i) {
2780                users[i] = mCurrentProfiles.keyAt(i);
2781            }
2782            return users;
2783        }
2784    }
2785
2786    private boolean isCurrentProfile(int userId) {
2787        synchronized (mCurrentProfiles) {
2788            return mCurrentProfiles.get(userId) != null;
2789        }
2790    }
2791
2792    private boolean isCall(String pkg, Notification n) {
2793        return CALL_PACKAGES.contains(pkg);
2794    }
2795
2796    private boolean isAlarm(String pkg, Notification n) {
2797        return ALARM_PACKAGES.contains(pkg);
2798    }
2799
2800    private boolean shouldIntercept(String pkg, Notification n) {
2801        if (mZenMode != Settings.Global.ZEN_MODE_OFF) {
2802            return !isAlarm(pkg, n);
2803        }
2804        return false;
2805    }
2806}
2807