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