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