NotificationManagerService.java revision 1af30c7ac480e5d335f267a3ac3b2e6c748ce240
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        mSettingsObserver.observe();
1246
1247        // spin up NotificationScorers
1248        String[] notificationScorerNames = resources.getStringArray(
1249                R.array.config_notificationScorers);
1250        for (String scorerName : notificationScorerNames) {
1251            try {
1252                Class<?> scorerClass = getContext().getClassLoader().loadClass(scorerName);
1253                NotificationScorer scorer = (NotificationScorer) scorerClass.newInstance();
1254                scorer.initialize(getContext());
1255                mScorers.add(scorer);
1256            } catch (ClassNotFoundException e) {
1257                Slog.w(TAG, "Couldn't find scorer " + scorerName + ".", e);
1258            } catch (InstantiationException e) {
1259                Slog.w(TAG, "Couldn't instantiate scorer " + scorerName + ".", e);
1260            } catch (IllegalAccessException e) {
1261                Slog.w(TAG, "Problem accessing scorer " + scorerName + ".", e);
1262            }
1263        }
1264
1265        publishBinderService(Context.NOTIFICATION_SERVICE, mService);
1266        publishLocalService(NotificationManagerInternal.class, mInternalService);
1267    }
1268
1269    /**
1270     * Read the old XML-based app block database and import those blockages into the AppOps system.
1271     */
1272    private void importOldBlockDb() {
1273        loadBlockDb();
1274
1275        PackageManager pm = getContext().getPackageManager();
1276        for (String pkg : mBlockedPackages) {
1277            PackageInfo info = null;
1278            try {
1279                info = pm.getPackageInfo(pkg, 0);
1280                setNotificationsEnabledForPackageImpl(pkg, info.applicationInfo.uid, false);
1281            } catch (NameNotFoundException e) {
1282                // forget you
1283            }
1284        }
1285        mBlockedPackages.clear();
1286        if (mPolicyFile != null) {
1287            mPolicyFile.delete();
1288        }
1289    }
1290
1291    @Override
1292    public void onBootPhase(int phase) {
1293        if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
1294            // no beeping until we're basically done booting
1295            mSystemReady = true;
1296
1297            // Grab our optional AudioService
1298            mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
1299
1300            // make sure our listener services are properly bound
1301            rebindListenerServices();
1302        }
1303    }
1304
1305    void setNotificationsEnabledForPackageImpl(String pkg, int uid, boolean enabled) {
1306        Slog.v(TAG, (enabled?"en":"dis") + "abling notifications for " + pkg);
1307
1308        mAppOps.setMode(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg,
1309                enabled ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED);
1310
1311        // Now, cancel any outstanding notifications that are part of a just-disabled app
1312        if (ENABLE_BLOCKED_NOTIFICATIONS && !enabled) {
1313            cancelAllNotificationsInt(MY_UID, MY_PID, pkg, 0, 0, true, UserHandle.getUserId(uid),
1314                    REASON_PACKAGE_BANNED, null);
1315        }
1316    }
1317
1318    private final IBinder mService = new INotificationManager.Stub() {
1319        // Toasts
1320        // ============================================================================
1321
1322        @Override
1323        public void enqueueToast(String pkg, ITransientNotification callback, int duration)
1324        {
1325            if (DBG) {
1326                Slog.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback
1327                        + " duration=" + duration);
1328            }
1329
1330            if (pkg == null || callback == null) {
1331                Slog.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback);
1332                return ;
1333            }
1334
1335            final boolean isSystemToast = isCallerSystem() || ("android".equals(pkg));
1336
1337            if (ENABLE_BLOCKED_TOASTS && !noteNotificationOp(pkg, Binder.getCallingUid())) {
1338                if (!isSystemToast) {
1339                    Slog.e(TAG, "Suppressing toast from package " + pkg + " by user request.");
1340                    return;
1341                }
1342            }
1343
1344            synchronized (mToastQueue) {
1345                int callingPid = Binder.getCallingPid();
1346                long callingId = Binder.clearCallingIdentity();
1347                try {
1348                    ToastRecord record;
1349                    int index = indexOfToastLocked(pkg, callback);
1350                    // If it's already in the queue, we update it in place, we don't
1351                    // move it to the end of the queue.
1352                    if (index >= 0) {
1353                        record = mToastQueue.get(index);
1354                        record.update(duration);
1355                    } else {
1356                        // Limit the number of toasts that any given package except the android
1357                        // package can enqueue.  Prevents DOS attacks and deals with leaks.
1358                        if (!isSystemToast) {
1359                            int count = 0;
1360                            final int N = mToastQueue.size();
1361                            for (int i=0; i<N; i++) {
1362                                 final ToastRecord r = mToastQueue.get(i);
1363                                 if (r.pkg.equals(pkg)) {
1364                                     count++;
1365                                     if (count >= MAX_PACKAGE_NOTIFICATIONS) {
1366                                         Slog.e(TAG, "Package has already posted " + count
1367                                                + " toasts. Not showing more. Package=" + pkg);
1368                                         return;
1369                                     }
1370                                 }
1371                            }
1372                        }
1373
1374                        record = new ToastRecord(callingPid, pkg, callback, duration);
1375                        mToastQueue.add(record);
1376                        index = mToastQueue.size() - 1;
1377                        keepProcessAliveLocked(callingPid);
1378                    }
1379                    // If it's at index 0, it's the current toast.  It doesn't matter if it's
1380                    // new or just been updated.  Call back and tell it to show itself.
1381                    // If the callback fails, this will remove it from the list, so don't
1382                    // assume that it's valid after this.
1383                    if (index == 0) {
1384                        showNextToastLocked();
1385                    }
1386                } finally {
1387                    Binder.restoreCallingIdentity(callingId);
1388                }
1389            }
1390        }
1391
1392        @Override
1393        public void cancelToast(String pkg, ITransientNotification callback) {
1394            Slog.i(TAG, "cancelToast pkg=" + pkg + " callback=" + callback);
1395
1396            if (pkg == null || callback == null) {
1397                Slog.e(TAG, "Not cancelling notification. pkg=" + pkg + " callback=" + callback);
1398                return ;
1399            }
1400
1401            synchronized (mToastQueue) {
1402                long callingId = Binder.clearCallingIdentity();
1403                try {
1404                    int index = indexOfToastLocked(pkg, callback);
1405                    if (index >= 0) {
1406                        cancelToastLocked(index);
1407                    } else {
1408                        Slog.w(TAG, "Toast already cancelled. pkg=" + pkg
1409                                + " callback=" + callback);
1410                    }
1411                } finally {
1412                    Binder.restoreCallingIdentity(callingId);
1413                }
1414            }
1415        }
1416
1417        @Override
1418        public void enqueueNotificationWithTag(String pkg, String basePkg, String tag, int id,
1419                Notification notification, int[] idOut, int userId) throws RemoteException {
1420            enqueueNotificationInternal(pkg, basePkg, Binder.getCallingUid(),
1421                    Binder.getCallingPid(), tag, id, notification, idOut, userId);
1422        }
1423
1424        @Override
1425        public void cancelNotificationWithTag(String pkg, String tag, int id, int userId) {
1426            checkCallerIsSystemOrSameApp(pkg);
1427            userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1428                    Binder.getCallingUid(), userId, true, false, "cancelNotificationWithTag", pkg);
1429            // Don't allow client applications to cancel foreground service notis.
1430            cancelNotification(Binder.getCallingUid(), Binder.getCallingPid(), pkg, tag, id, 0,
1431                    Binder.getCallingUid() == Process.SYSTEM_UID
1432                    ? 0 : Notification.FLAG_FOREGROUND_SERVICE, false, userId, REASON_NOMAN_CANCEL,
1433                    null);
1434        }
1435
1436        @Override
1437        public void cancelAllNotifications(String pkg, int userId) {
1438            checkCallerIsSystemOrSameApp(pkg);
1439
1440            userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1441                    Binder.getCallingUid(), userId, true, false, "cancelAllNotifications", pkg);
1442
1443            // Calling from user space, don't allow the canceling of actively
1444            // running foreground services.
1445            cancelAllNotificationsInt(Binder.getCallingUid(), Binder.getCallingPid(),
1446                    pkg, 0, Notification.FLAG_FOREGROUND_SERVICE, true, userId,
1447                    REASON_NOMAN_CANCEL_ALL, null);
1448        }
1449
1450        @Override
1451        public void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) {
1452            checkCallerIsSystem();
1453
1454            setNotificationsEnabledForPackageImpl(pkg, uid, enabled);
1455        }
1456
1457        /**
1458         * Use this when you just want to know if notifications are OK for this package.
1459         */
1460        @Override
1461        public boolean areNotificationsEnabledForPackage(String pkg, int uid) {
1462            checkCallerIsSystem();
1463            return (mAppOps.checkOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
1464                    == AppOpsManager.MODE_ALLOWED);
1465        }
1466
1467        /**
1468         * System-only API for getting a list of current (i.e. not cleared) notifications.
1469         *
1470         * Requires ACCESS_NOTIFICATIONS which is signature|system.
1471         */
1472        @Override
1473        public StatusBarNotification[] getActiveNotifications(String callingPkg) {
1474            // enforce() will ensure the calling uid has the correct permission
1475            getContext().enforceCallingOrSelfPermission(
1476                    android.Manifest.permission.ACCESS_NOTIFICATIONS,
1477                    "NotificationManagerService.getActiveNotifications");
1478
1479            StatusBarNotification[] tmp = null;
1480            int uid = Binder.getCallingUid();
1481
1482            // noteOp will check to make sure the callingPkg matches the uid
1483            if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
1484                    == AppOpsManager.MODE_ALLOWED) {
1485                synchronized (mNotificationList) {
1486                    tmp = new StatusBarNotification[mNotificationList.size()];
1487                    final int N = mNotificationList.size();
1488                    for (int i=0; i<N; i++) {
1489                        tmp[i] = mNotificationList.get(i).sbn;
1490                    }
1491                }
1492            }
1493            return tmp;
1494        }
1495
1496        /**
1497         * System-only API for getting a list of recent (cleared, no longer shown) notifications.
1498         *
1499         * Requires ACCESS_NOTIFICATIONS which is signature|system.
1500         */
1501        @Override
1502        public StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count) {
1503            // enforce() will ensure the calling uid has the correct permission
1504            getContext().enforceCallingOrSelfPermission(
1505                    android.Manifest.permission.ACCESS_NOTIFICATIONS,
1506                    "NotificationManagerService.getHistoricalNotifications");
1507
1508            StatusBarNotification[] tmp = null;
1509            int uid = Binder.getCallingUid();
1510
1511            // noteOp will check to make sure the callingPkg matches the uid
1512            if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
1513                    == AppOpsManager.MODE_ALLOWED) {
1514                synchronized (mArchive) {
1515                    tmp = mArchive.getArray(count);
1516                }
1517            }
1518            return tmp;
1519        }
1520
1521        /**
1522         * Register a listener binder directly with the notification manager.
1523         *
1524         * Only works with system callers. Apps should extend
1525         * {@link android.service.notification.NotificationListenerService}.
1526         */
1527        @Override
1528        public void registerListener(final INotificationListener listener,
1529                final ComponentName component, final int userid) {
1530            checkCallerIsSystem();
1531            checkNullListener(listener);
1532            registerListenerImpl(listener, component, userid);
1533        }
1534
1535        /**
1536         * Remove a listener binder directly
1537         */
1538        @Override
1539        public void unregisterListener(INotificationListener listener, int userid) {
1540            checkNullListener(listener);
1541            // no need to check permissions; if your listener binder is in the list,
1542            // that's proof that you had permission to add it in the first place
1543            unregisterListenerImpl(listener, userid);
1544        }
1545
1546        /**
1547         * Allow an INotificationListener to simulate a "clear all" operation.
1548         *
1549         * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onClearAllNotifications}
1550         *
1551         * @param token The binder for the listener, to check that the caller is allowed
1552         */
1553        @Override
1554        public void cancelAllNotificationsFromListener(INotificationListener token) {
1555            NotificationListenerInfo info = checkListenerToken(token);
1556            final int callingUid = Binder.getCallingUid();
1557            final int callingPid = Binder.getCallingPid();
1558            long identity = Binder.clearCallingIdentity();
1559            try {
1560                cancelAll(callingUid, callingPid, info.userid,
1561                        REASON_LISTENER_CANCEL_ALL, info);
1562            } finally {
1563                Binder.restoreCallingIdentity(identity);
1564            }
1565        }
1566
1567        /**
1568         * Allow an INotificationListener to simulate clearing (dismissing) a single notification.
1569         *
1570         * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onNotificationClear}
1571         *
1572         * @param token The binder for the listener, to check that the caller is allowed
1573         */
1574        @Override
1575        public void cancelNotificationFromListener(INotificationListener token, String pkg,
1576                String tag, int id) {
1577            NotificationListenerInfo info = checkListenerToken(token);
1578            final int callingUid = Binder.getCallingUid();
1579            final int callingPid = Binder.getCallingPid();
1580            long identity = Binder.clearCallingIdentity();
1581            try {
1582                cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
1583                        Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
1584                        true,
1585                        info.userid, REASON_LISTENER_CANCEL, info);
1586            } finally {
1587                Binder.restoreCallingIdentity(identity);
1588            }
1589        }
1590
1591        /**
1592         * Allow an INotificationListener to request the list of outstanding notifications seen by
1593         * the current user. Useful when starting up, after which point the listener callbacks
1594         * should be used.
1595         *
1596         * @param token The binder for the listener, to check that the caller is allowed
1597         */
1598        @Override
1599        public StatusBarNotification[] getActiveNotificationsFromListener(
1600                INotificationListener token) {
1601            NotificationListenerInfo info = checkListenerToken(token);
1602
1603            StatusBarNotification[] result = new StatusBarNotification[0];
1604            ArrayList<StatusBarNotification> list = new ArrayList<StatusBarNotification>();
1605            synchronized (mNotificationList) {
1606                final int N = mNotificationList.size();
1607                for (int i=0; i<N; i++) {
1608                    StatusBarNotification sbn = mNotificationList.get(i).sbn;
1609                    if (info.enabledAndUserMatches(sbn)) {
1610                        list.add(sbn);
1611                    }
1612                }
1613            }
1614            return list.toArray(result);
1615        }
1616
1617        @Override
1618        protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1619            if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
1620                    != PackageManager.PERMISSION_GRANTED) {
1621                pw.println("Permission Denial: can't dump NotificationManager from from pid="
1622                        + Binder.getCallingPid()
1623                        + ", uid=" + Binder.getCallingUid());
1624                return;
1625            }
1626
1627            dumpImpl(pw);
1628        }
1629    };
1630
1631    void dumpImpl(PrintWriter pw) {
1632        pw.println("Current Notification Manager state:");
1633
1634        pw.println("  Listeners (" + mEnabledListenersForCurrentUser.size()
1635                + ") enabled for current user:");
1636        for (ComponentName cmpt : mEnabledListenersForCurrentUser) {
1637            pw.println("    " + cmpt);
1638        }
1639
1640        pw.println("  Live listeners (" + mListeners.size() + "):");
1641        for (NotificationListenerInfo info : mListeners) {
1642            pw.println("    " + info.component
1643                    + " (user " + info.userid + "): " + info.listener
1644                    + (info.isSystem?" SYSTEM":""));
1645        }
1646
1647        int N;
1648
1649        synchronized (mToastQueue) {
1650            N = mToastQueue.size();
1651            if (N > 0) {
1652                pw.println("  Toast Queue:");
1653                for (int i=0; i<N; i++) {
1654                    mToastQueue.get(i).dump(pw, "    ");
1655                }
1656                pw.println("  ");
1657            }
1658
1659        }
1660
1661        synchronized (mNotificationList) {
1662            N = mNotificationList.size();
1663            if (N > 0) {
1664                pw.println("  Notification List:");
1665                for (int i=0; i<N; i++) {
1666                    mNotificationList.get(i).dump(pw, "    ", getContext());
1667                }
1668                pw.println("  ");
1669            }
1670
1671            N = mLights.size();
1672            if (N > 0) {
1673                pw.println("  Lights List:");
1674                for (int i=0; i<N; i++) {
1675                    pw.println("    " + mLights.get(i));
1676                }
1677                pw.println("  ");
1678            }
1679
1680            pw.println("  mSoundNotification=" + mSoundNotification);
1681            pw.println("  mVibrateNotification=" + mVibrateNotification);
1682            pw.println("  mDisableNotificationAlerts=" + mDisableNotificationAlerts);
1683            pw.println("  mZenMode=" + Settings.Global.zenModeToString(mZenMode));
1684            pw.println("  mSystemReady=" + mSystemReady);
1685            pw.println("  mArchive=" + mArchive.toString());
1686            Iterator<StatusBarNotification> iter = mArchive.descendingIterator();
1687            int i=0;
1688            while (iter.hasNext()) {
1689                pw.println("    " + iter.next());
1690                if (++i >= 5) {
1691                    if (iter.hasNext()) pw.println("    ...");
1692                    break;
1693                }
1694            }
1695
1696        }
1697    }
1698
1699    /**
1700     * The private API only accessible to the system process.
1701     */
1702    private final NotificationManagerInternal mInternalService = new NotificationManagerInternal() {
1703        @Override
1704        public void enqueueNotification(String pkg, String basePkg, int callingUid, int callingPid,
1705                String tag, int id, Notification notification, int[] idReceived, int userId) {
1706            enqueueNotificationInternal(pkg, basePkg, callingUid, callingPid, tag, id, notification,
1707                    idReceived, userId);
1708        }
1709    };
1710
1711    void enqueueNotificationInternal(final String pkg, String basePkg, final int callingUid,
1712            final int callingPid, final String tag, final int id, final Notification notification,
1713            int[] idOut, int incomingUserId) {
1714        if (DBG) {
1715            Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id
1716                    + " notification=" + notification);
1717        }
1718        checkCallerIsSystemOrSameApp(pkg);
1719        final boolean isSystemNotification = isUidSystem(callingUid) || ("android".equals(pkg));
1720
1721        final int userId = ActivityManager.handleIncomingUser(callingPid,
1722                callingUid, incomingUserId, true, false, "enqueueNotification", pkg);
1723        final UserHandle user = new UserHandle(userId);
1724
1725        // Limit the number of notifications that any given package except the android
1726        // package can enqueue.  Prevents DOS attacks and deals with leaks.
1727        if (!isSystemNotification) {
1728            synchronized (mNotificationList) {
1729                int count = 0;
1730                final int N = mNotificationList.size();
1731                for (int i=0; i<N; i++) {
1732                    final NotificationRecord r = mNotificationList.get(i);
1733                    if (r.sbn.getPackageName().equals(pkg) && r.sbn.getUserId() == userId) {
1734                        count++;
1735                        if (count >= MAX_PACKAGE_NOTIFICATIONS) {
1736                            Slog.e(TAG, "Package has already posted " + count
1737                                    + " notifications.  Not showing more.  package=" + pkg);
1738                            return;
1739                        }
1740                    }
1741                }
1742            }
1743        }
1744
1745        // This conditional is a dirty hack to limit the logging done on
1746        //     behalf of the download manager without affecting other apps.
1747        if (!pkg.equals("com.android.providers.downloads")
1748                || Log.isLoggable("DownloadManager", Log.VERBOSE)) {
1749            EventLogTags.writeNotificationEnqueue(callingUid, callingPid,
1750                    pkg, id, tag, userId, notification.toString());
1751        }
1752
1753        if (pkg == null || notification == null) {
1754            throw new IllegalArgumentException("null not allowed: pkg=" + pkg
1755                    + " id=" + id + " notification=" + notification);
1756        }
1757        if (notification.icon != 0) {
1758            if (notification.contentView == null) {
1759                throw new IllegalArgumentException("contentView required: pkg=" + pkg
1760                        + " id=" + id + " notification=" + notification);
1761            }
1762        }
1763
1764        mHandler.post(new Runnable() {
1765            @Override
1766            public void run() {
1767
1768                // === Scoring ===
1769
1770                // 0. Sanitize inputs
1771                notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN,
1772                        Notification.PRIORITY_MAX);
1773                // Migrate notification flags to scores
1774                if (0 != (notification.flags & Notification.FLAG_HIGH_PRIORITY)) {
1775                    if (notification.priority < Notification.PRIORITY_MAX) {
1776                        notification.priority = Notification.PRIORITY_MAX;
1777                    }
1778                } else if (SCORE_ONGOING_HIGHER &&
1779                        0 != (notification.flags & Notification.FLAG_ONGOING_EVENT)) {
1780                    if (notification.priority < Notification.PRIORITY_HIGH) {
1781                        notification.priority = Notification.PRIORITY_HIGH;
1782                    }
1783                }
1784
1785                // 1. initial score: buckets of 10, around the app
1786                int score = notification.priority * NOTIFICATION_PRIORITY_MULTIPLIER; //[-20..20]
1787
1788                // 2. Consult external heuristics (TBD)
1789
1790                // 3. Apply local rules
1791
1792                int initialScore = score;
1793                if (!mScorers.isEmpty()) {
1794                    if (DBG) Slog.v(TAG, "Initial score is " + score + ".");
1795                    for (NotificationScorer scorer : mScorers) {
1796                        try {
1797                            score = scorer.getScore(notification, score);
1798                        } catch (Throwable t) {
1799                            Slog.w(TAG, "Scorer threw on .getScore.", t);
1800                        }
1801                    }
1802                    if (DBG) Slog.v(TAG, "Final score is " + score + ".");
1803                }
1804
1805                // add extra to indicate score modified by NotificationScorer
1806                notification.extras.putBoolean(Notification.EXTRA_SCORE_MODIFIED,
1807                        score != initialScore);
1808
1809                // blocked apps
1810                if (ENABLE_BLOCKED_NOTIFICATIONS && !noteNotificationOp(pkg, callingUid)) {
1811                    if (!isSystemNotification) {
1812                        score = JUNK_SCORE;
1813                        Slog.e(TAG, "Suppressing notification from package " + pkg
1814                                + " by user request.");
1815                    }
1816                }
1817
1818                if (DBG) {
1819                    Slog.v(TAG, "Assigned score=" + score + " to " + notification);
1820                }
1821
1822                if (score < SCORE_DISPLAY_THRESHOLD) {
1823                    // Notification will be blocked because the score is too low.
1824                    return;
1825                }
1826
1827                // Is this notification intercepted by zen mode?
1828                final boolean intercept = shouldIntercept(pkg, notification);
1829                notification.extras.putBoolean(EXTRA_INTERCEPT, intercept);
1830
1831                // Should this notification make noise, vibe, or use the LED?
1832                final boolean canInterrupt = (score >= SCORE_INTERRUPTION_THRESHOLD) && !intercept;
1833                if (DBG) Slog.v(TAG, "canInterrupt=" + canInterrupt + " intercept=" + intercept);
1834                synchronized (mNotificationList) {
1835                    final StatusBarNotification n = new StatusBarNotification(
1836                            pkg, id, tag, callingUid, callingPid, score, notification, user);
1837                    NotificationRecord r = new NotificationRecord(n);
1838                    NotificationRecord old = null;
1839
1840                    int index = indexOfNotificationLocked(pkg, tag, id, userId);
1841                    if (index < 0) {
1842                        mNotificationList.add(r);
1843                    } else {
1844                        old = mNotificationList.remove(index);
1845                        mNotificationList.add(index, r);
1846                        // Make sure we don't lose the foreground service state.
1847                        if (old != null) {
1848                            notification.flags |=
1849                                old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;
1850                        }
1851                    }
1852
1853                    // Ensure if this is a foreground service that the proper additional
1854                    // flags are set.
1855                    if ((notification.flags&Notification.FLAG_FOREGROUND_SERVICE) != 0) {
1856                        notification.flags |= Notification.FLAG_ONGOING_EVENT
1857                                | Notification.FLAG_NO_CLEAR;
1858                    }
1859
1860                    final int currentUser;
1861                    final long token = Binder.clearCallingIdentity();
1862                    try {
1863                        currentUser = ActivityManager.getCurrentUser();
1864                    } finally {
1865                        Binder.restoreCallingIdentity(token);
1866                    }
1867
1868                    if (notification.icon != 0) {
1869                        if (old != null && old.statusBarKey != null) {
1870                            r.statusBarKey = old.statusBarKey;
1871                            final long identity = Binder.clearCallingIdentity();
1872                            try {
1873                                mStatusBar.updateNotification(r.statusBarKey, n);
1874                            } finally {
1875                                Binder.restoreCallingIdentity(identity);
1876                            }
1877                        } else {
1878                            final long identity = Binder.clearCallingIdentity();
1879                            try {
1880                                r.statusBarKey = mStatusBar.addNotification(n);
1881                                if ((n.getNotification().flags & Notification.FLAG_SHOW_LIGHTS) != 0
1882                                        && canInterrupt) {
1883                                    mAttentionLight.pulse();
1884                                }
1885                            } finally {
1886                                Binder.restoreCallingIdentity(identity);
1887                            }
1888                        }
1889                        // Send accessibility events only for the current user.
1890                        if (currentUser == userId) {
1891                            sendAccessibilityEvent(notification, pkg);
1892                        }
1893
1894                        notifyPostedLocked(r);
1895                    } else {
1896                        Slog.e(TAG, "Not posting notification with icon==0: " + notification);
1897                        if (old != null && old.statusBarKey != null) {
1898                            final long identity = Binder.clearCallingIdentity();
1899                            try {
1900                                mStatusBar.removeNotification(old.statusBarKey);
1901                            } finally {
1902                                Binder.restoreCallingIdentity(identity);
1903                            }
1904
1905                            notifyRemovedLocked(r);
1906                        }
1907                        // ATTENTION: in a future release we will bail out here
1908                        // so that we do not play sounds, show lights, etc. for invalid
1909                        // notifications
1910                        Slog.e(TAG, "WARNING: In a future release this will crash the app: "
1911                                + n.getPackageName());
1912                    }
1913
1914                    // If we're not supposed to beep, vibrate, etc. then don't.
1915                    if (!mDisableNotificationAlerts
1916                            && (!(old != null
1917                                && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 ))
1918                            && (r.getUserId() == UserHandle.USER_ALL ||
1919                                (r.getUserId() == userId && r.getUserId() == currentUser))
1920                            && canInterrupt
1921                            && mSystemReady
1922                            && mAudioManager != null) {
1923                        if (DBG) Slog.v(TAG, "Interrupting!");
1924                        // sound
1925
1926                        // should we use the default notification sound? (indicated either by
1927                        // DEFAULT_SOUND or because notification.sound is pointing at
1928                        // Settings.System.NOTIFICATION_SOUND)
1929                        final boolean useDefaultSound =
1930                               (notification.defaults & Notification.DEFAULT_SOUND) != 0 ||
1931                                       Settings.System.DEFAULT_NOTIFICATION_URI
1932                                               .equals(notification.sound);
1933
1934                        Uri soundUri = null;
1935                        boolean hasValidSound = false;
1936
1937                        if (useDefaultSound) {
1938                            soundUri = Settings.System.DEFAULT_NOTIFICATION_URI;
1939
1940                            // check to see if the default notification sound is silent
1941                            ContentResolver resolver = getContext().getContentResolver();
1942                            hasValidSound = Settings.System.getString(resolver,
1943                                   Settings.System.NOTIFICATION_SOUND) != null;
1944                        } else if (notification.sound != null) {
1945                            soundUri = notification.sound;
1946                            hasValidSound = (soundUri != null);
1947                        }
1948
1949                        if (hasValidSound) {
1950                            boolean looping =
1951                                    (notification.flags & Notification.FLAG_INSISTENT) != 0;
1952                            int audioStreamType;
1953                            if (notification.audioStreamType >= 0) {
1954                                audioStreamType = notification.audioStreamType;
1955                            } else {
1956                                audioStreamType = DEFAULT_STREAM_TYPE;
1957                            }
1958                            mSoundNotification = r;
1959                            // do not play notifications if stream volume is 0 (typically because
1960                            // ringer mode is silent) or if there is a user of exclusive audio focus
1961                            if ((mAudioManager.getStreamVolume(audioStreamType) != 0)
1962                                    && !mAudioManager.isAudioFocusExclusive()) {
1963                                final long identity = Binder.clearCallingIdentity();
1964                                try {
1965                                    final IRingtonePlayer player =
1966                                            mAudioManager.getRingtonePlayer();
1967                                    if (player != null) {
1968                                        if (DBG) Slog.v(TAG, "Playing sound " + soundUri
1969                                                + " on stream " + audioStreamType);
1970                                        player.playAsync(soundUri, user, looping, audioStreamType);
1971                                    }
1972                                } catch (RemoteException e) {
1973                                } finally {
1974                                    Binder.restoreCallingIdentity(identity);
1975                                }
1976                            }
1977                        }
1978
1979                        // vibrate
1980                        // Does the notification want to specify its own vibration?
1981                        final boolean hasCustomVibrate = notification.vibrate != null;
1982
1983                        // new in 4.2: if there was supposed to be a sound and we're in vibrate
1984                        // mode, and no other vibration is specified, we fall back to vibration
1985                        final boolean convertSoundToVibration =
1986                                   !hasCustomVibrate
1987                                && hasValidSound
1988                                && (mAudioManager.getRingerMode()
1989                                           == AudioManager.RINGER_MODE_VIBRATE);
1990
1991                        // The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback.
1992                        final boolean useDefaultVibrate =
1993                                (notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
1994
1995                        if ((useDefaultVibrate || convertSoundToVibration || hasCustomVibrate)
1996                                && !(mAudioManager.getRingerMode()
1997                                        == AudioManager.RINGER_MODE_SILENT)) {
1998                            mVibrateNotification = r;
1999
2000                            if (useDefaultVibrate || convertSoundToVibration) {
2001                                // Escalate privileges so we can use the vibrator even if the
2002                                // notifying app does not have the VIBRATE permission.
2003                                long identity = Binder.clearCallingIdentity();
2004                                try {
2005                                    mVibrator.vibrate(r.sbn.getUid(), r.sbn.getBasePkg(),
2006                                        useDefaultVibrate ? mDefaultVibrationPattern
2007                                            : mFallbackVibrationPattern,
2008                                        ((notification.flags & Notification.FLAG_INSISTENT) != 0)
2009                                                ? 0: -1, notification.audioStreamType);
2010                                } finally {
2011                                    Binder.restoreCallingIdentity(identity);
2012                                }
2013                            } else if (notification.vibrate.length > 1) {
2014                                // If you want your own vibration pattern, you need the VIBRATE
2015                                // permission
2016                                mVibrator.vibrate(r.sbn.getUid(), r.sbn.getBasePkg(),
2017                                        notification.vibrate,
2018                                    ((notification.flags & Notification.FLAG_INSISTENT) != 0)
2019                                            ? 0: -1, notification.audioStreamType);
2020                            }
2021                        }
2022                    }
2023
2024                    // light
2025                    // the most recent thing gets the light
2026                    mLights.remove(old);
2027                    if (mLedNotification == old) {
2028                        mLedNotification = null;
2029                    }
2030                    //Slog.i(TAG, "notification.lights="
2031                    //        + ((old.notification.lights.flags & Notification.FLAG_SHOW_LIGHTS)
2032                    //                  != 0));
2033                    if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0
2034                            && canInterrupt) {
2035                        mLights.add(r);
2036                        updateLightsLocked();
2037                    } else {
2038                        if (old != null
2039                                && ((old.getFlags() & Notification.FLAG_SHOW_LIGHTS) != 0)) {
2040                            updateLightsLocked();
2041                        }
2042                    }
2043                }
2044            }
2045        });
2046
2047        idOut[0] = id;
2048    }
2049
2050     void registerListenerImpl(final INotificationListener listener,
2051            final ComponentName component, final int userid) {
2052        synchronized (mNotificationList) {
2053            try {
2054                NotificationListenerInfo info
2055                        = new NotificationListenerInfo(listener, component, userid, true);
2056                listener.asBinder().linkToDeath(info, 0);
2057                mListeners.add(info);
2058            } catch (RemoteException e) {
2059                // already dead
2060            }
2061        }
2062    }
2063
2064    /**
2065     * Removes a listener from the list and unbinds from its service.
2066     */
2067    void unregisterListenerImpl(final INotificationListener listener, final int userid) {
2068        NotificationListenerInfo info = removeListenerImpl(listener, userid);
2069        if (info != null && info.connection != null) {
2070            getContext().unbindService(info.connection);
2071        }
2072    }
2073
2074    /**
2075     * Removes a listener from the list but does not unbind from the listener's service.
2076     *
2077     * @return the removed listener.
2078     */
2079    NotificationListenerInfo removeListenerImpl(
2080            final INotificationListener listener, final int userid) {
2081        NotificationListenerInfo listenerInfo = null;
2082        synchronized (mNotificationList) {
2083            final int N = mListeners.size();
2084            for (int i=N-1; i>=0; i--) {
2085                final NotificationListenerInfo info = mListeners.get(i);
2086                if (info.listener.asBinder() == listener.asBinder()
2087                        && info.userid == userid) {
2088                    listenerInfo = mListeners.remove(i);
2089                }
2090            }
2091        }
2092        return listenerInfo;
2093    }
2094
2095    void showNextToastLocked() {
2096        ToastRecord record = mToastQueue.get(0);
2097        while (record != null) {
2098            if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback);
2099            try {
2100                record.callback.show();
2101                scheduleTimeoutLocked(record);
2102                return;
2103            } catch (RemoteException e) {
2104                Slog.w(TAG, "Object died trying to show notification " + record.callback
2105                        + " in package " + record.pkg);
2106                // remove it from the list and let the process die
2107                int index = mToastQueue.indexOf(record);
2108                if (index >= 0) {
2109                    mToastQueue.remove(index);
2110                }
2111                keepProcessAliveLocked(record.pid);
2112                if (mToastQueue.size() > 0) {
2113                    record = mToastQueue.get(0);
2114                } else {
2115                    record = null;
2116                }
2117            }
2118        }
2119    }
2120
2121    void cancelToastLocked(int index) {
2122        ToastRecord record = mToastQueue.get(index);
2123        try {
2124            record.callback.hide();
2125        } catch (RemoteException e) {
2126            Slog.w(TAG, "Object died trying to hide notification " + record.callback
2127                    + " in package " + record.pkg);
2128            // don't worry about this, we're about to remove it from
2129            // the list anyway
2130        }
2131        mToastQueue.remove(index);
2132        keepProcessAliveLocked(record.pid);
2133        if (mToastQueue.size() > 0) {
2134            // Show the next one. If the callback fails, this will remove
2135            // it from the list, so don't assume that the list hasn't changed
2136            // after this point.
2137            showNextToastLocked();
2138        }
2139    }
2140
2141    private void scheduleTimeoutLocked(ToastRecord r)
2142    {
2143        mHandler.removeCallbacksAndMessages(r);
2144        Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r);
2145        long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY;
2146        mHandler.sendMessageDelayed(m, delay);
2147    }
2148
2149    private void handleTimeout(ToastRecord record)
2150    {
2151        if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback);
2152        synchronized (mToastQueue) {
2153            int index = indexOfToastLocked(record.pkg, record.callback);
2154            if (index >= 0) {
2155                cancelToastLocked(index);
2156            }
2157        }
2158    }
2159
2160    // lock on mToastQueue
2161    int indexOfToastLocked(String pkg, ITransientNotification callback)
2162    {
2163        IBinder cbak = callback.asBinder();
2164        ArrayList<ToastRecord> list = mToastQueue;
2165        int len = list.size();
2166        for (int i=0; i<len; i++) {
2167            ToastRecord r = list.get(i);
2168            if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) {
2169                return i;
2170            }
2171        }
2172        return -1;
2173    }
2174
2175    // lock on mToastQueue
2176    void keepProcessAliveLocked(int pid)
2177    {
2178        int toastCount = 0; // toasts from this pid
2179        ArrayList<ToastRecord> list = mToastQueue;
2180        int N = list.size();
2181        for (int i=0; i<N; i++) {
2182            ToastRecord r = list.get(i);
2183            if (r.pid == pid) {
2184                toastCount++;
2185            }
2186        }
2187        try {
2188            mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0);
2189        } catch (RemoteException e) {
2190            // Shouldn't happen.
2191        }
2192    }
2193
2194    private final class WorkerHandler extends Handler
2195    {
2196        @Override
2197        public void handleMessage(Message msg)
2198        {
2199            switch (msg.what)
2200            {
2201                case MESSAGE_TIMEOUT:
2202                    handleTimeout((ToastRecord)msg.obj);
2203                    break;
2204            }
2205        }
2206    }
2207
2208
2209    // Notifications
2210    // ============================================================================
2211    static int clamp(int x, int low, int high) {
2212        return (x < low) ? low : ((x > high) ? high : x);
2213    }
2214
2215    void sendAccessibilityEvent(Notification notification, CharSequence packageName) {
2216        AccessibilityManager manager = AccessibilityManager.getInstance(getContext());
2217        if (!manager.isEnabled()) {
2218            return;
2219        }
2220
2221        AccessibilityEvent event =
2222            AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
2223        event.setPackageName(packageName);
2224        event.setClassName(Notification.class.getName());
2225        event.setParcelableData(notification);
2226        CharSequence tickerText = notification.tickerText;
2227        if (!TextUtils.isEmpty(tickerText)) {
2228            event.getText().add(tickerText);
2229        }
2230
2231        manager.sendAccessibilityEvent(event);
2232    }
2233
2234    private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete) {
2235        // tell the app
2236        if (sendDelete) {
2237            if (r.getNotification().deleteIntent != null) {
2238                try {
2239                    r.getNotification().deleteIntent.send();
2240                } catch (PendingIntent.CanceledException ex) {
2241                    // do nothing - there's no relevant way to recover, and
2242                    //     no reason to let this propagate
2243                    Slog.w(TAG, "canceled PendingIntent for " + r.sbn.getPackageName(), ex);
2244                }
2245            }
2246        }
2247
2248        // status bar
2249        if (r.getNotification().icon != 0) {
2250            final long identity = Binder.clearCallingIdentity();
2251            try {
2252                mStatusBar.removeNotification(r.statusBarKey);
2253            } finally {
2254                Binder.restoreCallingIdentity(identity);
2255            }
2256            r.statusBarKey = null;
2257            notifyRemovedLocked(r);
2258        }
2259
2260        // sound
2261        if (mSoundNotification == r) {
2262            mSoundNotification = null;
2263            final long identity = Binder.clearCallingIdentity();
2264            try {
2265                final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
2266                if (player != null) {
2267                    player.stopAsync();
2268                }
2269            } catch (RemoteException e) {
2270            } finally {
2271                Binder.restoreCallingIdentity(identity);
2272            }
2273        }
2274
2275        // vibrate
2276        if (mVibrateNotification == r) {
2277            mVibrateNotification = null;
2278            long identity = Binder.clearCallingIdentity();
2279            try {
2280                mVibrator.cancel();
2281            }
2282            finally {
2283                Binder.restoreCallingIdentity(identity);
2284            }
2285        }
2286
2287        // light
2288        mLights.remove(r);
2289        if (mLedNotification == r) {
2290            mLedNotification = null;
2291        }
2292
2293        // Save it for users of getHistoricalNotifications()
2294        mArchive.record(r.sbn);
2295    }
2296
2297    /**
2298     * Cancels a notification ONLY if it has all of the {@code mustHaveFlags}
2299     * and none of the {@code mustNotHaveFlags}.
2300     */
2301    void cancelNotification(final int callingUid, final int callingPid,
2302            final String pkg, final String tag, final int id,
2303            final int mustHaveFlags, final int mustNotHaveFlags, final boolean sendDelete,
2304            final int userId, final int reason, final NotificationListenerInfo listener) {
2305        // In enqueueNotificationInternal notifications are added by scheduling the
2306        // work on the worker handler. Hence, we also schedule the cancel on this
2307        // handler to avoid a scenario where an add notification call followed by a
2308        // remove notification call ends up in not removing the notification.
2309        mHandler.post(new Runnable() {
2310            @Override
2311            public void run() {
2312                EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, id, tag, userId,
2313                        mustHaveFlags, mustNotHaveFlags, reason,
2314                        listener == null ? null : listener.component.toShortString());
2315
2316                synchronized (mNotificationList) {
2317                    int index = indexOfNotificationLocked(pkg, tag, id, userId);
2318                    if (index >= 0) {
2319                        NotificationRecord r = mNotificationList.get(index);
2320
2321                        if ((r.getNotification().flags & mustHaveFlags) != mustHaveFlags) {
2322                            return;
2323                        }
2324                        if ((r.getNotification().flags & mustNotHaveFlags) != 0) {
2325                            return;
2326                        }
2327
2328                        mNotificationList.remove(index);
2329
2330                        cancelNotificationLocked(r, sendDelete);
2331                        updateLightsLocked();
2332                    }
2333                }
2334            }
2335        });
2336    }
2337
2338    /**
2339     * Determine whether the userId applies to the notification in question, either because
2340     * they match exactly, or one of them is USER_ALL (which is treated as a wildcard).
2341     */
2342    private boolean notificationMatchesUserId(NotificationRecord r, int userId) {
2343        return
2344                // looking for USER_ALL notifications? match everything
2345                   userId == UserHandle.USER_ALL
2346                // a notification sent to USER_ALL matches any query
2347                || r.getUserId() == UserHandle.USER_ALL
2348                // an exact user match
2349                || r.getUserId() == userId;
2350    }
2351
2352    /**
2353     * Determine whether the userId applies to the notification in question, either because
2354     * they match exactly, or one of them is USER_ALL (which is treated as a wildcard) or
2355     * because it matches a related user.
2356     */
2357    private boolean notificationMatchesUserIdOrRelated(NotificationRecord r, int userId) {
2358        synchronized (mRelatedUsers) {
2359            return notificationMatchesUserId(r, userId)
2360                    || mRelatedUsers.get(r.getUserId()) != null;
2361        }
2362    }
2363
2364    /**
2365     * Cancels all notifications from a given package that have all of the
2366     * {@code mustHaveFlags}.
2367     */
2368    boolean cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, int mustHaveFlags,
2369            int mustNotHaveFlags, boolean doit, int userId, int reason,
2370            NotificationListenerInfo listener) {
2371        EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
2372                pkg, userId, mustHaveFlags, mustNotHaveFlags, reason,
2373                listener == null ? null : listener.component.toShortString());
2374
2375        synchronized (mNotificationList) {
2376            final int N = mNotificationList.size();
2377            boolean canceledSomething = false;
2378            for (int i = N-1; i >= 0; --i) {
2379                NotificationRecord r = mNotificationList.get(i);
2380                if (!notificationMatchesUserId(r, userId)) {
2381                    continue;
2382                }
2383                // Don't remove notifications to all, if there's no package name specified
2384                if (r.getUserId() == UserHandle.USER_ALL && pkg == null) {
2385                    continue;
2386                }
2387                if ((r.getFlags() & mustHaveFlags) != mustHaveFlags) {
2388                    continue;
2389                }
2390                if ((r.getFlags() & mustNotHaveFlags) != 0) {
2391                    continue;
2392                }
2393                if (pkg != null && !r.sbn.getPackageName().equals(pkg)) {
2394                    continue;
2395                }
2396                canceledSomething = true;
2397                if (!doit) {
2398                    return true;
2399                }
2400                mNotificationList.remove(i);
2401                cancelNotificationLocked(r, false);
2402            }
2403            if (canceledSomething) {
2404                updateLightsLocked();
2405            }
2406            return canceledSomething;
2407        }
2408    }
2409
2410
2411
2412    // Return true if the UID is a system or phone UID and therefore should not have
2413    // any notifications or toasts blocked.
2414    boolean isUidSystem(int uid) {
2415        final int appid = UserHandle.getAppId(uid);
2416        return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0);
2417    }
2418
2419    // same as isUidSystem(int, int) for the Binder caller's UID.
2420    boolean isCallerSystem() {
2421        return isUidSystem(Binder.getCallingUid());
2422    }
2423
2424    void checkCallerIsSystem() {
2425        if (isCallerSystem()) {
2426            return;
2427        }
2428        throw new SecurityException("Disallowed call for uid " + Binder.getCallingUid());
2429    }
2430
2431    void checkCallerIsSystemOrSameApp(String pkg) {
2432        if (isCallerSystem()) {
2433            return;
2434        }
2435        final int uid = Binder.getCallingUid();
2436        try {
2437            ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo(
2438                    pkg, 0, UserHandle.getCallingUserId());
2439            if (!UserHandle.isSameApp(ai.uid, uid)) {
2440                throw new SecurityException("Calling uid " + uid + " gave package"
2441                        + pkg + " which is owned by uid " + ai.uid);
2442            }
2443        } catch (RemoteException re) {
2444            throw new SecurityException("Unknown package " + pkg + "\n" + re);
2445        }
2446    }
2447
2448    void cancelAll(int callingUid, int callingPid, int userId, int reason,
2449            NotificationListenerInfo listener) {
2450        EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
2451                null, userId, 0, 0, reason,
2452                listener == null ? null : listener.component.toShortString());
2453        synchronized (mNotificationList) {
2454            final int N = mNotificationList.size();
2455            for (int i=N-1; i>=0; i--) {
2456                NotificationRecord r = mNotificationList.get(i);
2457
2458                if (!notificationMatchesUserIdOrRelated(r, userId)) {
2459                    continue;
2460                }
2461
2462                if ((r.getFlags() & (Notification.FLAG_ONGOING_EVENT
2463                                | Notification.FLAG_NO_CLEAR)) == 0) {
2464                    mNotificationList.remove(i);
2465                    cancelNotificationLocked(r, true);
2466                }
2467            }
2468
2469            updateLightsLocked();
2470        }
2471    }
2472
2473    // lock on mNotificationList
2474    void updateLightsLocked()
2475    {
2476        // handle notification lights
2477        if (mLedNotification == null) {
2478            // get next notification, if any
2479            int n = mLights.size();
2480            if (n > 0) {
2481                mLedNotification = mLights.get(n-1);
2482            }
2483        }
2484
2485        // Don't flash while we are in a call or screen is on
2486        if (mLedNotification == null || mInCall || mScreenOn) {
2487            mNotificationLight.turnOff();
2488        } else {
2489            final Notification ledno = mLedNotification.sbn.getNotification();
2490            int ledARGB = ledno.ledARGB;
2491            int ledOnMS = ledno.ledOnMS;
2492            int ledOffMS = ledno.ledOffMS;
2493            if ((ledno.defaults & Notification.DEFAULT_LIGHTS) != 0) {
2494                ledARGB = mDefaultNotificationColor;
2495                ledOnMS = mDefaultNotificationLedOn;
2496                ledOffMS = mDefaultNotificationLedOff;
2497            }
2498            if (mNotificationPulseEnabled) {
2499                // pulse repeatedly
2500                mNotificationLight.setFlashing(ledARGB, Light.LIGHT_FLASH_TIMED,
2501                        ledOnMS, ledOffMS);
2502            }
2503        }
2504    }
2505
2506    // lock on mNotificationList
2507    int indexOfNotificationLocked(String pkg, String tag, int id, int userId)
2508    {
2509        ArrayList<NotificationRecord> list = mNotificationList;
2510        final int len = list.size();
2511        for (int i=0; i<len; i++) {
2512            NotificationRecord r = list.get(i);
2513            if (!notificationMatchesUserId(r, userId) || r.sbn.getId() != id) {
2514                continue;
2515            }
2516            if (tag == null) {
2517                if (r.sbn.getTag() != null) {
2518                    continue;
2519                }
2520            } else {
2521                if (!tag.equals(r.sbn.getTag())) {
2522                    continue;
2523                }
2524            }
2525            if (r.sbn.getPackageName().equals(pkg)) {
2526                return i;
2527            }
2528        }
2529        return -1;
2530    }
2531
2532    private void updateNotificationPulse() {
2533        synchronized (mNotificationList) {
2534            updateLightsLocked();
2535        }
2536    }
2537
2538    private void updateZenMode() {
2539        final int mode = Settings.Global.getInt(getContext().getContentResolver(),
2540                Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF);
2541        if (mode != mZenMode) {
2542            Slog.d(TAG, String.format("updateZenMode: %s -> %s",
2543                    Settings.Global.zenModeToString(mZenMode),
2544                    Settings.Global.zenModeToString(mode)));
2545        }
2546        mZenMode = mode;
2547
2548        final String[] exceptionPackages = null; // none (for now)
2549
2550        // call restrictions
2551        final boolean muteCalls = mZenMode != Settings.Global.ZEN_MODE_OFF;
2552        mAppOps.setRestriction(AppOpsManager.OP_VIBRATE, AudioManager.STREAM_RING,
2553                muteCalls ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
2554                exceptionPackages);
2555        mAppOps.setRestriction(AppOpsManager.OP_PLAY_AUDIO, AudioManager.STREAM_RING,
2556                muteCalls ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
2557                exceptionPackages);
2558
2559        // alarm restrictions
2560        final boolean muteAlarms = mZenMode == Settings.Global.ZEN_MODE_FULL;
2561        mAppOps.setRestriction(AppOpsManager.OP_VIBRATE, AudioManager.STREAM_ALARM,
2562                muteAlarms ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
2563                exceptionPackages);
2564        mAppOps.setRestriction(AppOpsManager.OP_PLAY_AUDIO, AudioManager.STREAM_ALARM,
2565                muteAlarms ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
2566                exceptionPackages);
2567
2568        // restrict vibrations with no hints
2569        mAppOps.setRestriction(AppOpsManager.OP_VIBRATE, AudioManager.USE_DEFAULT_STREAM_TYPE,
2570                (muteAlarms || muteCalls) ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
2571                exceptionPackages);
2572    }
2573
2574    private void updateRelatedUserCache(Context context) {
2575        UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
2576        int currentUserId = ActivityManager.getCurrentUser();
2577        if (userManager != null) {
2578            List<UserInfo> relatedUsers = userManager.getRelatedUsers(currentUserId);
2579            synchronized (mRelatedUsers) {
2580                mRelatedUsers.clear();
2581                for (UserInfo related : relatedUsers) {
2582                    mRelatedUsers.put(related.id, related);
2583                }
2584            }
2585        }
2586    }
2587
2588    private boolean isCall(String pkg, Notification n) {
2589        return CALL_PACKAGES.contains(pkg);
2590    }
2591
2592    private boolean isAlarm(String pkg, Notification n) {
2593        return ALARM_PACKAGES.contains(pkg);
2594    }
2595
2596    private boolean shouldIntercept(String pkg, Notification n) {
2597        if (mZenMode == Settings.Global.ZEN_MODE_LIMITED) {
2598            return !isAlarm(pkg, n);
2599        } else if (mZenMode == Settings.Global.ZEN_MODE_FULL) {
2600            return true;
2601        }
2602        return false;
2603    }
2604}
2605