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