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