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