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