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