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