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