NotificationManagerService.java revision 3f19f60d654421eee5b35a92129081f08c977629
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 android.service.notification.NotificationRankerService.REASON_APP_CANCEL;
20import static android.service.notification.NotificationRankerService.REASON_APP_CANCEL_ALL;
21import static android.service.notification.NotificationRankerService.REASON_DELEGATE_CANCEL;
22import static android.service.notification.NotificationRankerService.REASON_DELEGATE_CANCEL_ALL;
23import static android.service.notification.NotificationRankerService.REASON_DELEGATE_CLICK;
24import static android.service.notification.NotificationRankerService.REASON_DELEGATE_ERROR;
25import static android.service.notification.NotificationRankerService.REASON_GROUP_OPTIMIZATION;
26import static android.service.notification.NotificationRankerService.REASON_GROUP_SUMMARY_CANCELED;
27import static android.service.notification.NotificationRankerService.REASON_LISTENER_CANCEL;
28import static android.service.notification.NotificationRankerService.REASON_LISTENER_CANCEL_ALL;
29import static android.service.notification.NotificationRankerService.REASON_PACKAGE_BANNED;
30import static android.service.notification.NotificationRankerService.REASON_PACKAGE_CHANGED;
31import static android.service.notification.NotificationRankerService.REASON_PACKAGE_SUSPENDED;
32import static android.service.notification.NotificationRankerService.REASON_PROFILE_TURNED_OFF;
33import static android.service.notification.NotificationRankerService.REASON_UNAUTOBUNDLED;
34import static android.service.notification.NotificationRankerService.REASON_USER_STOPPED;
35import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_EFFECTS;
36import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_NOTIFICATION_EFFECTS;
37import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS;
38import static android.service.notification.NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_OFF;
39import static android.service.notification.NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_ON;
40import static android.service.notification.NotificationListenerService.TRIM_FULL;
41import static android.service.notification.NotificationListenerService.TRIM_LIGHT;
42import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_DEFAULT;
43import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_NONE;
44import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
45
46import android.Manifest;
47import android.annotation.Nullable;
48import android.app.ActivityManager;
49import android.app.ActivityManagerNative;
50import android.app.AppGlobals;
51import android.app.AppOpsManager;
52import android.app.AutomaticZenRule;
53import android.app.IActivityManager;
54import android.app.INotificationManager;
55import android.app.ITransientNotification;
56import android.app.Notification;
57import android.app.NotificationManager;
58import android.app.NotificationManager.Policy;
59import android.app.PendingIntent;
60import android.app.StatusBarManager;
61import android.app.backup.BackupManager;
62import android.app.usage.UsageEvents;
63import android.app.usage.UsageStatsManagerInternal;
64import android.content.BroadcastReceiver;
65import android.content.ComponentName;
66import android.content.ContentResolver;
67import android.content.Context;
68import android.content.Intent;
69import android.content.IntentFilter;
70import android.content.pm.ApplicationInfo;
71import android.content.pm.IPackageManager;
72import android.content.pm.PackageInfo;
73import android.content.pm.PackageManager;
74import android.content.pm.PackageManager.NameNotFoundException;
75import android.content.pm.ParceledListSlice;
76import android.content.pm.UserInfo;
77import android.content.res.Resources;
78import android.database.ContentObserver;
79import android.media.AudioAttributes;
80import android.media.AudioManager;
81import android.media.AudioManagerInternal;
82import android.media.AudioSystem;
83import android.media.IRingtonePlayer;
84import android.net.Uri;
85import android.os.Binder;
86import android.os.Bundle;
87import android.os.Environment;
88import android.os.Handler;
89import android.os.HandlerThread;
90import android.os.IBinder;
91import android.os.IInterface;
92import android.os.Looper;
93import android.os.Message;
94import android.os.Process;
95import android.os.RemoteException;
96import android.os.SystemProperties;
97import android.os.UserHandle;
98import android.os.UserManager;
99import android.os.Vibrator;
100import android.provider.Settings;
101import android.service.notification.Adjustment;
102import android.service.notification.Condition;
103import android.service.notification.IConditionProvider;
104import android.service.notification.INotificationListener;
105import android.service.notification.IStatusBarNotificationHolder;
106import android.service.notification.NotificationRankerService;
107import android.service.notification.NotificationListenerService;
108import android.service.notification.NotificationRankingUpdate;
109import android.service.notification.StatusBarNotification;
110import android.service.notification.ZenModeConfig;
111import android.telephony.PhoneStateListener;
112import android.telephony.TelephonyManager;
113import android.text.TextUtils;
114import android.util.ArrayMap;
115import android.util.ArraySet;
116import android.util.AtomicFile;
117import android.util.Log;
118import android.util.Slog;
119import android.util.SparseArray;
120import android.util.Xml;
121import android.view.accessibility.AccessibilityEvent;
122import android.view.accessibility.AccessibilityManager;
123import android.widget.Toast;
124
125import com.android.internal.R;
126import com.android.internal.annotations.VisibleForTesting;
127import com.android.internal.statusbar.NotificationVisibility;
128import com.android.internal.util.FastXmlSerializer;
129import com.android.internal.util.Preconditions;
130import com.android.server.EventLogTags;
131import com.android.server.LocalServices;
132import com.android.server.SystemService;
133import com.android.server.lights.Light;
134import com.android.server.lights.LightsManager;
135import com.android.server.notification.ManagedServices.ManagedServiceInfo;
136import com.android.server.statusbar.StatusBarManagerInternal;
137import com.android.server.vr.VrManagerInternal;
138import com.android.server.notification.ManagedServices.UserProfiles;
139
140import libcore.io.IoUtils;
141
142import org.json.JSONException;
143import org.json.JSONObject;
144import org.xmlpull.v1.XmlPullParser;
145import org.xmlpull.v1.XmlPullParserException;
146import org.xmlpull.v1.XmlSerializer;
147
148import java.io.ByteArrayInputStream;
149import java.io.ByteArrayOutputStream;
150import java.io.File;
151import java.io.FileDescriptor;
152import java.io.FileInputStream;
153import java.io.FileNotFoundException;
154import java.io.FileOutputStream;
155import java.io.IOException;
156import java.io.InputStream;
157import java.io.OutputStream;
158import java.io.PrintWriter;
159import java.nio.charset.StandardCharsets;
160import java.util.ArrayDeque;
161import java.util.ArrayList;
162import java.util.Arrays;
163import java.util.Iterator;
164import java.util.List;
165import java.util.Map;
166import java.util.Map.Entry;
167import java.util.Objects;
168import java.util.Set;
169import java.util.concurrent.TimeUnit;
170
171/** {@hide} */
172public class NotificationManagerService extends SystemService {
173    static final String TAG = "NotificationService";
174    static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
175    public static final boolean ENABLE_CHILD_NOTIFICATIONS
176            = SystemProperties.getBoolean("debug.child_notifs", true);
177
178    static final int MAX_PACKAGE_NOTIFICATIONS = 50;
179
180    // message codes
181    static final int MESSAGE_TIMEOUT = 2;
182    static final int MESSAGE_SAVE_POLICY_FILE = 3;
183    static final int MESSAGE_SEND_RANKING_UPDATE = 4;
184    static final int MESSAGE_LISTENER_HINTS_CHANGED = 5;
185    static final int MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED = 6;
186
187    // ranking thread messages
188    private static final int MESSAGE_RECONSIDER_RANKING = 1000;
189    private static final int MESSAGE_RANKING_SORT = 1001;
190
191    static final int LONG_DELAY = 3500; // 3.5 seconds
192    static final int SHORT_DELAY = 2000; // 2 seconds
193
194    static final long[] DEFAULT_VIBRATE_PATTERN = {0, 250, 250, 250};
195
196    static final int VIBRATE_PATTERN_MAXLEN = 8 * 2 + 1; // up to eight bumps
197
198    static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_NOTIFICATION;
199
200    static final boolean ENABLE_BLOCKED_NOTIFICATIONS = true;
201    static final boolean ENABLE_BLOCKED_TOASTS = true;
202
203    // When #matchesCallFilter is called from the ringer, wait at most
204    // 3s to resolve the contacts. This timeout is required since
205    // ContactsProvider might take a long time to start up.
206    //
207    // Return STARRED_CONTACT when the timeout is hit in order to avoid
208    // missed calls in ZEN mode "Important".
209    static final int MATCHES_CALL_FILTER_CONTACTS_TIMEOUT_MS = 3000;
210    static final float MATCHES_CALL_FILTER_TIMEOUT_AFFINITY =
211            ValidateNotificationPeople.STARRED_CONTACT;
212
213    /** notification_enqueue status value for a newly enqueued notification. */
214    private static final int EVENTLOG_ENQUEUE_STATUS_NEW = 0;
215
216    /** notification_enqueue status value for an existing notification. */
217    private static final int EVENTLOG_ENQUEUE_STATUS_UPDATE = 1;
218
219    /** notification_enqueue status value for an ignored notification. */
220    private static final int EVENTLOG_ENQUEUE_STATUS_IGNORED = 2;
221    private String mRankerServicePackageName;
222
223    private IActivityManager mAm;
224    AudioManager mAudioManager;
225    AudioManagerInternal mAudioManagerInternal;
226    @Nullable StatusBarManagerInternal mStatusBar;
227    Vibrator mVibrator;
228    private VrManagerInternal mVrManagerInternal;
229
230    final IBinder mForegroundToken = new Binder();
231    private Handler mHandler;
232    private final HandlerThread mRankingThread = new HandlerThread("ranker",
233            Process.THREAD_PRIORITY_BACKGROUND);
234
235    private Light mNotificationLight;
236    Light mAttentionLight;
237    private int mDefaultNotificationColor;
238    private int mDefaultNotificationLedOn;
239
240    private int mDefaultNotificationLedOff;
241    private long[] mDefaultVibrationPattern;
242
243    private long[] mFallbackVibrationPattern;
244    private boolean mUseAttentionLight;
245    boolean mSystemReady;
246
247    private boolean mDisableNotificationEffects;
248    private int mCallState;
249    private String mSoundNotificationKey;
250    private String mVibrateNotificationKey;
251
252    private final SparseArray<ArraySet<ManagedServiceInfo>> mListenersDisablingEffects =
253            new SparseArray<ArraySet<ManagedServiceInfo>>();
254    private List<ComponentName> mEffectsSuppressors = new ArrayList<ComponentName>();
255    private int mListenerHints;  // right now, all hints are global
256    private int mInterruptionFilter = NotificationListenerService.INTERRUPTION_FILTER_UNKNOWN;
257
258    // for enabling and disabling notification pulse behavior
259    private boolean mScreenOn = true;
260    private boolean mInCall = false;
261    private boolean mNotificationPulseEnabled;
262
263    // used as a mutex for access to all active notifications & listeners
264    final ArrayList<NotificationRecord> mNotificationList =
265            new ArrayList<NotificationRecord>();
266    final ArrayMap<String, NotificationRecord> mNotificationsByKey =
267            new ArrayMap<String, NotificationRecord>();
268    final ArrayMap<String, String> mAutobundledSummaries = new ArrayMap<>();
269    final ArrayList<ToastRecord> mToastQueue = new ArrayList<ToastRecord>();
270    final ArrayMap<String, NotificationRecord> mSummaryByGroupKey = new ArrayMap<>();
271    final PolicyAccess mPolicyAccess = new PolicyAccess();
272
273    // The last key in this list owns the hardware.
274    ArrayList<String> mLights = new ArrayList<>();
275
276    private AppOpsManager mAppOps;
277    private UsageStatsManagerInternal mAppUsageStats;
278
279    private Archive mArchive;
280
281    // Persistent storage for notification policy
282    private AtomicFile mPolicyFile;
283
284    private static final int DB_VERSION = 1;
285
286    private static final String TAG_NOTIFICATION_POLICY = "notification-policy";
287    private static final String ATTR_VERSION = "version";
288
289    private RankingHelper mRankingHelper;
290
291    private final UserProfiles mUserProfiles = new UserProfiles();
292    private NotificationListeners mListeners;
293    private NotificationRankers mRankerServices;
294    private ConditionProviders mConditionProviders;
295    private NotificationUsageStats mUsageStats;
296
297    private static final int MY_UID = Process.myUid();
298    private static final int MY_PID = Process.myPid();
299    private RankingHandler mRankingHandler;
300
301    private static class Archive {
302        final int mBufferSize;
303        final ArrayDeque<StatusBarNotification> mBuffer;
304
305        public Archive(int size) {
306            mBufferSize = size;
307            mBuffer = new ArrayDeque<StatusBarNotification>(mBufferSize);
308        }
309
310        public String toString() {
311            final StringBuilder sb = new StringBuilder();
312            final int N = mBuffer.size();
313            sb.append("Archive (");
314            sb.append(N);
315            sb.append(" notification");
316            sb.append((N==1)?")":"s)");
317            return sb.toString();
318        }
319
320        public void record(StatusBarNotification nr) {
321            if (mBuffer.size() == mBufferSize) {
322                mBuffer.removeFirst();
323            }
324
325            // We don't want to store the heavy bits of the notification in the archive,
326            // but other clients in the system process might be using the object, so we
327            // store a (lightened) copy.
328            mBuffer.addLast(nr.cloneLight());
329        }
330
331        public Iterator<StatusBarNotification> descendingIterator() {
332            return mBuffer.descendingIterator();
333        }
334
335        public StatusBarNotification[] getArray(int count) {
336            if (count == 0) count = mBufferSize;
337            final StatusBarNotification[] a
338                    = new StatusBarNotification[Math.min(count, mBuffer.size())];
339            Iterator<StatusBarNotification> iter = descendingIterator();
340            int i=0;
341            while (iter.hasNext() && i < count) {
342                a[i++] = iter.next();
343            }
344            return a;
345        }
346
347    }
348
349    private void readPolicyXml(InputStream stream, boolean forRestore)
350            throws XmlPullParserException, NumberFormatException, IOException {
351        final XmlPullParser parser = Xml.newPullParser();
352        parser.setInput(stream, StandardCharsets.UTF_8.name());
353
354        while (parser.next() != END_DOCUMENT) {
355            mZenModeHelper.readXml(parser, forRestore);
356            mRankingHelper.readXml(parser, forRestore);
357        }
358    }
359
360    private void loadPolicyFile() {
361        if (DBG) Slog.d(TAG, "loadPolicyFile");
362        synchronized(mPolicyFile) {
363
364            FileInputStream infile = null;
365            try {
366                infile = mPolicyFile.openRead();
367                readPolicyXml(infile, false /*forRestore*/);
368            } catch (FileNotFoundException e) {
369                // No data yet
370            } catch (IOException e) {
371                Log.wtf(TAG, "Unable to read notification policy", e);
372            } catch (NumberFormatException e) {
373                Log.wtf(TAG, "Unable to parse notification policy", e);
374            } catch (XmlPullParserException e) {
375                Log.wtf(TAG, "Unable to parse notification policy", e);
376            } finally {
377                IoUtils.closeQuietly(infile);
378            }
379        }
380    }
381
382    public void savePolicyFile() {
383        mHandler.removeMessages(MESSAGE_SAVE_POLICY_FILE);
384        mHandler.sendEmptyMessage(MESSAGE_SAVE_POLICY_FILE);
385    }
386
387    private void handleSavePolicyFile() {
388        if (DBG) Slog.d(TAG, "handleSavePolicyFile");
389        synchronized (mPolicyFile) {
390            final FileOutputStream stream;
391            try {
392                stream = mPolicyFile.startWrite();
393            } catch (IOException e) {
394                Slog.w(TAG, "Failed to save policy file", e);
395                return;
396            }
397
398            try {
399                writePolicyXml(stream, false /*forBackup*/);
400                mPolicyFile.finishWrite(stream);
401            } catch (IOException e) {
402                Slog.w(TAG, "Failed to save policy file, restoring backup", e);
403                mPolicyFile.failWrite(stream);
404            }
405        }
406        BackupManager.dataChanged(getContext().getPackageName());
407    }
408
409    private void writePolicyXml(OutputStream stream, boolean forBackup) throws IOException {
410        final XmlSerializer out = new FastXmlSerializer();
411        out.setOutput(stream, StandardCharsets.UTF_8.name());
412        out.startDocument(null, true);
413        out.startTag(null, TAG_NOTIFICATION_POLICY);
414        out.attribute(null, ATTR_VERSION, Integer.toString(DB_VERSION));
415        mZenModeHelper.writeXml(out, forBackup);
416        mRankingHelper.writeXml(out, forBackup);
417        out.endTag(null, TAG_NOTIFICATION_POLICY);
418        out.endDocument();
419    }
420
421    /** Use this when you actually want to post a notification or toast.
422     *
423     * Unchecked. Not exposed via Binder, but can be called in the course of enqueue*().
424     */
425    private boolean noteNotificationOp(String pkg, int uid) {
426        if (mAppOps.noteOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
427                != AppOpsManager.MODE_ALLOWED) {
428            Slog.v(TAG, "notifications are disabled by AppOps for " + pkg);
429            return false;
430        }
431        return true;
432    }
433
434    /** Use this to check if a package can post a notification or toast. */
435    private boolean checkNotificationOp(String pkg, int uid) {
436        return mAppOps.checkOp(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
437                == AppOpsManager.MODE_ALLOWED && !isPackageSuspendedForUser(pkg, uid);
438    }
439
440    private static final class ToastRecord
441    {
442        final int pid;
443        final String pkg;
444        final ITransientNotification callback;
445        int duration;
446
447        ToastRecord(int pid, String pkg, ITransientNotification callback, int duration)
448        {
449            this.pid = pid;
450            this.pkg = pkg;
451            this.callback = callback;
452            this.duration = duration;
453        }
454
455        void update(int duration) {
456            this.duration = duration;
457        }
458
459        void dump(PrintWriter pw, String prefix, DumpFilter filter) {
460            if (filter != null && !filter.matches(pkg)) return;
461            pw.println(prefix + this);
462        }
463
464        @Override
465        public final String toString()
466        {
467            return "ToastRecord{"
468                + Integer.toHexString(System.identityHashCode(this))
469                + " pkg=" + pkg
470                + " callback=" + callback
471                + " duration=" + duration;
472        }
473    }
474
475    private final NotificationDelegate mNotificationDelegate = new NotificationDelegate() {
476
477        @Override
478        public void onSetDisabled(int status) {
479            synchronized (mNotificationList) {
480                mDisableNotificationEffects =
481                        (status & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0;
482                if (disableNotificationEffects(null) != null) {
483                    // cancel whatever's going on
484                    long identity = Binder.clearCallingIdentity();
485                    try {
486                        final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
487                        if (player != null) {
488                            player.stopAsync();
489                        }
490                    } catch (RemoteException e) {
491                    } finally {
492                        Binder.restoreCallingIdentity(identity);
493                    }
494
495                    identity = Binder.clearCallingIdentity();
496                    try {
497                        mVibrator.cancel();
498                    } finally {
499                        Binder.restoreCallingIdentity(identity);
500                    }
501                }
502            }
503        }
504
505        @Override
506        public void onClearAll(int callingUid, int callingPid, int userId) {
507            synchronized (mNotificationList) {
508                cancelAllLocked(callingUid, callingPid, userId, REASON_DELEGATE_CANCEL_ALL, null,
509                        /*includeCurrentProfiles*/ true);
510            }
511        }
512
513        @Override
514        public void onNotificationClick(int callingUid, int callingPid, String key) {
515            synchronized (mNotificationList) {
516                NotificationRecord r = mNotificationsByKey.get(key);
517                if (r == null) {
518                    Log.w(TAG, "No notification with key: " + key);
519                    return;
520                }
521                final long now = System.currentTimeMillis();
522                EventLogTags.writeNotificationClicked(key,
523                        r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
524
525                StatusBarNotification sbn = r.sbn;
526                cancelNotification(callingUid, callingPid, sbn.getPackageName(), sbn.getTag(),
527                        sbn.getId(), Notification.FLAG_AUTO_CANCEL,
528                        Notification.FLAG_FOREGROUND_SERVICE, false, r.getUserId(),
529                        REASON_DELEGATE_CLICK, null);
530            }
531        }
532
533        @Override
534        public void onNotificationActionClick(int callingUid, int callingPid, String key,
535                int actionIndex) {
536            synchronized (mNotificationList) {
537                NotificationRecord r = mNotificationsByKey.get(key);
538                if (r == null) {
539                    Log.w(TAG, "No notification with key: " + key);
540                    return;
541                }
542                final long now = System.currentTimeMillis();
543                EventLogTags.writeNotificationActionClicked(key, actionIndex,
544                        r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
545                // TODO: Log action click via UsageStats.
546            }
547        }
548
549        @Override
550        public void onNotificationClear(int callingUid, int callingPid,
551                String pkg, String tag, int id, int userId) {
552            cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
553                    Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
554                    true, userId, REASON_DELEGATE_CANCEL, null);
555        }
556
557        @Override
558        public void onPanelRevealed(boolean clearEffects, int items) {
559            EventLogTags.writeNotificationPanelRevealed(items);
560            if (clearEffects) {
561                clearEffects();
562            }
563        }
564
565        @Override
566        public void onPanelHidden() {
567            EventLogTags.writeNotificationPanelHidden();
568        }
569
570        @Override
571        public void clearEffects() {
572            synchronized (mNotificationList) {
573                if (DBG) Slog.d(TAG, "clearEffects");
574                clearSoundLocked();
575                clearVibrateLocked();
576                clearLightsLocked();
577            }
578        }
579
580        @Override
581        public void onNotificationError(int callingUid, int callingPid, String pkg, String tag, int id,
582                int uid, int initialPid, String message, int userId) {
583            Slog.d(TAG, "onNotification error pkg=" + pkg + " tag=" + tag + " id=" + id
584                    + "; will crashApplication(uid=" + uid + ", pid=" + initialPid + ")");
585            cancelNotification(callingUid, callingPid, pkg, tag, id, 0, 0, false, userId,
586                    REASON_DELEGATE_ERROR, null);
587            long ident = Binder.clearCallingIdentity();
588            try {
589                ActivityManagerNative.getDefault().crashApplication(uid, initialPid, pkg,
590                        "Bad notification posted from package " + pkg
591                        + ": " + message);
592            } catch (RemoteException e) {
593            }
594            Binder.restoreCallingIdentity(ident);
595        }
596
597        @Override
598        public void onNotificationVisibilityChanged(NotificationVisibility[] newlyVisibleKeys,
599                NotificationVisibility[] noLongerVisibleKeys) {
600            synchronized (mNotificationList) {
601                for (NotificationVisibility nv : newlyVisibleKeys) {
602                    NotificationRecord r = mNotificationsByKey.get(nv.key);
603                    if (r == null) continue;
604                    r.setVisibility(true, nv.rank);
605                    nv.recycle();
606                }
607                // Note that we might receive this event after notifications
608                // have already left the system, e.g. after dismissing from the
609                // shade. Hence not finding notifications in
610                // mNotificationsByKey is not an exceptional condition.
611                for (NotificationVisibility nv : noLongerVisibleKeys) {
612                    NotificationRecord r = mNotificationsByKey.get(nv.key);
613                    if (r == null) continue;
614                    r.setVisibility(false, nv.rank);
615                    nv.recycle();
616                }
617            }
618        }
619
620        @Override
621        public void onNotificationExpansionChanged(String key,
622                boolean userAction, boolean expanded) {
623            synchronized (mNotificationList) {
624                NotificationRecord r = mNotificationsByKey.get(key);
625                if (r != null) {
626                    r.stats.onExpansionChanged(userAction, expanded);
627                    final long now = System.currentTimeMillis();
628                    EventLogTags.writeNotificationExpansion(key,
629                            userAction ? 1 : 0, expanded ? 1 : 0,
630                            r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
631                }
632            }
633        }
634    };
635
636    private void clearSoundLocked() {
637        mSoundNotificationKey = null;
638        long identity = Binder.clearCallingIdentity();
639        try {
640            final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
641            if (player != null) {
642                player.stopAsync();
643            }
644        } catch (RemoteException e) {
645        } finally {
646            Binder.restoreCallingIdentity(identity);
647        }
648    }
649
650    private void clearVibrateLocked() {
651        mVibrateNotificationKey = null;
652        long identity = Binder.clearCallingIdentity();
653        try {
654            mVibrator.cancel();
655        } finally {
656            Binder.restoreCallingIdentity(identity);
657        }
658    }
659
660    private void clearLightsLocked() {
661        // light
662        mLights.clear();
663        updateLightsLocked();
664    }
665
666    private final BroadcastReceiver mPackageIntentReceiver = new BroadcastReceiver() {
667        @Override
668        public void onReceive(Context context, Intent intent) {
669            String action = intent.getAction();
670            if (action == null) {
671                return;
672            }
673
674            boolean queryRestart = false;
675            boolean queryRemove = false;
676            boolean packageChanged = false;
677            boolean cancelNotifications = true;
678            int reason = REASON_PACKAGE_CHANGED;
679
680            if (action.equals(Intent.ACTION_PACKAGE_ADDED)
681                    || (queryRemove=action.equals(Intent.ACTION_PACKAGE_REMOVED))
682                    || action.equals(Intent.ACTION_PACKAGE_RESTARTED)
683                    || (packageChanged=action.equals(Intent.ACTION_PACKAGE_CHANGED))
684                    || (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART))
685                    || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)
686                    || action.equals(Intent.ACTION_PACKAGES_SUSPENDED)) {
687                int changeUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
688                        UserHandle.USER_ALL);
689                String pkgList[] = null;
690                boolean queryReplace = queryRemove &&
691                        intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
692                if (DBG) Slog.i(TAG, "action=" + action + " queryReplace=" + queryReplace);
693                if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
694                    pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
695                } else if (action.equals(Intent.ACTION_PACKAGES_SUSPENDED)) {
696                    pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
697                    reason = REASON_PACKAGE_SUSPENDED;
698                } else if (queryRestart) {
699                    pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
700                } else {
701                    Uri uri = intent.getData();
702                    if (uri == null) {
703                        return;
704                    }
705                    String pkgName = uri.getSchemeSpecificPart();
706                    if (pkgName == null) {
707                        return;
708                    }
709                    if (packageChanged) {
710                        // We cancel notifications for packages which have just been disabled
711                        try {
712                            final IPackageManager pm = AppGlobals.getPackageManager();
713                            final int enabled = pm.getApplicationEnabledSetting(pkgName,
714                                    changeUserId != UserHandle.USER_ALL ? changeUserId :
715                                    UserHandle.USER_SYSTEM);
716                            if (enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED
717                                    || enabled == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
718                                cancelNotifications = false;
719                            }
720                        } catch (IllegalArgumentException e) {
721                            // Package doesn't exist; probably racing with uninstall.
722                            // cancelNotifications is already true, so nothing to do here.
723                            if (DBG) {
724                                Slog.i(TAG, "Exception trying to look up app enabled setting", e);
725                            }
726                        } catch (RemoteException e) {
727                            // Failed to talk to PackageManagerService Should never happen!
728                        }
729                    }
730                    pkgList = new String[]{pkgName};
731                }
732
733                if (pkgList != null && (pkgList.length > 0)) {
734                    for (String pkgName : pkgList) {
735                        if (cancelNotifications) {
736                            cancelAllNotificationsInt(MY_UID, MY_PID, pkgName, 0, 0, !queryRestart,
737                                    changeUserId, reason, null);
738                        }
739                    }
740                }
741                mListeners.onPackagesChanged(queryReplace, pkgList);
742                mRankerServices.onPackagesChanged(queryReplace, pkgList);
743                mConditionProviders.onPackagesChanged(queryReplace, pkgList);
744                mRankingHelper.onPackagesChanged(queryReplace, pkgList);
745            }
746        }
747    };
748
749    private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
750        @Override
751        public void onReceive(Context context, Intent intent) {
752            String action = intent.getAction();
753
754            if (action.equals(Intent.ACTION_SCREEN_ON)) {
755                // Keep track of screen on/off state, but do not turn off the notification light
756                // until user passes through the lock screen or views the notification.
757                mScreenOn = true;
758                updateNotificationPulse();
759            } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
760                mScreenOn = false;
761                updateNotificationPulse();
762            } else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) {
763                mInCall = TelephonyManager.EXTRA_STATE_OFFHOOK
764                        .equals(intent.getStringExtra(TelephonyManager.EXTRA_STATE));
765                updateNotificationPulse();
766            } else if (action.equals(Intent.ACTION_USER_STOPPED)) {
767                int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
768                if (userHandle >= 0) {
769                    cancelAllNotificationsInt(MY_UID, MY_PID, null, 0, 0, true, userHandle,
770                            REASON_USER_STOPPED, null);
771                }
772            } else if (action.equals(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)) {
773                int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
774                if (userHandle >= 0) {
775                    cancelAllNotificationsInt(MY_UID, MY_PID, null, 0, 0, true, userHandle,
776                            REASON_PROFILE_TURNED_OFF, null);
777                }
778            } else if (action.equals(Intent.ACTION_USER_PRESENT)) {
779                // turn off LED when user passes through lock screen
780                mNotificationLight.turnOff();
781                if (mStatusBar != null) {
782                    mStatusBar.notificationLightOff();
783                }
784            } else if (action.equals(Intent.ACTION_USER_SWITCHED)) {
785                final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
786                // reload per-user settings
787                mSettingsObserver.update(null);
788                mUserProfiles.updateCache(context);
789                // Refresh managed services
790                mConditionProviders.onUserSwitched(user);
791                mListeners.onUserSwitched(user);
792                mRankerServices.onUserSwitched(user);
793                mZenModeHelper.onUserSwitched(user);
794            } else if (action.equals(Intent.ACTION_USER_ADDED)) {
795                mUserProfiles.updateCache(context);
796            } else if (action.equals(Intent.ACTION_USER_REMOVED)) {
797                final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
798                mZenModeHelper.onUserRemoved(user);
799            } else if (action.equals(Intent.ACTION_USER_UNLOCKED)) {
800                final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
801                mConditionProviders.onUserUnlocked(user);
802                mListeners.onUserUnlocked(user);
803                mRankerServices.onUserUnlocked(user);
804                mZenModeHelper.onUserUnlocked(user);
805            }
806        }
807    };
808
809    private final class SettingsObserver extends ContentObserver {
810        private final Uri NOTIFICATION_LIGHT_PULSE_URI
811                = Settings.System.getUriFor(Settings.System.NOTIFICATION_LIGHT_PULSE);
812
813        SettingsObserver(Handler handler) {
814            super(handler);
815        }
816
817        void observe() {
818            ContentResolver resolver = getContext().getContentResolver();
819            resolver.registerContentObserver(NOTIFICATION_LIGHT_PULSE_URI,
820                    false, this, UserHandle.USER_ALL);
821            update(null);
822        }
823
824        @Override public void onChange(boolean selfChange, Uri uri) {
825            update(uri);
826        }
827
828        public void update(Uri uri) {
829            ContentResolver resolver = getContext().getContentResolver();
830            if (uri == null || NOTIFICATION_LIGHT_PULSE_URI.equals(uri)) {
831                boolean pulseEnabled = Settings.System.getInt(resolver,
832                            Settings.System.NOTIFICATION_LIGHT_PULSE, 0) != 0;
833                if (mNotificationPulseEnabled != pulseEnabled) {
834                    mNotificationPulseEnabled = pulseEnabled;
835                    updateNotificationPulse();
836                }
837            }
838        }
839    }
840
841    private SettingsObserver mSettingsObserver;
842    private ZenModeHelper mZenModeHelper;
843
844    private final Runnable mBuzzBeepBlinked = new Runnable() {
845        @Override
846        public void run() {
847            if (mStatusBar != null) {
848                mStatusBar.buzzBeepBlinked();
849            }
850        }
851    };
852
853    static long[] getLongArray(Resources r, int resid, int maxlen, long[] def) {
854        int[] ar = r.getIntArray(resid);
855        if (ar == null) {
856            return def;
857        }
858        final int len = ar.length > maxlen ? maxlen : ar.length;
859        long[] out = new long[len];
860        for (int i=0; i<len; i++) {
861            out[i] = ar[i];
862        }
863        return out;
864    }
865
866    public NotificationManagerService(Context context) {
867        super(context);
868    }
869
870    @VisibleForTesting
871    void setAudioManager(AudioManager audioMananger) {
872        mAudioManager = audioMananger;
873    }
874
875    @VisibleForTesting
876    void setVibrator(Vibrator vibrator) {
877        mVibrator = vibrator;
878    }
879
880    @VisibleForTesting
881    void setSystemReady(boolean systemReady) {
882        mSystemReady = systemReady;
883    }
884
885    @VisibleForTesting
886    void setHandler(Handler handler) {
887        mHandler = handler;
888    }
889
890    @Override
891    public void onStart() {
892        Resources resources = getContext().getResources();
893
894        mAm = ActivityManagerNative.getDefault();
895        mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
896        mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
897        mAppUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
898
899        // This is the package that contains the AOSP framework update.
900        mRankerServicePackageName = getContext().getPackageManager()
901                .getServicesSystemSharedLibraryPackageName();
902
903        mHandler = new WorkerHandler();
904        mRankingThread.start();
905        String[] extractorNames;
906        try {
907            extractorNames = resources.getStringArray(R.array.config_notificationSignalExtractors);
908        } catch (Resources.NotFoundException e) {
909            extractorNames = new String[0];
910        }
911        mUsageStats = new NotificationUsageStats(getContext());
912        mRankingHandler = new RankingHandlerWorker(mRankingThread.getLooper());
913        mRankingHelper = new RankingHelper(getContext(),
914                mRankingHandler,
915                mUsageStats,
916                extractorNames);
917        mConditionProviders = new ConditionProviders(getContext(), mHandler, mUserProfiles);
918        mZenModeHelper = new ZenModeHelper(getContext(), mHandler.getLooper(), mConditionProviders);
919        mZenModeHelper.addCallback(new ZenModeHelper.Callback() {
920            @Override
921            public void onConfigChanged() {
922                savePolicyFile();
923            }
924
925            @Override
926            void onZenModeChanged() {
927                sendRegisteredOnlyBroadcast(NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED);
928                getContext().sendBroadcastAsUser(
929                        new Intent(NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED_INTERNAL)
930                                .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT),
931                        UserHandle.ALL, android.Manifest.permission.MANAGE_NOTIFICATIONS);
932                synchronized(mNotificationList) {
933                    updateInterruptionFilterLocked();
934                }
935            }
936
937            @Override
938            void onPolicyChanged() {
939                sendRegisteredOnlyBroadcast(NotificationManager.ACTION_NOTIFICATION_POLICY_CHANGED);
940            }
941        });
942        final File systemDir = new File(Environment.getDataDirectory(), "system");
943        mPolicyFile = new AtomicFile(new File(systemDir, "notification_policy.xml"));
944
945        syncBlockDb();
946
947        // This is a MangedServices object that keeps track of the listeners.
948        mListeners = new NotificationListeners();
949
950        // This is a MangedServices object that keeps track of the ranker.
951        mRankerServices = new NotificationRankers();
952        // Find the updatable ranker and register it.
953        mRankerServices.registerRanker();
954
955        mStatusBar = getLocalService(StatusBarManagerInternal.class);
956        if (mStatusBar != null) {
957            mStatusBar.setNotificationDelegate(mNotificationDelegate);
958        }
959
960        final LightsManager lights = getLocalService(LightsManager.class);
961        mNotificationLight = lights.getLight(LightsManager.LIGHT_ID_NOTIFICATIONS);
962        mAttentionLight = lights.getLight(LightsManager.LIGHT_ID_ATTENTION);
963
964        mDefaultNotificationColor = resources.getColor(
965                R.color.config_defaultNotificationColor);
966        mDefaultNotificationLedOn = resources.getInteger(
967                R.integer.config_defaultNotificationLedOn);
968        mDefaultNotificationLedOff = resources.getInteger(
969                R.integer.config_defaultNotificationLedOff);
970
971        mDefaultVibrationPattern = getLongArray(resources,
972                R.array.config_defaultNotificationVibePattern,
973                VIBRATE_PATTERN_MAXLEN,
974                DEFAULT_VIBRATE_PATTERN);
975
976        mFallbackVibrationPattern = getLongArray(resources,
977                R.array.config_notificationFallbackVibePattern,
978                VIBRATE_PATTERN_MAXLEN,
979                DEFAULT_VIBRATE_PATTERN);
980
981        mUseAttentionLight = resources.getBoolean(R.bool.config_useAttentionLight);
982
983        // Don't start allowing notifications until the setup wizard has run once.
984        // After that, including subsequent boots, init with notifications turned on.
985        // This works on the first boot because the setup wizard will toggle this
986        // flag at least once and we'll go back to 0 after that.
987        if (0 == Settings.Global.getInt(getContext().getContentResolver(),
988                    Settings.Global.DEVICE_PROVISIONED, 0)) {
989            mDisableNotificationEffects = true;
990        }
991        mZenModeHelper.initZenMode();
992        mInterruptionFilter = mZenModeHelper.getZenModeListenerInterruptionFilter();
993
994        mUserProfiles.updateCache(getContext());
995        listenForCallState();
996
997        // register for various Intents
998        IntentFilter filter = new IntentFilter();
999        filter.addAction(Intent.ACTION_SCREEN_ON);
1000        filter.addAction(Intent.ACTION_SCREEN_OFF);
1001        filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
1002        filter.addAction(Intent.ACTION_USER_PRESENT);
1003        filter.addAction(Intent.ACTION_USER_STOPPED);
1004        filter.addAction(Intent.ACTION_USER_SWITCHED);
1005        filter.addAction(Intent.ACTION_USER_ADDED);
1006        filter.addAction(Intent.ACTION_USER_REMOVED);
1007        filter.addAction(Intent.ACTION_USER_UNLOCKED);
1008        filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
1009        getContext().registerReceiver(mIntentReceiver, filter);
1010
1011        IntentFilter pkgFilter = new IntentFilter();
1012        pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
1013        pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
1014        pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
1015        pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
1016        pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
1017        pkgFilter.addDataScheme("package");
1018        getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, pkgFilter, null,
1019                null);
1020
1021        IntentFilter suspendedPkgFilter = new IntentFilter();
1022        suspendedPkgFilter.addAction(Intent.ACTION_PACKAGES_SUSPENDED);
1023        getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL,
1024                suspendedPkgFilter, null, null);
1025
1026        IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
1027        getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, sdFilter, null,
1028                null);
1029
1030        mSettingsObserver = new SettingsObserver(mHandler);
1031
1032        mArchive = new Archive(resources.getInteger(
1033                R.integer.config_notificationServiceArchiveSize));
1034
1035        publishBinderService(Context.NOTIFICATION_SERVICE, mService);
1036        publishLocalService(NotificationManagerInternal.class, mInternalService);
1037    }
1038
1039    private void sendRegisteredOnlyBroadcast(String action) {
1040        getContext().sendBroadcastAsUser(new Intent(action)
1041                .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), UserHandle.ALL, null);
1042    }
1043
1044    /**
1045     * Make sure the XML config and the the AppOps system agree about blocks.
1046     */
1047    private void syncBlockDb() {
1048        loadPolicyFile();
1049
1050        // sync bans from ranker into app opps
1051        Map<Integer, String> packageBans = mRankingHelper.getPackageBans();
1052        for(Entry<Integer, String> ban : packageBans.entrySet()) {
1053            final int uid = ban.getKey();
1054            final String packageName = ban.getValue();
1055            setNotificationsEnabledForPackageImpl(packageName, uid, false);
1056        }
1057
1058        // sync bans from app opps into ranker
1059        packageBans.clear();
1060        for (UserInfo user : UserManager.get(getContext()).getUsers()) {
1061            final int userId = user.getUserHandle().getIdentifier();
1062            final PackageManager packageManager = getContext().getPackageManager();
1063            List<PackageInfo> packages = packageManager.getInstalledPackagesAsUser(0, userId);
1064            final int packageCount = packages.size();
1065            for (int p = 0; p < packageCount; p++) {
1066                final String packageName = packages.get(p).packageName;
1067                try {
1068                    final int uid = packageManager.getPackageUidAsUser(packageName, userId);
1069                    if (!checkNotificationOp(packageName, uid)) {
1070                        packageBans.put(uid, packageName);
1071                    }
1072                } catch (NameNotFoundException e) {
1073                    // forget you
1074                }
1075            }
1076        }
1077        for (Entry<Integer, String> ban : packageBans.entrySet()) {
1078            mRankingHelper.setImportance(ban.getValue(), ban.getKey(), IMPORTANCE_NONE);
1079        }
1080
1081        savePolicyFile();
1082    }
1083
1084    @Override
1085    public void onBootPhase(int phase) {
1086        if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
1087            // no beeping until we're basically done booting
1088            mSystemReady = true;
1089
1090            // Grab our optional AudioService
1091            mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
1092            mAudioManagerInternal = getLocalService(AudioManagerInternal.class);
1093            mVrManagerInternal = getLocalService(VrManagerInternal.class);
1094            mZenModeHelper.onSystemReady();
1095        } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
1096            // This observer will force an update when observe is called, causing us to
1097            // bind to listener services.
1098            mSettingsObserver.observe();
1099            mListeners.onBootPhaseAppsCanStart();
1100            mRankerServices.onBootPhaseAppsCanStart();
1101            mConditionProviders.onBootPhaseAppsCanStart();
1102        }
1103    }
1104
1105    void setNotificationsEnabledForPackageImpl(String pkg, int uid, boolean enabled) {
1106        Slog.v(TAG, (enabled?"en":"dis") + "abling notifications for " + pkg);
1107
1108        mAppOps.setMode(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg,
1109                enabled ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED);
1110
1111        // Now, cancel any outstanding notifications that are part of a just-disabled app
1112        if (ENABLE_BLOCKED_NOTIFICATIONS && !enabled) {
1113            cancelAllNotificationsInt(MY_UID, MY_PID, pkg, 0, 0, true, UserHandle.getUserId(uid),
1114                    REASON_PACKAGE_BANNED, null);
1115        }
1116    }
1117
1118    private void updateListenerHintsLocked() {
1119        final int hints = calculateHints();
1120        if (hints == mListenerHints) return;
1121        ZenLog.traceListenerHintsChanged(mListenerHints, hints, mEffectsSuppressors.size());
1122        mListenerHints = hints;
1123        scheduleListenerHintsChanged(hints);
1124    }
1125
1126    private void updateEffectsSuppressorLocked() {
1127        final long updatedSuppressedEffects = calculateSuppressedEffects();
1128        if (updatedSuppressedEffects == mZenModeHelper.getSuppressedEffects()) return;
1129        final List<ComponentName> suppressors = getSuppressors();
1130        ZenLog.traceEffectsSuppressorChanged(mEffectsSuppressors, suppressors, updatedSuppressedEffects);
1131        mEffectsSuppressors = suppressors;
1132        mZenModeHelper.setSuppressedEffects(updatedSuppressedEffects);
1133        sendRegisteredOnlyBroadcast(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED);
1134    }
1135
1136    private ArrayList<ComponentName> getSuppressors() {
1137        ArrayList<ComponentName> names = new ArrayList<ComponentName>();
1138        for (int i = mListenersDisablingEffects.size() - 1; i >= 0; --i) {
1139            ArraySet<ManagedServiceInfo> serviceInfoList = mListenersDisablingEffects.valueAt(i);
1140
1141            for (ManagedServiceInfo info : serviceInfoList) {
1142                names.add(info.component);
1143            }
1144        }
1145
1146        return names;
1147    }
1148
1149    private boolean removeDisabledHints(ManagedServiceInfo info) {
1150        return removeDisabledHints(info, 0);
1151    }
1152
1153    private boolean removeDisabledHints(ManagedServiceInfo info, int hints) {
1154        boolean removed = false;
1155
1156        for (int i = mListenersDisablingEffects.size() - 1; i >= 0; --i) {
1157            final int hint = mListenersDisablingEffects.keyAt(i);
1158            final ArraySet<ManagedServiceInfo> listeners =
1159                    mListenersDisablingEffects.valueAt(i);
1160
1161            if (hints == 0 || (hint & hints) == hint) {
1162                removed = removed || listeners.remove(info);
1163            }
1164        }
1165
1166        return removed;
1167    }
1168
1169    private void addDisabledHints(ManagedServiceInfo info, int hints) {
1170        if ((hints & HINT_HOST_DISABLE_EFFECTS) != 0) {
1171            addDisabledHint(info, HINT_HOST_DISABLE_EFFECTS);
1172        }
1173
1174        if ((hints & HINT_HOST_DISABLE_NOTIFICATION_EFFECTS) != 0) {
1175            addDisabledHint(info, HINT_HOST_DISABLE_NOTIFICATION_EFFECTS);
1176        }
1177
1178        if ((hints & HINT_HOST_DISABLE_CALL_EFFECTS) != 0) {
1179            addDisabledHint(info, HINT_HOST_DISABLE_CALL_EFFECTS);
1180        }
1181    }
1182
1183    private void addDisabledHint(ManagedServiceInfo info, int hint) {
1184        if (mListenersDisablingEffects.indexOfKey(hint) < 0) {
1185            mListenersDisablingEffects.put(hint, new ArraySet<ManagedServiceInfo>());
1186        }
1187
1188        ArraySet<ManagedServiceInfo> hintListeners = mListenersDisablingEffects.get(hint);
1189        hintListeners.add(info);
1190    }
1191
1192    private int calculateHints() {
1193        int hints = 0;
1194        for (int i = mListenersDisablingEffects.size() - 1; i >= 0; --i) {
1195            int hint = mListenersDisablingEffects.keyAt(i);
1196            ArraySet<ManagedServiceInfo> serviceInfoList = mListenersDisablingEffects.valueAt(i);
1197
1198            if (!serviceInfoList.isEmpty()) {
1199                hints |= hint;
1200            }
1201        }
1202
1203        return hints;
1204    }
1205
1206    private long calculateSuppressedEffects() {
1207        int hints = calculateHints();
1208        long suppressedEffects = 0;
1209
1210        if ((hints & HINT_HOST_DISABLE_EFFECTS) != 0) {
1211            suppressedEffects |= ZenModeHelper.SUPPRESSED_EFFECT_ALL;
1212        }
1213
1214        if ((hints & HINT_HOST_DISABLE_NOTIFICATION_EFFECTS) != 0) {
1215            suppressedEffects |= ZenModeHelper.SUPPRESSED_EFFECT_NOTIFICATIONS;
1216        }
1217
1218        if ((hints & HINT_HOST_DISABLE_CALL_EFFECTS) != 0) {
1219            suppressedEffects |= ZenModeHelper.SUPPRESSED_EFFECT_CALLS;
1220        }
1221
1222        return suppressedEffects;
1223    }
1224
1225    private void updateInterruptionFilterLocked() {
1226        int interruptionFilter = mZenModeHelper.getZenModeListenerInterruptionFilter();
1227        if (interruptionFilter == mInterruptionFilter) return;
1228        mInterruptionFilter = interruptionFilter;
1229        scheduleInterruptionFilterChanged(interruptionFilter);
1230    }
1231
1232    private final IBinder mService = new INotificationManager.Stub() {
1233        // Toasts
1234        // ============================================================================
1235
1236        @Override
1237        public void enqueueToast(String pkg, ITransientNotification callback, int duration)
1238        {
1239            if (DBG) {
1240                Slog.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback
1241                        + " duration=" + duration);
1242            }
1243
1244            if (pkg == null || callback == null) {
1245                Slog.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback);
1246                return ;
1247            }
1248
1249            final boolean isSystemToast = isCallerSystem() || ("android".equals(pkg));
1250            final boolean isPackageSuspended =
1251                    isPackageSuspendedForUser(pkg, Binder.getCallingUid());
1252
1253            if (ENABLE_BLOCKED_TOASTS && (!noteNotificationOp(pkg, Binder.getCallingUid())
1254                    || isPackageSuspended)) {
1255                if (!isSystemToast) {
1256                    Slog.e(TAG, "Suppressing toast from package " + pkg
1257                            + (isPackageSuspended
1258                                    ? " due to package suspended by administrator."
1259                                    : " by user request."));
1260                    return;
1261                }
1262            }
1263
1264            synchronized (mToastQueue) {
1265                int callingPid = Binder.getCallingPid();
1266                long callingId = Binder.clearCallingIdentity();
1267                try {
1268                    ToastRecord record;
1269                    int index = indexOfToastLocked(pkg, callback);
1270                    // If it's already in the queue, we update it in place, we don't
1271                    // move it to the end of the queue.
1272                    if (index >= 0) {
1273                        record = mToastQueue.get(index);
1274                        record.update(duration);
1275                    } else {
1276                        // Limit the number of toasts that any given package except the android
1277                        // package can enqueue.  Prevents DOS attacks and deals with leaks.
1278                        if (!isSystemToast) {
1279                            int count = 0;
1280                            final int N = mToastQueue.size();
1281                            for (int i=0; i<N; i++) {
1282                                 final ToastRecord r = mToastQueue.get(i);
1283                                 if (r.pkg.equals(pkg)) {
1284                                     count++;
1285                                     if (count >= MAX_PACKAGE_NOTIFICATIONS) {
1286                                         Slog.e(TAG, "Package has already posted " + count
1287                                                + " toasts. Not showing more. Package=" + pkg);
1288                                         return;
1289                                     }
1290                                 }
1291                            }
1292                        }
1293
1294                        record = new ToastRecord(callingPid, pkg, callback, duration);
1295                        mToastQueue.add(record);
1296                        index = mToastQueue.size() - 1;
1297                        keepProcessAliveLocked(callingPid);
1298                    }
1299                    // If it's at index 0, it's the current toast.  It doesn't matter if it's
1300                    // new or just been updated.  Call back and tell it to show itself.
1301                    // If the callback fails, this will remove it from the list, so don't
1302                    // assume that it's valid after this.
1303                    if (index == 0) {
1304                        showNextToastLocked();
1305                    }
1306                } finally {
1307                    Binder.restoreCallingIdentity(callingId);
1308                }
1309            }
1310        }
1311
1312        @Override
1313        public void cancelToast(String pkg, ITransientNotification callback) {
1314            Slog.i(TAG, "cancelToast pkg=" + pkg + " callback=" + callback);
1315
1316            if (pkg == null || callback == null) {
1317                Slog.e(TAG, "Not cancelling notification. pkg=" + pkg + " callback=" + callback);
1318                return ;
1319            }
1320
1321            synchronized (mToastQueue) {
1322                long callingId = Binder.clearCallingIdentity();
1323                try {
1324                    int index = indexOfToastLocked(pkg, callback);
1325                    if (index >= 0) {
1326                        cancelToastLocked(index);
1327                    } else {
1328                        Slog.w(TAG, "Toast already cancelled. pkg=" + pkg
1329                                + " callback=" + callback);
1330                    }
1331                } finally {
1332                    Binder.restoreCallingIdentity(callingId);
1333                }
1334            }
1335        }
1336
1337        @Override
1338        public void enqueueNotificationWithTag(String pkg, String opPkg, String tag, int id,
1339                Notification notification, int[] idOut, int userId) throws RemoteException {
1340            enqueueNotificationInternal(pkg, opPkg, Binder.getCallingUid(),
1341                    Binder.getCallingPid(), tag, id, notification, idOut, userId);
1342        }
1343
1344        @Override
1345        public void cancelNotificationWithTag(String pkg, String tag, int id, int userId) {
1346            checkCallerIsSystemOrSameApp(pkg);
1347            userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1348                    Binder.getCallingUid(), userId, true, false, "cancelNotificationWithTag", pkg);
1349            // Don't allow client applications to cancel foreground service notis or autobundled
1350            // summaries.
1351            cancelNotification(Binder.getCallingUid(), Binder.getCallingPid(), pkg, tag, id, 0,
1352                    (Binder.getCallingUid() == Process.SYSTEM_UID
1353                            ? 0 : Notification.FLAG_FOREGROUND_SERVICE)
1354                            | (Binder.getCallingUid() == Process.SYSTEM_UID
1355                            ? 0 : Notification.FLAG_AUTOGROUP_SUMMARY), false, userId,
1356                    REASON_APP_CANCEL, null);
1357        }
1358
1359        @Override
1360        public void cancelAllNotifications(String pkg, int userId) {
1361            checkCallerIsSystemOrSameApp(pkg);
1362
1363            userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1364                    Binder.getCallingUid(), userId, true, false, "cancelAllNotifications", pkg);
1365
1366            // Calling from user space, don't allow the canceling of actively
1367            // running foreground services.
1368            cancelAllNotificationsInt(Binder.getCallingUid(), Binder.getCallingPid(),
1369                    pkg, 0, Notification.FLAG_FOREGROUND_SERVICE, true, userId,
1370                    REASON_APP_CANCEL_ALL, null);
1371        }
1372
1373        @Override
1374        public void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) {
1375            checkCallerIsSystem();
1376
1377            setNotificationsEnabledForPackageImpl(pkg, uid, enabled);
1378            mRankingHelper.setEnabled(pkg, uid, enabled);
1379            savePolicyFile();
1380        }
1381
1382        /**
1383         * Use this when you just want to know if notifications are OK for this package.
1384         */
1385        @Override
1386        public boolean areNotificationsEnabled(String pkg) {
1387            return areNotificationsEnabledForPackage(pkg, Binder.getCallingUid());
1388        }
1389
1390        /**
1391         * Use this when you just want to know if notifications are OK for this package.
1392         */
1393        @Override
1394        public boolean areNotificationsEnabledForPackage(String pkg, int uid) {
1395            checkCallerIsSystemOrSameApp(pkg);
1396            return (mAppOps.checkOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
1397                    == AppOpsManager.MODE_ALLOWED) && !isPackageSuspendedForUser(pkg, uid);
1398        }
1399
1400        @Override
1401        public void setPriority(String pkg, int uid, int priority) {
1402            checkCallerIsSystem();
1403            mRankingHelper.setPriority(pkg, uid, priority);
1404            savePolicyFile();
1405        }
1406
1407        @Override
1408        public int getPriority(String pkg, int uid) {
1409            checkCallerIsSystem();
1410            return mRankingHelper.getPriority(pkg, uid);
1411        }
1412
1413        @Override
1414        public void setVisibilityOverride(String pkg, int uid, int visibility) {
1415            checkCallerIsSystem();
1416            mRankingHelper.setVisibilityOverride(pkg, uid, visibility);
1417            savePolicyFile();
1418        }
1419
1420        @Override
1421        public int getVisibilityOverride(String pkg, int uid) {
1422            checkCallerIsSystem();
1423            return mRankingHelper.getVisibilityOverride(pkg, uid);
1424        }
1425
1426        @Override
1427        public void setImportance(String pkg, int uid, int importance) {
1428            enforceSystemOrSystemUI("Caller not system or systemui");
1429            setNotificationsEnabledForPackageImpl(pkg, uid,
1430                    importance != NotificationListenerService.Ranking.IMPORTANCE_NONE);
1431            mRankingHelper.setImportance(pkg, uid, importance);
1432            savePolicyFile();
1433        }
1434
1435        @Override
1436        public int getPackageImportance(String pkg) {
1437            checkCallerIsSystemOrSameApp(pkg);
1438            return mRankingHelper.getImportance(pkg, Binder.getCallingUid());
1439        }
1440
1441        @Override
1442        public int getImportance(String pkg, int uid) {
1443            enforceSystemOrSystemUI("Caller not system or systemui");
1444            return mRankingHelper.getImportance(pkg, uid);
1445        }
1446
1447        /**
1448         * System-only API for getting a list of current (i.e. not cleared) notifications.
1449         *
1450         * Requires ACCESS_NOTIFICATIONS which is signature|system.
1451         * @returns A list of all the notifications, in natural order.
1452         */
1453        @Override
1454        public StatusBarNotification[] getActiveNotifications(String callingPkg) {
1455            // enforce() will ensure the calling uid has the correct permission
1456            getContext().enforceCallingOrSelfPermission(
1457                    android.Manifest.permission.ACCESS_NOTIFICATIONS,
1458                    "NotificationManagerService.getActiveNotifications");
1459
1460            StatusBarNotification[] tmp = null;
1461            int uid = Binder.getCallingUid();
1462
1463            // noteOp will check to make sure the callingPkg matches the uid
1464            if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
1465                    == AppOpsManager.MODE_ALLOWED) {
1466                synchronized (mNotificationList) {
1467                    tmp = new StatusBarNotification[mNotificationList.size()];
1468                    final int N = mNotificationList.size();
1469                    for (int i=0; i<N; i++) {
1470                        tmp[i] = mNotificationList.get(i).sbn;
1471                    }
1472                }
1473            }
1474            return tmp;
1475        }
1476
1477        /**
1478         * Public API for getting a list of current notifications for the calling package/uid.
1479         *
1480         * @returns A list of all the package's notifications, in natural order.
1481         */
1482        @Override
1483        public ParceledListSlice<StatusBarNotification> getAppActiveNotifications(String pkg,
1484                int incomingUserId) {
1485            checkCallerIsSystemOrSameApp(pkg);
1486            int userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1487                    Binder.getCallingUid(), incomingUserId, true, false,
1488                    "getAppActiveNotifications", pkg);
1489
1490            final ArrayList<StatusBarNotification> list
1491                    = new ArrayList<StatusBarNotification>(mNotificationList.size());
1492
1493            synchronized (mNotificationList) {
1494                final int N = mNotificationList.size();
1495                for (int i = 0; i < N; i++) {
1496                    final StatusBarNotification sbn = mNotificationList.get(i).sbn;
1497                    if (sbn.getPackageName().equals(pkg) && sbn.getUserId() == userId
1498                            && (sbn.getNotification().flags
1499                            & Notification.FLAG_AUTOGROUP_SUMMARY) == 0) {
1500                        // We could pass back a cloneLight() but clients might get confused and
1501                        // try to send this thing back to notify() again, which would not work
1502                        // very well.
1503                        final StatusBarNotification sbnOut = new StatusBarNotification(
1504                                sbn.getPackageName(),
1505                                sbn.getOpPkg(),
1506                                sbn.getId(), sbn.getTag(), sbn.getUid(), sbn.getInitialPid(),
1507                                0, // hide score from apps
1508                                sbn.getNotification().clone(),
1509                                sbn.getUser(), sbn.getPostTime());
1510                        list.add(sbnOut);
1511                    }
1512                }
1513            }
1514
1515            return new ParceledListSlice<StatusBarNotification>(list);
1516        }
1517
1518        /**
1519         * System-only API for getting a list of recent (cleared, no longer shown) notifications.
1520         *
1521         * Requires ACCESS_NOTIFICATIONS which is signature|system.
1522         */
1523        @Override
1524        public StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count) {
1525            // enforce() will ensure the calling uid has the correct permission
1526            getContext().enforceCallingOrSelfPermission(
1527                    android.Manifest.permission.ACCESS_NOTIFICATIONS,
1528                    "NotificationManagerService.getHistoricalNotifications");
1529
1530            StatusBarNotification[] tmp = null;
1531            int uid = Binder.getCallingUid();
1532
1533            // noteOp will check to make sure the callingPkg matches the uid
1534            if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
1535                    == AppOpsManager.MODE_ALLOWED) {
1536                synchronized (mArchive) {
1537                    tmp = mArchive.getArray(count);
1538                }
1539            }
1540            return tmp;
1541        }
1542
1543        /**
1544         * Register a listener binder directly with the notification manager.
1545         *
1546         * Only works with system callers. Apps should extend
1547         * {@link android.service.notification.NotificationListenerService}.
1548         */
1549        @Override
1550        public void registerListener(final INotificationListener listener,
1551                final ComponentName component, final int userid) {
1552            enforceSystemOrSystemUI("INotificationManager.registerListener");
1553            mListeners.registerService(listener, component, userid);
1554        }
1555
1556        /**
1557         * Remove a listener binder directly
1558         */
1559        @Override
1560        public void unregisterListener(INotificationListener token, int userid) {
1561            mListeners.unregisterService(token, userid);
1562        }
1563
1564        /**
1565         * Allow an INotificationListener to simulate a "clear all" operation.
1566         *
1567         * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onClearAllNotifications}
1568         *
1569         * @param token The binder for the listener, to check that the caller is allowed
1570         */
1571        @Override
1572        public void cancelNotificationsFromListener(INotificationListener token, String[] keys) {
1573            final int callingUid = Binder.getCallingUid();
1574            final int callingPid = Binder.getCallingPid();
1575            long identity = Binder.clearCallingIdentity();
1576            try {
1577                synchronized (mNotificationList) {
1578                    final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1579                    if (keys != null) {
1580                        final int N = keys.length;
1581                        for (int i = 0; i < N; i++) {
1582                            NotificationRecord r = mNotificationsByKey.get(keys[i]);
1583                            if (r == null) continue;
1584                            final int userId = r.sbn.getUserId();
1585                            if (userId != info.userid && userId != UserHandle.USER_ALL &&
1586                                    !mUserProfiles.isCurrentProfile(userId)) {
1587                                throw new SecurityException("Disallowed call from listener: "
1588                                        + info.service);
1589                            }
1590                            cancelNotificationFromListenerLocked(info, callingUid, callingPid,
1591                                    r.sbn.getPackageName(), r.sbn.getTag(), r.sbn.getId(),
1592                                    userId);
1593                        }
1594                    } else {
1595                        cancelAllLocked(callingUid, callingPid, info.userid,
1596                                REASON_LISTENER_CANCEL_ALL, info, info.supportsProfiles());
1597                    }
1598                }
1599            } finally {
1600                Binder.restoreCallingIdentity(identity);
1601            }
1602        }
1603
1604        /**
1605         * Handle request from an approved listener to re-enable itself.
1606         *
1607         * @param component The componenet to be re-enabled, caller must match package.
1608         */
1609        @Override
1610        public void requestBindListener(ComponentName component) {
1611            checkCallerIsSystemOrSameApp(component.getPackageName());
1612            long identity = Binder.clearCallingIdentity();
1613            try {
1614                ManagedServices manager =
1615                        mRankerServices.isComponentEnabledForCurrentProfiles(component)
1616                        ? mRankerServices
1617                        : mListeners;
1618                manager.setComponentState(component, true);
1619            } finally {
1620                Binder.restoreCallingIdentity(identity);
1621            }
1622        }
1623
1624        @Override
1625        public void requestUnbindListener(INotificationListener token) {
1626            long identity = Binder.clearCallingIdentity();
1627            try {
1628                // allow bound services to disable themselves
1629                final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1630                info.getOwner().setComponentState(info.component, false);
1631            } finally {
1632                Binder.restoreCallingIdentity(identity);
1633            }
1634        }
1635
1636        @Override
1637        public void setNotificationsShownFromListener(INotificationListener token, String[] keys) {
1638            long identity = Binder.clearCallingIdentity();
1639            try {
1640                synchronized (mNotificationList) {
1641                    final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1642                    if (keys != null) {
1643                        final int N = keys.length;
1644                        for (int i = 0; i < N; i++) {
1645                            NotificationRecord r = mNotificationsByKey.get(keys[i]);
1646                            if (r == null) continue;
1647                            final int userId = r.sbn.getUserId();
1648                            if (userId != info.userid && userId != UserHandle.USER_ALL &&
1649                                    !mUserProfiles.isCurrentProfile(userId)) {
1650                                throw new SecurityException("Disallowed call from listener: "
1651                                        + info.service);
1652                            }
1653                            if (!r.isSeen()) {
1654                                if (DBG) Slog.d(TAG, "Marking notification as seen " + keys[i]);
1655                                mAppUsageStats.reportEvent(r.sbn.getPackageName(),
1656                                        userId == UserHandle.USER_ALL ? UserHandle.USER_SYSTEM
1657                                                : userId,
1658                                        UsageEvents.Event.USER_INTERACTION);
1659                                r.setSeen();
1660                            }
1661                        }
1662                    }
1663                }
1664            } finally {
1665                Binder.restoreCallingIdentity(identity);
1666            }
1667        }
1668
1669        private void cancelNotificationFromListenerLocked(ManagedServiceInfo info,
1670                int callingUid, int callingPid, String pkg, String tag, int id, int userId) {
1671            cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
1672                    Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
1673                    true,
1674                    userId, REASON_LISTENER_CANCEL, info);
1675        }
1676
1677        /**
1678         * Allow an INotificationListener to simulate clearing (dismissing) a single notification.
1679         *
1680         * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onNotificationClear}
1681         *
1682         * @param token The binder for the listener, to check that the caller is allowed
1683         */
1684        @Override
1685        public void cancelNotificationFromListener(INotificationListener token, String pkg,
1686                String tag, int id) {
1687            final int callingUid = Binder.getCallingUid();
1688            final int callingPid = Binder.getCallingPid();
1689            long identity = Binder.clearCallingIdentity();
1690            try {
1691                synchronized (mNotificationList) {
1692                    final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1693                    if (info.supportsProfiles()) {
1694                        Log.e(TAG, "Ignoring deprecated cancelNotification(pkg, tag, id) "
1695                                + "from " + info.component
1696                                + " use cancelNotification(key) instead.");
1697                    } else {
1698                        cancelNotificationFromListenerLocked(info, callingUid, callingPid,
1699                                pkg, tag, id, info.userid);
1700                    }
1701                }
1702            } finally {
1703                Binder.restoreCallingIdentity(identity);
1704            }
1705        }
1706
1707        /**
1708         * Allow an INotificationListener to request the list of outstanding notifications seen by
1709         * the current user. Useful when starting up, after which point the listener callbacks
1710         * should be used.
1711         *
1712         * @param token The binder for the listener, to check that the caller is allowed
1713         * @param keys An array of notification keys to fetch, or null to fetch everything
1714         * @returns The return value will contain the notifications specified in keys, in that
1715         *      order, or if keys is null, all the notifications, in natural order.
1716         */
1717        @Override
1718        public ParceledListSlice<StatusBarNotification> getActiveNotificationsFromListener(
1719                INotificationListener token, String[] keys, int trim) {
1720            synchronized (mNotificationList) {
1721                final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1722                final boolean getKeys = keys != null;
1723                final int N = getKeys ? keys.length : mNotificationList.size();
1724                final ArrayList<StatusBarNotification> list
1725                        = new ArrayList<StatusBarNotification>(N);
1726                for (int i=0; i<N; i++) {
1727                    final NotificationRecord r = getKeys
1728                            ? mNotificationsByKey.get(keys[i])
1729                            : mNotificationList.get(i);
1730                    if (r == null) continue;
1731                    StatusBarNotification sbn = r.sbn;
1732                    if (!isVisibleToListener(sbn, info)) continue;
1733                    StatusBarNotification sbnToSend =
1734                            (trim == TRIM_FULL) ? sbn : sbn.cloneLight();
1735                    list.add(sbnToSend);
1736                }
1737                return new ParceledListSlice<StatusBarNotification>(list);
1738            }
1739        }
1740
1741        @Override
1742        public void requestHintsFromListener(INotificationListener token, int hints) {
1743            final long identity = Binder.clearCallingIdentity();
1744            try {
1745                synchronized (mNotificationList) {
1746                    final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1747                    final int disableEffectsMask = HINT_HOST_DISABLE_EFFECTS
1748                            | HINT_HOST_DISABLE_NOTIFICATION_EFFECTS
1749                            | HINT_HOST_DISABLE_CALL_EFFECTS;
1750                    final boolean disableEffects = (hints & disableEffectsMask) != 0;
1751                    if (disableEffects) {
1752                        addDisabledHints(info, hints);
1753                    } else {
1754                        removeDisabledHints(info, hints);
1755                    }
1756                    updateListenerHintsLocked();
1757                    updateEffectsSuppressorLocked();
1758                }
1759            } finally {
1760                Binder.restoreCallingIdentity(identity);
1761            }
1762        }
1763
1764        @Override
1765        public int getHintsFromListener(INotificationListener token) {
1766            synchronized (mNotificationList) {
1767                return mListenerHints;
1768            }
1769        }
1770
1771        @Override
1772        public void requestInterruptionFilterFromListener(INotificationListener token,
1773                int interruptionFilter) throws RemoteException {
1774            final long identity = Binder.clearCallingIdentity();
1775            try {
1776                synchronized (mNotificationList) {
1777                    final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1778                    mZenModeHelper.requestFromListener(info.component, interruptionFilter);
1779                    updateInterruptionFilterLocked();
1780                }
1781            } finally {
1782                Binder.restoreCallingIdentity(identity);
1783            }
1784        }
1785
1786        @Override
1787        public int getInterruptionFilterFromListener(INotificationListener token)
1788                throws RemoteException {
1789            synchronized (mNotificationLight) {
1790                return mInterruptionFilter;
1791            }
1792        }
1793
1794        @Override
1795        public void setOnNotificationPostedTrimFromListener(INotificationListener token, int trim)
1796                throws RemoteException {
1797            synchronized (mNotificationList) {
1798                final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1799                if (info == null) return;
1800                mListeners.setOnNotificationPostedTrimLocked(info, trim);
1801            }
1802        }
1803
1804        @Override
1805        public int getZenMode() {
1806            return mZenModeHelper.getZenMode();
1807        }
1808
1809        @Override
1810        public ZenModeConfig getZenModeConfig() {
1811            enforceSystemOrSystemUIOrVolume("INotificationManager.getZenModeConfig");
1812            return mZenModeHelper.getConfig();
1813        }
1814
1815        @Override
1816        public void setZenMode(int mode, Uri conditionId, String reason) throws RemoteException {
1817            enforceSystemOrSystemUIOrVolume("INotificationManager.setZenMode");
1818            final long identity = Binder.clearCallingIdentity();
1819            try {
1820                mZenModeHelper.setManualZenMode(mode, conditionId, reason);
1821            } finally {
1822                Binder.restoreCallingIdentity(identity);
1823            }
1824        }
1825
1826        @Override
1827        public List<ZenModeConfig.ZenRule> getZenRules() throws RemoteException {
1828            enforcePolicyAccess(Binder.getCallingUid(), "getAutomaticZenRules");
1829            return mZenModeHelper.getZenRules();
1830        }
1831
1832        @Override
1833        public AutomaticZenRule getAutomaticZenRule(String id) throws RemoteException {
1834            Preconditions.checkNotNull(id, "Id is null");
1835            enforcePolicyAccess(Binder.getCallingUid(), "getAutomaticZenRule");
1836            return mZenModeHelper.getAutomaticZenRule(id);
1837        }
1838
1839        @Override
1840        public String addAutomaticZenRule(AutomaticZenRule automaticZenRule)
1841                throws RemoteException {
1842            Preconditions.checkNotNull(automaticZenRule, "automaticZenRule is null");
1843            Preconditions.checkNotNull(automaticZenRule.getName(), "Name is null");
1844            Preconditions.checkNotNull(automaticZenRule.getOwner(), "Owner is null");
1845            Preconditions.checkNotNull(automaticZenRule.getConditionId(), "ConditionId is null");
1846            enforcePolicyAccess(Binder.getCallingUid(), "addAutomaticZenRule");
1847
1848            return mZenModeHelper.addAutomaticZenRule(automaticZenRule,
1849                    "addAutomaticZenRule");
1850        }
1851
1852        @Override
1853        public boolean updateAutomaticZenRule(String id, AutomaticZenRule automaticZenRule)
1854                throws RemoteException {
1855            Preconditions.checkNotNull(automaticZenRule, "automaticZenRule is null");
1856            Preconditions.checkNotNull(automaticZenRule.getName(), "Name is null");
1857            Preconditions.checkNotNull(automaticZenRule.getOwner(), "Owner is null");
1858            Preconditions.checkNotNull(automaticZenRule.getConditionId(), "ConditionId is null");
1859            enforcePolicyAccess(Binder.getCallingUid(), "updateAutomaticZenRule");
1860
1861            return mZenModeHelper.updateAutomaticZenRule(id, automaticZenRule,
1862                    "updateAutomaticZenRule");
1863        }
1864
1865        @Override
1866        public boolean removeAutomaticZenRule(String id) throws RemoteException {
1867            Preconditions.checkNotNull(id, "Id is null");
1868            // Verify that they can modify zen rules.
1869            enforcePolicyAccess(Binder.getCallingUid(), "removeAutomaticZenRule");
1870
1871            return mZenModeHelper.removeAutomaticZenRule(id, "removeAutomaticZenRule");
1872        }
1873
1874        @Override
1875        public boolean removeAutomaticZenRules(String packageName) throws RemoteException {
1876            Preconditions.checkNotNull(packageName, "Package name is null");
1877            enforceSystemOrSystemUI("removeAutomaticZenRules");
1878
1879            return mZenModeHelper.removeAutomaticZenRules(packageName, "removeAutomaticZenRules");
1880        }
1881
1882        @Override
1883        public int getRuleInstanceCount(ComponentName owner) throws RemoteException {
1884            Preconditions.checkNotNull(owner, "Owner is null");
1885            enforceSystemOrSystemUI("getRuleInstanceCount");
1886
1887            return mZenModeHelper.getCurrentInstanceCount(owner);
1888        }
1889
1890        @Override
1891        public void setInterruptionFilter(String pkg, int filter) throws RemoteException {
1892            enforcePolicyAccess(pkg, "setInterruptionFilter");
1893            final int zen = NotificationManager.zenModeFromInterruptionFilter(filter, -1);
1894            if (zen == -1) throw new IllegalArgumentException("Invalid filter: " + filter);
1895            final long identity = Binder.clearCallingIdentity();
1896            try {
1897                mZenModeHelper.setManualZenMode(zen, null, "setInterruptionFilter");
1898            } finally {
1899                Binder.restoreCallingIdentity(identity);
1900            }
1901        }
1902
1903        @Override
1904        public void notifyConditions(final String pkg, IConditionProvider provider,
1905                final Condition[] conditions) {
1906            final ManagedServiceInfo info = mConditionProviders.checkServiceToken(provider);
1907            checkCallerIsSystemOrSameApp(pkg);
1908            mHandler.post(new Runnable() {
1909                @Override
1910                public void run() {
1911                    mConditionProviders.notifyConditions(pkg, info, conditions);
1912                }
1913            });
1914        }
1915
1916        private void enforceSystemOrSystemUIOrVolume(String message) {
1917            if (mAudioManagerInternal != null) {
1918                final int vcuid = mAudioManagerInternal.getVolumeControllerUid();
1919                if (vcuid > 0 && Binder.getCallingUid() == vcuid) {
1920                    return;
1921                }
1922            }
1923            enforceSystemOrSystemUI(message);
1924        }
1925
1926        private void enforceSystemOrSystemUI(String message) {
1927            if (isCallerSystem()) return;
1928            getContext().enforceCallingPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
1929                    message);
1930        }
1931
1932        private void enforceSystemOrSystemUIOrSamePackage(String pkg, String message) {
1933            try {
1934                checkCallerIsSystemOrSameApp(pkg);
1935            } catch (SecurityException e) {
1936                getContext().enforceCallingPermission(
1937                        android.Manifest.permission.STATUS_BAR_SERVICE,
1938                        message);
1939            }
1940        }
1941
1942        private void enforcePolicyAccess(int uid, String method) {
1943            if (PackageManager.PERMISSION_GRANTED == getContext().checkCallingPermission(
1944                    android.Manifest.permission.MANAGE_NOTIFICATIONS)) {
1945                return;
1946            }
1947            boolean accessAllowed = false;
1948            String[] packages = getContext().getPackageManager().getPackagesForUid(uid);
1949            final int packageCount = packages.length;
1950            for (int i = 0; i < packageCount; i++) {
1951                if (checkPolicyAccess(packages[i])) {
1952                    accessAllowed = true;
1953                }
1954            }
1955            if (!accessAllowed) {
1956                Slog.w(TAG, "Notification policy access denied calling " + method);
1957                throw new SecurityException("Notification policy access denied");
1958            }
1959        }
1960
1961        private void enforcePolicyAccess(String pkg, String method) {
1962            if (PackageManager.PERMISSION_GRANTED == getContext().checkCallingPermission(
1963                    android.Manifest.permission.MANAGE_NOTIFICATIONS)) {
1964                return;
1965            }
1966            if (!checkPolicyAccess(pkg)) {
1967                Slog.w(TAG, "Notification policy access denied calling " + method);
1968                throw new SecurityException("Notification policy access denied");
1969            }
1970        }
1971
1972        private boolean checkPackagePolicyAccess(String pkg) {
1973            return mPolicyAccess.isPackageGranted(pkg);
1974        }
1975
1976        private boolean checkPolicyAccess(String pkg) {
1977            try {
1978                int uid = getContext().getPackageManager().getPackageUidAsUser(
1979                        pkg, UserHandle.getCallingUserId());
1980                if (PackageManager.PERMISSION_GRANTED == ActivityManager.checkComponentPermission(
1981                        android.Manifest.permission.MANAGE_NOTIFICATIONS, uid,
1982                        -1, true)) {
1983                    return true;
1984                }
1985            } catch (NameNotFoundException e) {
1986                return false;
1987            }
1988            return checkPackagePolicyAccess(pkg) || mListeners.isComponentEnabledForPackage(pkg);
1989        }
1990
1991        @Override
1992        protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1993            if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
1994                    != PackageManager.PERMISSION_GRANTED) {
1995                pw.println("Permission Denial: can't dump NotificationManager from pid="
1996                        + Binder.getCallingPid()
1997                        + ", uid=" + Binder.getCallingUid());
1998                return;
1999            }
2000
2001            final DumpFilter filter = DumpFilter.parseFromArguments(args);
2002            if (filter != null && filter.stats) {
2003                dumpJson(pw, filter);
2004            } else {
2005                dumpImpl(pw, filter);
2006            }
2007        }
2008
2009        @Override
2010        public ComponentName getEffectsSuppressor() {
2011            enforceSystemOrSystemUIOrVolume("INotificationManager.getEffectsSuppressor");
2012            return !mEffectsSuppressors.isEmpty() ? mEffectsSuppressors.get(0) : null;
2013        }
2014
2015        @Override
2016        public boolean matchesCallFilter(Bundle extras) {
2017            enforceSystemOrSystemUI("INotificationManager.matchesCallFilter");
2018            return mZenModeHelper.matchesCallFilter(
2019                    Binder.getCallingUserHandle(),
2020                    extras,
2021                    mRankingHelper.findExtractor(ValidateNotificationPeople.class),
2022                    MATCHES_CALL_FILTER_CONTACTS_TIMEOUT_MS,
2023                    MATCHES_CALL_FILTER_TIMEOUT_AFFINITY);
2024        }
2025
2026        @Override
2027        public boolean isSystemConditionProviderEnabled(String path) {
2028            enforceSystemOrSystemUIOrVolume("INotificationManager.isSystemConditionProviderEnabled");
2029            return mConditionProviders.isSystemProviderEnabled(path);
2030        }
2031
2032        // Backup/restore interface
2033        @Override
2034        public byte[] getBackupPayload(int user) {
2035            if (DBG) Slog.d(TAG, "getBackupPayload u=" + user);
2036            //TODO: http://b/22388012
2037            if (user != UserHandle.USER_SYSTEM) {
2038                Slog.w(TAG, "getBackupPayload: cannot backup policy for user " + user);
2039                return null;
2040            }
2041            final ByteArrayOutputStream baos = new ByteArrayOutputStream();
2042            try {
2043                writePolicyXml(baos, true /*forBackup*/);
2044                return baos.toByteArray();
2045            } catch (IOException e) {
2046                Slog.w(TAG, "getBackupPayload: error writing payload for user " + user, e);
2047            }
2048            return null;
2049        }
2050
2051        @Override
2052        public void applyRestore(byte[] payload, int user) {
2053            if (DBG) Slog.d(TAG, "applyRestore u=" + user + " payload="
2054                    + (payload != null ? new String(payload, StandardCharsets.UTF_8) : null));
2055            if (payload == null) {
2056                Slog.w(TAG, "applyRestore: no payload to restore for user " + user);
2057                return;
2058            }
2059            //TODO: http://b/22388012
2060            if (user != UserHandle.USER_SYSTEM) {
2061                Slog.w(TAG, "applyRestore: cannot restore policy for user " + user);
2062                return;
2063            }
2064            final ByteArrayInputStream bais = new ByteArrayInputStream(payload);
2065            try {
2066                readPolicyXml(bais, true /*forRestore*/);
2067                savePolicyFile();
2068            } catch (NumberFormatException | XmlPullParserException | IOException e) {
2069                Slog.w(TAG, "applyRestore: error reading payload", e);
2070            }
2071        }
2072
2073        @Override
2074        public boolean isNotificationPolicyAccessGranted(String pkg) {
2075            return checkPolicyAccess(pkg);
2076        }
2077
2078        @Override
2079        public boolean isNotificationPolicyAccessGrantedForPackage(String pkg) {;
2080            enforceSystemOrSystemUIOrSamePackage(pkg,
2081                    "request policy access status for another package");
2082            return checkPolicyAccess(pkg);
2083        }
2084
2085        @Override
2086        public String[] getPackagesRequestingNotificationPolicyAccess()
2087                throws RemoteException {
2088            enforceSystemOrSystemUI("request policy access packages");
2089            final long identity = Binder.clearCallingIdentity();
2090            try {
2091                return mPolicyAccess.getRequestingPackages();
2092            } finally {
2093                Binder.restoreCallingIdentity(identity);
2094            }
2095        }
2096
2097        @Override
2098        public void setNotificationPolicyAccessGranted(String pkg, boolean granted)
2099                throws RemoteException {
2100            enforceSystemOrSystemUI("grant notification policy access");
2101            final long identity = Binder.clearCallingIdentity();
2102            try {
2103                synchronized (mNotificationList) {
2104                    mPolicyAccess.put(pkg, granted);
2105                }
2106            } finally {
2107                Binder.restoreCallingIdentity(identity);
2108            }
2109        }
2110
2111        @Override
2112        public Policy getNotificationPolicy(String pkg) {
2113            enforcePolicyAccess(pkg, "getNotificationPolicy");
2114            final long identity = Binder.clearCallingIdentity();
2115            try {
2116                return mZenModeHelper.getNotificationPolicy();
2117            } finally {
2118                Binder.restoreCallingIdentity(identity);
2119            }
2120        }
2121
2122        @Override
2123        public void setNotificationPolicy(String pkg, Policy policy) {
2124            enforcePolicyAccess(pkg, "setNotificationPolicy");
2125            final long identity = Binder.clearCallingIdentity();
2126            try {
2127                mZenModeHelper.setNotificationPolicy(policy);
2128            } finally {
2129                Binder.restoreCallingIdentity(identity);
2130            }
2131        }
2132
2133        @Override
2134        public void applyAdjustmentFromRankerService(INotificationListener token,
2135                Adjustment adjustment) throws RemoteException {
2136            final long identity = Binder.clearCallingIdentity();
2137            try {
2138                synchronized (mNotificationList) {
2139                    mRankerServices.checkServiceTokenLocked(token);
2140                    applyAdjustmentLocked(adjustment);
2141                }
2142                maybeAddAutobundleSummary(adjustment);
2143                mRankingHandler.requestSort();
2144            } finally {
2145                Binder.restoreCallingIdentity(identity);
2146            }
2147        }
2148
2149        @Override
2150        public void applyAdjustmentsFromRankerService(INotificationListener token,
2151                List<Adjustment> adjustments) throws RemoteException {
2152
2153            final long identity = Binder.clearCallingIdentity();
2154            try {
2155                synchronized (mNotificationList) {
2156                    mRankerServices.checkServiceTokenLocked(token);
2157                    for (Adjustment adjustment : adjustments) {
2158                        applyAdjustmentLocked(adjustment);
2159                    }
2160                }
2161                for (Adjustment adjustment : adjustments) {
2162                    maybeAddAutobundleSummary(adjustment);
2163                }
2164                mRankingHandler.requestSort();
2165            } finally {
2166                Binder.restoreCallingIdentity(identity);
2167            }
2168        }
2169    };
2170
2171    private void applyAdjustmentLocked(Adjustment adjustment) {
2172        maybeClearAutobundleSummaryLocked(adjustment);
2173        NotificationRecord n = mNotificationsByKey.get(adjustment.getKey());
2174        if (n == null) {
2175            return;
2176        }
2177        if (adjustment.getImportance() != IMPORTANCE_NONE) {
2178            n.setImportance(adjustment.getImportance(), adjustment.getExplanation());
2179        }
2180        if (adjustment.getSignals() != null) {
2181            Bundle.setDefusable(adjustment.getSignals(), true);
2182            final String autoGroupKey = adjustment.getSignals().getString(
2183                    Adjustment.GROUP_KEY_OVERRIDE_KEY, null);
2184            if (autoGroupKey == null) {
2185                EventLogTags.writeNotificationUnautogrouped(adjustment.getKey());
2186            } else {
2187                EventLogTags.writeNotificationAutogrouped(adjustment.getKey());
2188            }
2189            n.sbn.setOverrideGroupKey(autoGroupKey);
2190        }
2191    }
2192
2193    // Clears the 'fake' auto-bunding summary.
2194    private void maybeClearAutobundleSummaryLocked(Adjustment adjustment) {
2195        if (adjustment.getSignals() != null) {
2196            Bundle.setDefusable(adjustment.getSignals(), true);
2197            if (adjustment.getSignals().containsKey(Adjustment.NEEDS_AUTOGROUPING_KEY)
2198                && !adjustment.getSignals().getBoolean(Adjustment.NEEDS_AUTOGROUPING_KEY, false)) {
2199                if (mAutobundledSummaries.containsKey(adjustment.getPackage())) {
2200                    // Clear summary.
2201                    final NotificationRecord removed = mNotificationsByKey.get(
2202                            mAutobundledSummaries.remove(adjustment.getPackage()));
2203                    if (removed != null) {
2204                        mNotificationList.remove(removed);
2205                        cancelNotificationLocked(removed, false, REASON_UNAUTOBUNDLED);
2206                    }
2207                }
2208            }
2209        }
2210    }
2211
2212    // Posts a 'fake' summary for a package that has exceeded the solo-notification limit.
2213    private void maybeAddAutobundleSummary(Adjustment adjustment) {
2214        if (adjustment.getSignals() != null) {
2215            Bundle.setDefusable(adjustment.getSignals(), true);
2216            if (adjustment.getSignals().getBoolean(Adjustment.NEEDS_AUTOGROUPING_KEY, false)) {
2217                final String newAutoBundleKey =
2218                        adjustment.getSignals().getString(Adjustment.GROUP_KEY_OVERRIDE_KEY, null);
2219                int userId = -1;
2220                NotificationRecord summaryRecord = null;
2221                synchronized (mNotificationList) {
2222                    if (!mAutobundledSummaries.containsKey(adjustment.getPackage())
2223                            && newAutoBundleKey != null) {
2224                        // Add summary
2225                        final StatusBarNotification adjustedSbn
2226                                = mNotificationsByKey.get(adjustment.getKey()).sbn;
2227
2228                        final ApplicationInfo appInfo =
2229                                adjustedSbn.getNotification().extras.getParcelable(
2230                                        Notification.EXTRA_BUILDER_APPLICATION_INFO);
2231                        final Bundle extras = new Bundle();
2232                        extras.putParcelable(Notification.EXTRA_BUILDER_APPLICATION_INFO, appInfo);
2233                        final Notification summaryNotification =
2234                                new Notification.Builder(getContext()).setSmallIcon(
2235                                        adjustedSbn.getNotification().getSmallIcon())
2236                                        .setGroupSummary(true)
2237                                        .setGroup(newAutoBundleKey)
2238                                        .setFlag(Notification.FLAG_AUTOGROUP_SUMMARY, true)
2239                                        .setFlag(Notification.FLAG_GROUP_SUMMARY, true)
2240                                        .build();
2241                        summaryNotification.extras.putAll(extras);
2242                        Intent appIntent = getContext().getPackageManager()
2243                                .getLaunchIntentForPackage(adjustment.getPackage());
2244                        if (appIntent != null) {
2245                            summaryNotification.contentIntent = PendingIntent.getActivityAsUser(
2246                                    getContext(), 0, appIntent, 0, null,
2247                                    UserHandle.of(adjustedSbn.getUserId()));
2248                        }
2249                        final StatusBarNotification summarySbn =
2250                                new StatusBarNotification(adjustedSbn.getPackageName(),
2251                                        adjustedSbn.getOpPkg(),
2252                                        Integer.MAX_VALUE, Adjustment.GROUP_KEY_OVERRIDE_KEY,
2253                                        adjustedSbn.getUid(), adjustedSbn.getInitialPid(),
2254                                        summaryNotification, adjustedSbn.getUser(),
2255                                        newAutoBundleKey,
2256                                        System.currentTimeMillis());
2257                        summaryRecord = new NotificationRecord(getContext(), summarySbn);
2258                        mAutobundledSummaries.put(adjustment.getPackage(), summarySbn.getKey());
2259                        userId = adjustedSbn.getUser().getIdentifier();
2260                    }
2261                }
2262                if (summaryRecord != null) {
2263                    mHandler.post(new EnqueueNotificationRunnable(userId, summaryRecord));
2264                }
2265            }
2266        }
2267    }
2268
2269    private String disableNotificationEffects(NotificationRecord record) {
2270        if (mDisableNotificationEffects) {
2271            return "booleanState";
2272        }
2273        if ((mListenerHints & HINT_HOST_DISABLE_EFFECTS) != 0) {
2274            return "listenerHints";
2275        }
2276        if (mCallState != TelephonyManager.CALL_STATE_IDLE && !mZenModeHelper.isCall(record)) {
2277            return "callState";
2278        }
2279        return null;
2280    };
2281
2282    private void dumpJson(PrintWriter pw, DumpFilter filter) {
2283        JSONObject dump = new JSONObject();
2284        try {
2285            dump.put("service", "Notification Manager");
2286            dump.put("bans", mRankingHelper.dumpBansJson(filter));
2287            dump.put("ranking", mRankingHelper.dumpJson(filter));
2288            dump.put("stats", mUsageStats.dumpJson(filter));
2289        } catch (JSONException e) {
2290            e.printStackTrace();
2291        }
2292        pw.println(dump);
2293    }
2294
2295    void dumpImpl(PrintWriter pw, DumpFilter filter) {
2296        pw.print("Current Notification Manager state");
2297        if (filter.filtered) {
2298            pw.print(" (filtered to "); pw.print(filter); pw.print(")");
2299        }
2300        pw.println(':');
2301        int N;
2302        final boolean zenOnly = filter.filtered && filter.zen;
2303
2304        if (!zenOnly) {
2305            synchronized (mToastQueue) {
2306                N = mToastQueue.size();
2307                if (N > 0) {
2308                    pw.println("  Toast Queue:");
2309                    for (int i=0; i<N; i++) {
2310                        mToastQueue.get(i).dump(pw, "    ", filter);
2311                    }
2312                    pw.println("  ");
2313                }
2314            }
2315        }
2316
2317        synchronized (mNotificationList) {
2318            if (!zenOnly) {
2319                N = mNotificationList.size();
2320                if (N > 0) {
2321                    pw.println("  Notification List:");
2322                    for (int i=0; i<N; i++) {
2323                        final NotificationRecord nr = mNotificationList.get(i);
2324                        if (filter.filtered && !filter.matches(nr.sbn)) continue;
2325                        nr.dump(pw, "    ", getContext(), filter.redact);
2326                    }
2327                    pw.println("  ");
2328                }
2329
2330                if (!filter.filtered) {
2331                    N = mLights.size();
2332                    if (N > 0) {
2333                        pw.println("  Lights List:");
2334                        for (int i=0; i<N; i++) {
2335                            if (i == N - 1) {
2336                                pw.print("  > ");
2337                            } else {
2338                                pw.print("    ");
2339                            }
2340                            pw.println(mLights.get(i));
2341                        }
2342                        pw.println("  ");
2343                    }
2344                    pw.println("  mUseAttentionLight=" + mUseAttentionLight);
2345                    pw.println("  mNotificationPulseEnabled=" + mNotificationPulseEnabled);
2346                    pw.println("  mSoundNotificationKey=" + mSoundNotificationKey);
2347                    pw.println("  mVibrateNotificationKey=" + mVibrateNotificationKey);
2348                    pw.println("  mDisableNotificationEffects=" + mDisableNotificationEffects);
2349                    pw.println("  mCallState=" + callStateToString(mCallState));
2350                    pw.println("  mSystemReady=" + mSystemReady);
2351                }
2352                pw.println("  mArchive=" + mArchive.toString());
2353                Iterator<StatusBarNotification> iter = mArchive.descendingIterator();
2354                int i=0;
2355                while (iter.hasNext()) {
2356                    final StatusBarNotification sbn = iter.next();
2357                    if (filter != null && !filter.matches(sbn)) continue;
2358                    pw.println("    " + sbn);
2359                    if (++i >= 5) {
2360                        if (iter.hasNext()) pw.println("    ...");
2361                        break;
2362                    }
2363                }
2364            }
2365
2366            if (!zenOnly) {
2367                pw.println("\n  Usage Stats:");
2368                mUsageStats.dump(pw, "    ", filter);
2369            }
2370
2371            if (!filter.filtered || zenOnly) {
2372                pw.println("\n  Zen Mode:");
2373                pw.print("    mInterruptionFilter="); pw.println(mInterruptionFilter);
2374                mZenModeHelper.dump(pw, "    ");
2375
2376                pw.println("\n  Zen Log:");
2377                ZenLog.dump(pw, "    ");
2378            }
2379
2380            if (!zenOnly) {
2381                pw.println("\n  Ranking Config:");
2382                mRankingHelper.dump(pw, "    ", filter);
2383
2384                pw.println("\n  Notification listeners:");
2385                mListeners.dump(pw, filter);
2386                pw.print("    mListenerHints: "); pw.println(mListenerHints);
2387                pw.print("    mListenersDisablingEffects: (");
2388                N = mListenersDisablingEffects.size();
2389                for (int i = 0; i < N; i++) {
2390                    final int hint = mListenersDisablingEffects.keyAt(i);
2391                    if (i > 0) pw.print(';');
2392                    pw.print("hint[" + hint + "]:");
2393
2394                    final ArraySet<ManagedServiceInfo> listeners =
2395                            mListenersDisablingEffects.valueAt(i);
2396                    final int listenerSize = listeners.size();
2397
2398                    for (int j = 0; j < listenerSize; j++) {
2399                        if (i > 0) pw.print(',');
2400                        final ManagedServiceInfo listener = listeners.valueAt(i);
2401                        pw.print(listener.component);
2402                    }
2403                }
2404                pw.println(')');
2405                pw.println("\n  mRankerServicePackageName: " + mRankerServicePackageName);
2406                pw.println("\n  Notification ranker services:");
2407                mRankerServices.dump(pw, filter);
2408            }
2409            pw.println("\n  Policy access:");
2410            pw.print("    mPolicyAccess: "); pw.println(mPolicyAccess);
2411
2412            pw.println("\n  Condition providers:");
2413            mConditionProviders.dump(pw, filter);
2414
2415            pw.println("\n  Group summaries:");
2416            for (Entry<String, NotificationRecord> entry : mSummaryByGroupKey.entrySet()) {
2417                NotificationRecord r = entry.getValue();
2418                pw.println("    " + entry.getKey() + " -> " + r.getKey());
2419                if (mNotificationsByKey.get(r.getKey()) != r) {
2420                    pw.println("!!!!!!LEAK: Record not found in mNotificationsByKey.");
2421                    r.dump(pw, "      ", getContext(), filter.redact);
2422                }
2423            }
2424        }
2425    }
2426
2427    /**
2428     * The private API only accessible to the system process.
2429     */
2430    private final NotificationManagerInternal mInternalService = new NotificationManagerInternal() {
2431        @Override
2432        public void enqueueNotification(String pkg, String opPkg, int callingUid, int callingPid,
2433                String tag, int id, Notification notification, int[] idReceived, int userId) {
2434            enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, notification,
2435                    idReceived, userId);
2436        }
2437
2438        @Override
2439        public void removeForegroundServiceFlagFromNotification(String pkg, int notificationId,
2440                int userId) {
2441            checkCallerIsSystem();
2442            synchronized (mNotificationList) {
2443                int i = indexOfNotificationLocked(pkg, null, notificationId, userId);
2444                if (i < 0) {
2445                    Log.d(TAG, "stripForegroundServiceFlag: Could not find notification with "
2446                            + "pkg=" + pkg + " / id=" + notificationId + " / userId=" + userId);
2447                    return;
2448                }
2449                NotificationRecord r = mNotificationList.get(i);
2450                StatusBarNotification sbn = r.sbn;
2451                // NoMan adds flags FLAG_NO_CLEAR and FLAG_ONGOING_EVENT when it sees
2452                // FLAG_FOREGROUND_SERVICE. Hence it's not enough to remove FLAG_FOREGROUND_SERVICE,
2453                // we have to revert to the flags we received initially *and* force remove
2454                // FLAG_FOREGROUND_SERVICE.
2455                sbn.getNotification().flags =
2456                        (r.mOriginalFlags & ~Notification.FLAG_FOREGROUND_SERVICE);
2457                mRankingHelper.sort(mNotificationList);
2458                mListeners.notifyPostedLocked(sbn, sbn /* oldSbn */);
2459            }
2460        }
2461    };
2462
2463    void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,
2464            final int callingPid, final String tag, final int id, final Notification notification,
2465            int[] idOut, int incomingUserId) {
2466        if (DBG) {
2467            Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id
2468                    + " notification=" + notification);
2469        }
2470        checkCallerIsSystemOrSameApp(pkg);
2471        final boolean isSystemNotification = isUidSystem(callingUid) || ("android".equals(pkg));
2472        final boolean isNotificationFromListener = mListeners.isListenerPackage(pkg);
2473
2474        final int userId = ActivityManager.handleIncomingUser(callingPid,
2475                callingUid, incomingUserId, true, false, "enqueueNotification", pkg);
2476        final UserHandle user = new UserHandle(userId);
2477
2478        // Fix the notification as best we can.
2479        try {
2480            final ApplicationInfo ai = getContext().getPackageManager().getApplicationInfoAsUser(
2481                    pkg, PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
2482                    (userId == UserHandle.USER_ALL) ? UserHandle.USER_SYSTEM : userId);
2483            Notification.addFieldsFromContext(ai, userId, notification);
2484        } catch (NameNotFoundException e) {
2485            Slog.e(TAG, "Cannot create a context for sending app", e);
2486            return;
2487        }
2488
2489        // Limit the number of notifications that any given package except the android
2490        // package or a registered listener can enqueue.  Prevents DOS attacks and deals with leaks.
2491        if (!isSystemNotification && !isNotificationFromListener) {
2492            synchronized (mNotificationList) {
2493                int count = 0;
2494                final int N = mNotificationList.size();
2495                for (int i=0; i<N; i++) {
2496                    final NotificationRecord r = mNotificationList.get(i);
2497                    if (r.sbn.getPackageName().equals(pkg) && r.sbn.getUserId() == userId) {
2498                        if (r.sbn.getId() == id && TextUtils.equals(r.sbn.getTag(), tag)) {
2499                            break;  // Allow updating existing notification
2500                        }
2501                        count++;
2502                        if (count >= MAX_PACKAGE_NOTIFICATIONS) {
2503                            Slog.e(TAG, "Package has already posted " + count
2504                                    + " notifications.  Not showing more.  package=" + pkg);
2505                            return;
2506                        }
2507                    }
2508                }
2509            }
2510        }
2511
2512        if (pkg == null || notification == null) {
2513            throw new IllegalArgumentException("null not allowed: pkg=" + pkg
2514                    + " id=" + id + " notification=" + notification);
2515        }
2516
2517        // Sanitize inputs
2518        notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN,
2519                Notification.PRIORITY_MAX);
2520
2521        // setup local book-keeping
2522        final StatusBarNotification n = new StatusBarNotification(
2523                pkg, opPkg, id, tag, callingUid, callingPid, 0, notification,
2524                user);
2525        final NotificationRecord r = new NotificationRecord(getContext(), n);
2526        mHandler.post(new EnqueueNotificationRunnable(userId, r));
2527
2528        idOut[0] = id;
2529    }
2530
2531    private class EnqueueNotificationRunnable implements Runnable {
2532        private final NotificationRecord r;
2533        private final int userId;
2534
2535        EnqueueNotificationRunnable(int userId, NotificationRecord r) {
2536            this.userId = userId;
2537            this.r = r;
2538        };
2539
2540        @Override
2541        public void run() {
2542
2543            synchronized (mNotificationList) {
2544                final StatusBarNotification n = r.sbn;
2545                if (DBG) Slog.d(TAG, "EnqueueNotificationRunnable.run for: " + n.getKey());
2546                NotificationRecord old = mNotificationsByKey.get(n.getKey());
2547                if (old != null) {
2548                    // Retain ranking information from previous record
2549                    r.copyRankingInformation(old);
2550                }
2551
2552                final int callingUid = n.getUid();
2553                final int callingPid = n.getInitialPid();
2554                final Notification notification = n.getNotification();
2555                final String pkg = n.getPackageName();
2556                final int id = n.getId();
2557                final String tag = n.getTag();
2558                final boolean isSystemNotification = isUidSystem(callingUid) ||
2559                        ("android".equals(pkg));
2560
2561                // Handle grouped notifications and bail out early if we
2562                // can to avoid extracting signals.
2563                handleGroupedNotificationLocked(r, old, callingUid, callingPid);
2564
2565                // This conditional is a dirty hack to limit the logging done on
2566                //     behalf of the download manager without affecting other apps.
2567                if (!pkg.equals("com.android.providers.downloads")
2568                        || Log.isLoggable("DownloadManager", Log.VERBOSE)) {
2569                    int enqueueStatus = EVENTLOG_ENQUEUE_STATUS_NEW;
2570                    if (old != null) {
2571                        enqueueStatus = EVENTLOG_ENQUEUE_STATUS_UPDATE;
2572                    }
2573                    EventLogTags.writeNotificationEnqueue(callingUid, callingPid,
2574                            pkg, id, tag, userId, notification.toString(),
2575                            enqueueStatus);
2576                }
2577
2578                mRankingHelper.extractSignals(r);
2579
2580                final boolean isPackageSuspended = isPackageSuspendedForUser(pkg, callingUid);
2581
2582                // blocked apps
2583                if (r.getImportance() == NotificationListenerService.Ranking.IMPORTANCE_NONE
2584                        || !noteNotificationOp(pkg, callingUid) || isPackageSuspended) {
2585                    if (!isSystemNotification) {
2586                        if (isPackageSuspended) {
2587                            Slog.e(TAG, "Suppressing notification from package due to package "
2588                                    + "suspended by administrator.");
2589                            mUsageStats.registerSuspendedByAdmin(r);
2590                        } else {
2591                            Slog.e(TAG, "Suppressing notification from package by user request.");
2592                            mUsageStats.registerBlocked(r);
2593                        }
2594                        return;
2595                    }
2596                }
2597
2598                // tell the ranker service about the notification
2599                if (mRankerServices.isEnabled()) {
2600                    mRankerServices.onNotificationEnqueued(r);
2601                    // TODO delay the code below here for 100ms or until there is an answer
2602                }
2603
2604
2605                int index = indexOfNotificationLocked(n.getKey());
2606                if (index < 0) {
2607                    mNotificationList.add(r);
2608                    mUsageStats.registerPostedByApp(r);
2609                } else {
2610                    old = mNotificationList.get(index);
2611                    mNotificationList.set(index, r);
2612                    mUsageStats.registerUpdatedByApp(r, old);
2613                    // Make sure we don't lose the foreground service state.
2614                    notification.flags |=
2615                            old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;
2616                    r.isUpdate = true;
2617                }
2618
2619                mNotificationsByKey.put(n.getKey(), r);
2620
2621                // Ensure if this is a foreground service that the proper additional
2622                // flags are set.
2623                if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
2624                    notification.flags |= Notification.FLAG_ONGOING_EVENT
2625                            | Notification.FLAG_NO_CLEAR;
2626                }
2627
2628                applyZenModeLocked(r);
2629                mRankingHelper.sort(mNotificationList);
2630
2631                if (notification.getSmallIcon() != null) {
2632                    StatusBarNotification oldSbn = (old != null) ? old.sbn : null;
2633                    mListeners.notifyPostedLocked(n, oldSbn);
2634                } else {
2635                    Slog.e(TAG, "Not posting notification without small icon: " + notification);
2636                    if (old != null && !old.isCanceled) {
2637                        mListeners.notifyRemovedLocked(n);
2638                    }
2639                    // ATTENTION: in a future release we will bail out here
2640                    // so that we do not play sounds, show lights, etc. for invalid
2641                    // notifications
2642                    Slog.e(TAG, "WARNING: In a future release this will crash the app: "
2643                            + n.getPackageName());
2644                }
2645
2646                buzzBeepBlinkLocked(r);
2647            }
2648        }
2649    }
2650
2651    /**
2652     * Ensures that grouped notification receive their special treatment.
2653     *
2654     * <p>Cancels group children if the new notification causes a group to lose
2655     * its summary.</p>
2656     *
2657     * <p>Updates mSummaryByGroupKey.</p>
2658     */
2659    private void handleGroupedNotificationLocked(NotificationRecord r, NotificationRecord old,
2660            int callingUid, int callingPid) {
2661        StatusBarNotification sbn = r.sbn;
2662        Notification n = sbn.getNotification();
2663        String group = sbn.getGroupKey();
2664        boolean isSummary = n.isGroupSummary();
2665
2666        Notification oldN = old != null ? old.sbn.getNotification() : null;
2667        String oldGroup = old != null ? old.sbn.getGroupKey() : null;
2668        boolean oldIsSummary = old != null && oldN.isGroupSummary();
2669
2670        if (oldIsSummary) {
2671            NotificationRecord removedSummary = mSummaryByGroupKey.remove(oldGroup);
2672            if (removedSummary != old) {
2673                String removedKey =
2674                        removedSummary != null ? removedSummary.getKey() : "<null>";
2675                Slog.w(TAG, "Removed summary didn't match old notification: old=" + old.getKey() +
2676                        ", removed=" + removedKey);
2677            }
2678        }
2679        if (isSummary) {
2680            mSummaryByGroupKey.put(group, r);
2681        }
2682
2683        // Clear out group children of the old notification if the update
2684        // causes the group summary to go away. This happens when the old
2685        // notification was a summary and the new one isn't, or when the old
2686        // notification was a summary and its group key changed.
2687        if (oldIsSummary && (!isSummary || !oldGroup.equals(group))) {
2688            cancelGroupChildrenLocked(old, callingUid, callingPid, null,
2689                    REASON_GROUP_SUMMARY_CANCELED);
2690        }
2691    }
2692
2693    @VisibleForTesting
2694    void buzzBeepBlinkLocked(NotificationRecord record) {
2695        boolean buzz = false;
2696        boolean beep = false;
2697        boolean blink = false;
2698
2699        final Notification notification = record.sbn.getNotification();
2700        final String key = record.getKey();
2701
2702        // Should this notification make noise, vibe, or use the LED?
2703        final boolean aboveThreshold = record.getImportance() >= IMPORTANCE_DEFAULT;
2704        final boolean canInterrupt = aboveThreshold && !record.isIntercepted();
2705        if (DBG || record.isIntercepted())
2706            Slog.v(TAG,
2707                    "pkg=" + record.sbn.getPackageName() + " canInterrupt=" + canInterrupt +
2708                            " intercept=" + record.isIntercepted()
2709            );
2710
2711        final int currentUser;
2712        final long token = Binder.clearCallingIdentity();
2713        try {
2714            currentUser = ActivityManager.getCurrentUser();
2715        } finally {
2716            Binder.restoreCallingIdentity(token);
2717        }
2718
2719        // If we're not supposed to beep, vibrate, etc. then don't.
2720        final String disableEffects = disableNotificationEffects(record);
2721        if (disableEffects != null) {
2722            ZenLog.traceDisableEffects(record, disableEffects);
2723        }
2724
2725        // Remember if this notification already owns the notification channels.
2726        boolean wasBeep = key != null && key.equals(mSoundNotificationKey);
2727        boolean wasBuzz = key != null && key.equals(mVibrateNotificationKey);
2728
2729        // These are set inside the conditional if the notification is allowed to make noise.
2730        boolean hasValidVibrate = false;
2731        boolean hasValidSound = false;
2732        if (disableEffects == null
2733                && (record.getUserId() == UserHandle.USER_ALL ||
2734                    record.getUserId() == currentUser ||
2735                    mUserProfiles.isCurrentProfile(record.getUserId()))
2736                && canInterrupt
2737                && mSystemReady
2738                && mAudioManager != null) {
2739            if (DBG) Slog.v(TAG, "Interrupting!");
2740
2741            // should we use the default notification sound? (indicated either by
2742            // DEFAULT_SOUND or because notification.sound is pointing at
2743            // Settings.System.NOTIFICATION_SOUND)
2744            final boolean useDefaultSound =
2745                   (notification.defaults & Notification.DEFAULT_SOUND) != 0 ||
2746                           Settings.System.DEFAULT_NOTIFICATION_URI
2747                                   .equals(notification.sound);
2748
2749            Uri soundUri = null;
2750            if (useDefaultSound) {
2751                soundUri = Settings.System.DEFAULT_NOTIFICATION_URI;
2752
2753                // check to see if the default notification sound is silent
2754                ContentResolver resolver = getContext().getContentResolver();
2755                hasValidSound = Settings.System.getString(resolver,
2756                       Settings.System.NOTIFICATION_SOUND) != null;
2757            } else if (notification.sound != null) {
2758                soundUri = notification.sound;
2759                hasValidSound = (soundUri != null);
2760            }
2761
2762            // Does the notification want to specify its own vibration?
2763            final boolean hasCustomVibrate = notification.vibrate != null;
2764
2765            // new in 4.2: if there was supposed to be a sound and we're in vibrate
2766            // mode, and no other vibration is specified, we fall back to vibration
2767            final boolean convertSoundToVibration =
2768                    !hasCustomVibrate
2769                            && hasValidSound
2770                            && (mAudioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_VIBRATE);
2771
2772            // The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback.
2773            final boolean useDefaultVibrate =
2774                    (notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
2775
2776            hasValidVibrate = useDefaultVibrate || convertSoundToVibration ||
2777                    hasCustomVibrate;
2778
2779            // We can alert, and we're allowed to alert, but if the developer asked us to only do
2780            // it once, and we already have, then don't.
2781            if (!(record.isUpdate
2782                    && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0)) {
2783
2784                sendAccessibilityEvent(notification, record.sbn.getPackageName());
2785
2786                if (hasValidSound) {
2787                    boolean looping =
2788                            (notification.flags & Notification.FLAG_INSISTENT) != 0;
2789                    AudioAttributes audioAttributes = audioAttributesForNotification(notification);
2790                    mSoundNotificationKey = key;
2791                    // do not play notifications if stream volume is 0 (typically because
2792                    // ringer mode is silent) or if there is a user of exclusive audio focus
2793                    if ((mAudioManager.getStreamVolume(
2794                            AudioAttributes.toLegacyStreamType(audioAttributes)) != 0)
2795                            && !mAudioManager.isAudioFocusExclusive()) {
2796                        final long identity = Binder.clearCallingIdentity();
2797                        try {
2798                            final IRingtonePlayer player =
2799                                    mAudioManager.getRingtonePlayer();
2800                            if (player != null) {
2801                                if (DBG) Slog.v(TAG, "Playing sound " + soundUri
2802                                        + " with attributes " + audioAttributes);
2803                                player.playAsync(soundUri, record.sbn.getUser(), looping,
2804                                        audioAttributes);
2805                                beep = true;
2806                            }
2807                        } catch (RemoteException e) {
2808                        } finally {
2809                            Binder.restoreCallingIdentity(identity);
2810                        }
2811                    }
2812                }
2813
2814                if (hasValidVibrate && !(mAudioManager.getRingerModeInternal()
2815                        == AudioManager.RINGER_MODE_SILENT)) {
2816                    mVibrateNotificationKey = key;
2817
2818                    if (useDefaultVibrate || convertSoundToVibration) {
2819                        // Escalate privileges so we can use the vibrator even if the
2820                        // notifying app does not have the VIBRATE permission.
2821                        long identity = Binder.clearCallingIdentity();
2822                        try {
2823                            mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
2824                                    useDefaultVibrate ? mDefaultVibrationPattern
2825                                            : mFallbackVibrationPattern,
2826                                    ((notification.flags & Notification.FLAG_INSISTENT) != 0)
2827                                            ? 0: -1, audioAttributesForNotification(notification));
2828                            buzz = true;
2829                        } finally {
2830                            Binder.restoreCallingIdentity(identity);
2831                        }
2832                    } else if (notification.vibrate.length > 1) {
2833                        // If you want your own vibration pattern, you need the VIBRATE
2834                        // permission
2835                        mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
2836                                notification.vibrate,
2837                                ((notification.flags & Notification.FLAG_INSISTENT) != 0)
2838                                        ? 0: -1, audioAttributesForNotification(notification));
2839                        buzz = true;
2840                    }
2841                }
2842            }
2843
2844        }
2845        // If a notification is updated to remove the actively playing sound or vibrate,
2846        // cancel that feedback now
2847        if (wasBeep && !hasValidSound) {
2848            clearSoundLocked();
2849        }
2850        if (wasBuzz && !hasValidVibrate) {
2851            clearVibrateLocked();
2852        }
2853
2854        // light
2855        // release the light
2856        boolean wasShowLights = mLights.remove(key);
2857        if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0 && aboveThreshold
2858                && ((record.getSuppressedVisualEffects()
2859                & NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_OFF) == 0)) {
2860            mLights.add(key);
2861            updateLightsLocked();
2862            if (mUseAttentionLight) {
2863                mAttentionLight.pulse();
2864            }
2865            blink = true;
2866        } else if (wasShowLights) {
2867            updateLightsLocked();
2868        }
2869        if (buzz || beep || blink) {
2870            if (((record.getSuppressedVisualEffects()
2871                    & NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_OFF) != 0)) {
2872                if (DBG) Slog.v(TAG, "Suppressed SystemUI from triggering screen on");
2873            } else {
2874                EventLogTags.writeNotificationAlert(key,
2875                        buzz ? 1 : 0, beep ? 1 : 0, blink ? 1 : 0);
2876                mHandler.post(mBuzzBeepBlinked);
2877            }
2878        }
2879    }
2880
2881    private static AudioAttributes audioAttributesForNotification(Notification n) {
2882        if (n.audioAttributes != null
2883                && !Notification.AUDIO_ATTRIBUTES_DEFAULT.equals(n.audioAttributes)) {
2884            // the audio attributes are set and different from the default, use them
2885            return n.audioAttributes;
2886        } else if (n.audioStreamType >= 0 && n.audioStreamType < AudioSystem.getNumStreamTypes()) {
2887            // the stream type is valid, use it
2888            return new AudioAttributes.Builder()
2889                    .setInternalLegacyStreamType(n.audioStreamType)
2890                    .build();
2891        } else if (n.audioStreamType == AudioSystem.STREAM_DEFAULT) {
2892            return Notification.AUDIO_ATTRIBUTES_DEFAULT;
2893        } else {
2894            Log.w(TAG, String.format("Invalid stream type: %d", n.audioStreamType));
2895            return Notification.AUDIO_ATTRIBUTES_DEFAULT;
2896        }
2897    }
2898
2899    void showNextToastLocked() {
2900        ToastRecord record = mToastQueue.get(0);
2901        while (record != null) {
2902            if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback);
2903            try {
2904                record.callback.show();
2905                scheduleTimeoutLocked(record);
2906                return;
2907            } catch (RemoteException e) {
2908                Slog.w(TAG, "Object died trying to show notification " + record.callback
2909                        + " in package " + record.pkg);
2910                // remove it from the list and let the process die
2911                int index = mToastQueue.indexOf(record);
2912                if (index >= 0) {
2913                    mToastQueue.remove(index);
2914                }
2915                keepProcessAliveLocked(record.pid);
2916                if (mToastQueue.size() > 0) {
2917                    record = mToastQueue.get(0);
2918                } else {
2919                    record = null;
2920                }
2921            }
2922        }
2923    }
2924
2925    void cancelToastLocked(int index) {
2926        ToastRecord record = mToastQueue.get(index);
2927        try {
2928            record.callback.hide();
2929        } catch (RemoteException e) {
2930            Slog.w(TAG, "Object died trying to hide notification " + record.callback
2931                    + " in package " + record.pkg);
2932            // don't worry about this, we're about to remove it from
2933            // the list anyway
2934        }
2935        mToastQueue.remove(index);
2936        keepProcessAliveLocked(record.pid);
2937        if (mToastQueue.size() > 0) {
2938            // Show the next one. If the callback fails, this will remove
2939            // it from the list, so don't assume that the list hasn't changed
2940            // after this point.
2941            showNextToastLocked();
2942        }
2943    }
2944
2945    private void scheduleTimeoutLocked(ToastRecord r)
2946    {
2947        mHandler.removeCallbacksAndMessages(r);
2948        Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r);
2949        long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY;
2950        mHandler.sendMessageDelayed(m, delay);
2951    }
2952
2953    private void handleTimeout(ToastRecord record)
2954    {
2955        if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback);
2956        synchronized (mToastQueue) {
2957            int index = indexOfToastLocked(record.pkg, record.callback);
2958            if (index >= 0) {
2959                cancelToastLocked(index);
2960            }
2961        }
2962    }
2963
2964    // lock on mToastQueue
2965    int indexOfToastLocked(String pkg, ITransientNotification callback)
2966    {
2967        IBinder cbak = callback.asBinder();
2968        ArrayList<ToastRecord> list = mToastQueue;
2969        int len = list.size();
2970        for (int i=0; i<len; i++) {
2971            ToastRecord r = list.get(i);
2972            if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) {
2973                return i;
2974            }
2975        }
2976        return -1;
2977    }
2978
2979    // lock on mToastQueue
2980    void keepProcessAliveLocked(int pid)
2981    {
2982        int toastCount = 0; // toasts from this pid
2983        ArrayList<ToastRecord> list = mToastQueue;
2984        int N = list.size();
2985        for (int i=0; i<N; i++) {
2986            ToastRecord r = list.get(i);
2987            if (r.pid == pid) {
2988                toastCount++;
2989            }
2990        }
2991        try {
2992            mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0);
2993        } catch (RemoteException e) {
2994            // Shouldn't happen.
2995        }
2996    }
2997
2998    private void handleRankingReconsideration(Message message) {
2999        if (!(message.obj instanceof RankingReconsideration)) return;
3000        RankingReconsideration recon = (RankingReconsideration) message.obj;
3001        recon.run();
3002        boolean changed;
3003        synchronized (mNotificationList) {
3004            final NotificationRecord record = mNotificationsByKey.get(recon.getKey());
3005            if (record == null) {
3006                return;
3007            }
3008            int indexBefore = findNotificationRecordIndexLocked(record);
3009            boolean interceptBefore = record.isIntercepted();
3010            int visibilityBefore = record.getPackageVisibilityOverride();
3011            recon.applyChangesLocked(record);
3012            applyZenModeLocked(record);
3013            mRankingHelper.sort(mNotificationList);
3014            int indexAfter = findNotificationRecordIndexLocked(record);
3015            boolean interceptAfter = record.isIntercepted();
3016            int visibilityAfter = record.getPackageVisibilityOverride();
3017            changed = indexBefore != indexAfter || interceptBefore != interceptAfter
3018                    || visibilityBefore != visibilityAfter;
3019            if (interceptBefore && !interceptAfter) {
3020                buzzBeepBlinkLocked(record);
3021            }
3022        }
3023        if (changed) {
3024            scheduleSendRankingUpdate();
3025        }
3026    }
3027
3028    private void handleRankingSort() {
3029        synchronized (mNotificationList) {
3030            final int N = mNotificationList.size();
3031            ArrayList<String> orderBefore = new ArrayList<String>(N);
3032            ArrayList<String> groupOverrideBefore = new ArrayList<>(N);
3033            int[] visibilities = new int[N];
3034            int[] importances = new int[N];
3035            for (int i = 0; i < N; i++) {
3036                final NotificationRecord r = mNotificationList.get(i);
3037                orderBefore.add(r.getKey());
3038                groupOverrideBefore.add(r.sbn.getGroupKey());
3039                visibilities[i] = r.getPackageVisibilityOverride();
3040                importances[i] = r.getImportance();
3041                mRankingHelper.extractSignals(r);
3042            }
3043            mRankingHelper.sort(mNotificationList);
3044            for (int i = 0; i < N; i++) {
3045                final NotificationRecord r = mNotificationList.get(i);
3046                if (!orderBefore.get(i).equals(r.getKey())
3047                        || visibilities[i] != r.getPackageVisibilityOverride()
3048                        || importances[i] != r.getImportance()
3049                        || !groupOverrideBefore.get(i).equals(r.sbn.getGroupKey())) {
3050                    scheduleSendRankingUpdate();
3051                    return;
3052                }
3053            }
3054        }
3055    }
3056
3057    // let zen mode evaluate this record
3058    private void applyZenModeLocked(NotificationRecord record) {
3059        record.setIntercepted(mZenModeHelper.shouldIntercept(record));
3060        if (record.isIntercepted()) {
3061            int suppressed = (mZenModeHelper.shouldSuppressWhenScreenOff()
3062                    ? SUPPRESSED_EFFECT_SCREEN_OFF : 0)
3063                    | (mZenModeHelper.shouldSuppressWhenScreenOn()
3064                    ? SUPPRESSED_EFFECT_SCREEN_ON : 0);
3065            record.setSuppressedVisualEffects(suppressed);
3066        }
3067    }
3068
3069    // lock on mNotificationList
3070    private int findNotificationRecordIndexLocked(NotificationRecord target) {
3071        return mRankingHelper.indexOf(mNotificationList, target);
3072    }
3073
3074    private void scheduleSendRankingUpdate() {
3075        if (!mHandler.hasMessages(MESSAGE_SEND_RANKING_UPDATE)) {
3076            Message m = Message.obtain(mHandler, MESSAGE_SEND_RANKING_UPDATE);
3077            mHandler.sendMessage(m);
3078        }
3079    }
3080
3081    private void handleSendRankingUpdate() {
3082        synchronized (mNotificationList) {
3083            mListeners.notifyRankingUpdateLocked();
3084        }
3085    }
3086
3087    private void scheduleListenerHintsChanged(int state) {
3088        mHandler.removeMessages(MESSAGE_LISTENER_HINTS_CHANGED);
3089        mHandler.obtainMessage(MESSAGE_LISTENER_HINTS_CHANGED, state, 0).sendToTarget();
3090    }
3091
3092    private void scheduleInterruptionFilterChanged(int listenerInterruptionFilter) {
3093        mHandler.removeMessages(MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED);
3094        mHandler.obtainMessage(
3095                MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED,
3096                listenerInterruptionFilter,
3097                0).sendToTarget();
3098    }
3099
3100    private void handleListenerHintsChanged(int hints) {
3101        synchronized (mNotificationList) {
3102            mListeners.notifyListenerHintsChangedLocked(hints);
3103        }
3104    }
3105
3106    private void handleListenerInterruptionFilterChanged(int interruptionFilter) {
3107        synchronized (mNotificationList) {
3108            mListeners.notifyInterruptionFilterChanged(interruptionFilter);
3109        }
3110    }
3111
3112    private final class WorkerHandler extends Handler
3113    {
3114        @Override
3115        public void handleMessage(Message msg)
3116        {
3117            switch (msg.what)
3118            {
3119                case MESSAGE_TIMEOUT:
3120                    handleTimeout((ToastRecord)msg.obj);
3121                    break;
3122                case MESSAGE_SAVE_POLICY_FILE:
3123                    handleSavePolicyFile();
3124                    break;
3125                case MESSAGE_SEND_RANKING_UPDATE:
3126                    handleSendRankingUpdate();
3127                    break;
3128                case MESSAGE_LISTENER_HINTS_CHANGED:
3129                    handleListenerHintsChanged(msg.arg1);
3130                    break;
3131                case MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED:
3132                    handleListenerInterruptionFilterChanged(msg.arg1);
3133                    break;
3134            }
3135        }
3136
3137    }
3138
3139    private final class RankingHandlerWorker extends Handler implements RankingHandler
3140    {
3141        public RankingHandlerWorker(Looper looper) {
3142            super(looper);
3143        }
3144
3145        @Override
3146        public void handleMessage(Message msg) {
3147            switch (msg.what) {
3148                case MESSAGE_RECONSIDER_RANKING:
3149                    handleRankingReconsideration(msg);
3150                    break;
3151                case MESSAGE_RANKING_SORT:
3152                    handleRankingSort();
3153                    break;
3154            }
3155        }
3156
3157        public void requestSort() {
3158            removeMessages(MESSAGE_RANKING_SORT);
3159            sendEmptyMessage(MESSAGE_RANKING_SORT);
3160        }
3161
3162        public void requestReconsideration(RankingReconsideration recon) {
3163            Message m = Message.obtain(this,
3164                    NotificationManagerService.MESSAGE_RECONSIDER_RANKING, recon);
3165            long delay = recon.getDelay(TimeUnit.MILLISECONDS);
3166            sendMessageDelayed(m, delay);
3167        }
3168    }
3169
3170    // Notifications
3171    // ============================================================================
3172    static int clamp(int x, int low, int high) {
3173        return (x < low) ? low : ((x > high) ? high : x);
3174    }
3175
3176    void sendAccessibilityEvent(Notification notification, CharSequence packageName) {
3177        AccessibilityManager manager = AccessibilityManager.getInstance(getContext());
3178        if (!manager.isEnabled()) {
3179            return;
3180        }
3181
3182        AccessibilityEvent event =
3183            AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
3184        event.setPackageName(packageName);
3185        event.setClassName(Notification.class.getName());
3186        event.setParcelableData(notification);
3187        CharSequence tickerText = notification.tickerText;
3188        if (!TextUtils.isEmpty(tickerText)) {
3189            event.getText().add(tickerText);
3190        }
3191
3192        manager.sendAccessibilityEvent(event);
3193    }
3194
3195    private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason) {
3196        // tell the app
3197        if (sendDelete) {
3198            if (r.getNotification().deleteIntent != null) {
3199                try {
3200                    r.getNotification().deleteIntent.send();
3201                } catch (PendingIntent.CanceledException ex) {
3202                    // do nothing - there's no relevant way to recover, and
3203                    //     no reason to let this propagate
3204                    Slog.w(TAG, "canceled PendingIntent for " + r.sbn.getPackageName(), ex);
3205                }
3206            }
3207        }
3208
3209        // status bar
3210        if (r.getNotification().getSmallIcon() != null) {
3211            r.isCanceled = true;
3212            mListeners.notifyRemovedLocked(r.sbn);
3213        }
3214
3215        final String canceledKey = r.getKey();
3216
3217        // sound
3218        if (canceledKey.equals(mSoundNotificationKey)) {
3219            mSoundNotificationKey = null;
3220            final long identity = Binder.clearCallingIdentity();
3221            try {
3222                final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
3223                if (player != null) {
3224                    player.stopAsync();
3225                }
3226            } catch (RemoteException e) {
3227            } finally {
3228                Binder.restoreCallingIdentity(identity);
3229            }
3230        }
3231
3232        // vibrate
3233        if (canceledKey.equals(mVibrateNotificationKey)) {
3234            mVibrateNotificationKey = null;
3235            long identity = Binder.clearCallingIdentity();
3236            try {
3237                mVibrator.cancel();
3238            }
3239            finally {
3240                Binder.restoreCallingIdentity(identity);
3241            }
3242        }
3243
3244        // light
3245        mLights.remove(canceledKey);
3246
3247        // Record usage stats
3248        // TODO: add unbundling stats?
3249        switch (reason) {
3250            case REASON_DELEGATE_CANCEL:
3251            case REASON_DELEGATE_CANCEL_ALL:
3252            case REASON_LISTENER_CANCEL:
3253            case REASON_LISTENER_CANCEL_ALL:
3254                mUsageStats.registerDismissedByUser(r);
3255                break;
3256            case REASON_APP_CANCEL:
3257            case REASON_APP_CANCEL_ALL:
3258                mUsageStats.registerRemovedByApp(r);
3259                break;
3260        }
3261
3262        mNotificationsByKey.remove(r.sbn.getKey());
3263        String groupKey = r.getGroupKey();
3264        NotificationRecord groupSummary = mSummaryByGroupKey.get(groupKey);
3265        if (groupSummary != null && groupSummary.getKey().equals(r.getKey())) {
3266            mSummaryByGroupKey.remove(groupKey);
3267        }
3268        if (r.sbn.getKey().equals(mAutobundledSummaries.get(r.sbn.getPackageName()))) {
3269            mAutobundledSummaries.remove(r.sbn.getPackageName());
3270        }
3271
3272        // Save it for users of getHistoricalNotifications()
3273        mArchive.record(r.sbn);
3274
3275        final long now = System.currentTimeMillis();
3276        EventLogTags.writeNotificationCanceled(canceledKey, reason,
3277                r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
3278    }
3279
3280    /**
3281     * Cancels a notification ONLY if it has all of the {@code mustHaveFlags}
3282     * and none of the {@code mustNotHaveFlags}.
3283     */
3284    void cancelNotification(final int callingUid, final int callingPid,
3285            final String pkg, final String tag, final int id,
3286            final int mustHaveFlags, final int mustNotHaveFlags, final boolean sendDelete,
3287            final int userId, final int reason, final ManagedServiceInfo listener) {
3288        // In enqueueNotificationInternal notifications are added by scheduling the
3289        // work on the worker handler. Hence, we also schedule the cancel on this
3290        // handler to avoid a scenario where an add notification call followed by a
3291        // remove notification call ends up in not removing the notification.
3292        mHandler.post(new Runnable() {
3293            @Override
3294            public void run() {
3295                String listenerName = listener == null ? null : listener.component.toShortString();
3296                if (DBG) EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, id, tag,
3297                        userId, mustHaveFlags, mustNotHaveFlags, reason, listenerName);
3298
3299                synchronized (mNotificationList) {
3300                    int index = indexOfNotificationLocked(pkg, tag, id, userId);
3301                    if (index >= 0) {
3302                        NotificationRecord r = mNotificationList.get(index);
3303
3304                        // Ideally we'd do this in the caller of this method. However, that would
3305                        // require the caller to also find the notification.
3306                        if (reason == REASON_DELEGATE_CLICK) {
3307                            mUsageStats.registerClickedByUser(r);
3308                        }
3309
3310                        if ((r.getNotification().flags & mustHaveFlags) != mustHaveFlags) {
3311                            return;
3312                        }
3313                        if ((r.getNotification().flags & mustNotHaveFlags) != 0) {
3314                            return;
3315                        }
3316
3317                        mNotificationList.remove(index);
3318
3319                        cancelNotificationLocked(r, sendDelete, reason);
3320                        cancelGroupChildrenLocked(r, callingUid, callingPid, listenerName,
3321                                REASON_GROUP_SUMMARY_CANCELED);
3322                        updateLightsLocked();
3323                    }
3324                }
3325            }
3326        });
3327    }
3328
3329    /**
3330     * Determine whether the userId applies to the notification in question, either because
3331     * they match exactly, or one of them is USER_ALL (which is treated as a wildcard).
3332     */
3333    private boolean notificationMatchesUserId(NotificationRecord r, int userId) {
3334        return
3335                // looking for USER_ALL notifications? match everything
3336                   userId == UserHandle.USER_ALL
3337                // a notification sent to USER_ALL matches any query
3338                || r.getUserId() == UserHandle.USER_ALL
3339                // an exact user match
3340                || r.getUserId() == userId;
3341    }
3342
3343    /**
3344     * Determine whether the userId applies to the notification in question, either because
3345     * they match exactly, or one of them is USER_ALL (which is treated as a wildcard) or
3346     * because it matches one of the users profiles.
3347     */
3348    private boolean notificationMatchesCurrentProfiles(NotificationRecord r, int userId) {
3349        return notificationMatchesUserId(r, userId)
3350                || mUserProfiles.isCurrentProfile(r.getUserId());
3351    }
3352
3353    /**
3354     * Cancels all notifications from a given package that have all of the
3355     * {@code mustHaveFlags}.
3356     */
3357    boolean cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, int mustHaveFlags,
3358            int mustNotHaveFlags, boolean doit, int userId, int reason,
3359            ManagedServiceInfo listener) {
3360        String listenerName = listener == null ? null : listener.component.toShortString();
3361        EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
3362                pkg, userId, mustHaveFlags, mustNotHaveFlags, reason,
3363                listenerName);
3364
3365        synchronized (mNotificationList) {
3366            final int N = mNotificationList.size();
3367            ArrayList<NotificationRecord> canceledNotifications = null;
3368            for (int i = N-1; i >= 0; --i) {
3369                NotificationRecord r = mNotificationList.get(i);
3370                if (!notificationMatchesUserId(r, userId)) {
3371                    continue;
3372                }
3373                // Don't remove notifications to all, if there's no package name specified
3374                if (r.getUserId() == UserHandle.USER_ALL && pkg == null) {
3375                    continue;
3376                }
3377                if ((r.getFlags() & mustHaveFlags) != mustHaveFlags) {
3378                    continue;
3379                }
3380                if ((r.getFlags() & mustNotHaveFlags) != 0) {
3381                    continue;
3382                }
3383                if (pkg != null && !r.sbn.getPackageName().equals(pkg)) {
3384                    continue;
3385                }
3386                if (canceledNotifications == null) {
3387                    canceledNotifications = new ArrayList<>();
3388                }
3389                canceledNotifications.add(r);
3390                if (!doit) {
3391                    return true;
3392                }
3393                mNotificationList.remove(i);
3394                cancelNotificationLocked(r, false, reason);
3395            }
3396            if (doit && canceledNotifications != null) {
3397                final int M = canceledNotifications.size();
3398                for (int i = 0; i < M; i++) {
3399                    cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid,
3400                            listenerName, REASON_GROUP_SUMMARY_CANCELED);
3401                }
3402            }
3403            if (canceledNotifications != null) {
3404                updateLightsLocked();
3405            }
3406            return canceledNotifications != null;
3407        }
3408    }
3409
3410    void cancelAllLocked(int callingUid, int callingPid, int userId, int reason,
3411            ManagedServiceInfo listener, boolean includeCurrentProfiles) {
3412        String listenerName = listener == null ? null : listener.component.toShortString();
3413        EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
3414                null, userId, 0, 0, reason, listenerName);
3415
3416        ArrayList<NotificationRecord> canceledNotifications = null;
3417        final int N = mNotificationList.size();
3418        for (int i=N-1; i>=0; i--) {
3419            NotificationRecord r = mNotificationList.get(i);
3420            if (includeCurrentProfiles) {
3421                if (!notificationMatchesCurrentProfiles(r, userId)) {
3422                    continue;
3423                }
3424            } else {
3425                if (!notificationMatchesUserId(r, userId)) {
3426                    continue;
3427                }
3428            }
3429
3430            if ((r.getFlags() & (Notification.FLAG_ONGOING_EVENT
3431                            | Notification.FLAG_NO_CLEAR)) == 0) {
3432                mNotificationList.remove(i);
3433                cancelNotificationLocked(r, true, reason);
3434                // Make a note so we can cancel children later.
3435                if (canceledNotifications == null) {
3436                    canceledNotifications = new ArrayList<>();
3437                }
3438                canceledNotifications.add(r);
3439            }
3440        }
3441        int M = canceledNotifications != null ? canceledNotifications.size() : 0;
3442        for (int i = 0; i < M; i++) {
3443            cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid,
3444                    listenerName, REASON_GROUP_SUMMARY_CANCELED);
3445        }
3446        updateLightsLocked();
3447    }
3448
3449    // Warning: The caller is responsible for invoking updateLightsLocked().
3450    private void cancelGroupChildrenLocked(NotificationRecord r, int callingUid, int callingPid,
3451            String listenerName, int reason) {
3452        Notification n = r.getNotification();
3453        if (!n.isGroupSummary()) {
3454            return;
3455        }
3456
3457        String pkg = r.sbn.getPackageName();
3458        int userId = r.getUserId();
3459
3460        if (pkg == null) {
3461            if (DBG) Log.e(TAG, "No package for group summary: " + r.getKey());
3462            return;
3463        }
3464
3465        final int N = mNotificationList.size();
3466        for (int i = N - 1; i >= 0; i--) {
3467            NotificationRecord childR = mNotificationList.get(i);
3468            StatusBarNotification childSbn = childR.sbn;
3469            if ((childSbn.isGroup() && !childSbn.getNotification().isGroupSummary()) &&
3470                    childR.getGroupKey().equals(r.getGroupKey())) {
3471                EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, childSbn.getId(),
3472                        childSbn.getTag(), userId, 0, 0, reason, listenerName);
3473                mNotificationList.remove(i);
3474                cancelNotificationLocked(childR, false, reason);
3475            }
3476        }
3477    }
3478
3479    // lock on mNotificationList
3480    void updateLightsLocked()
3481    {
3482        // handle notification lights
3483        NotificationRecord ledNotification = null;
3484        while (ledNotification == null && !mLights.isEmpty()) {
3485            final String owner = mLights.get(mLights.size() - 1);
3486            ledNotification = mNotificationsByKey.get(owner);
3487            if (ledNotification == null) {
3488                Slog.wtfStack(TAG, "LED Notification does not exist: " + owner);
3489                mLights.remove(owner);
3490            }
3491        }
3492
3493        // Don't flash while we are in a call or screen is on
3494        if (ledNotification == null || mInCall || mScreenOn) {
3495            mNotificationLight.turnOff();
3496            if (mStatusBar != null) {
3497                mStatusBar.notificationLightOff();
3498            }
3499        } else {
3500            final Notification ledno = ledNotification.sbn.getNotification();
3501            int ledARGB = ledno.ledARGB;
3502            int ledOnMS = ledno.ledOnMS;
3503            int ledOffMS = ledno.ledOffMS;
3504            if ((ledno.defaults & Notification.DEFAULT_LIGHTS) != 0) {
3505                ledARGB = mDefaultNotificationColor;
3506                ledOnMS = mDefaultNotificationLedOn;
3507                ledOffMS = mDefaultNotificationLedOff;
3508            }
3509            if (mNotificationPulseEnabled) {
3510                // pulse repeatedly
3511                mNotificationLight.setFlashing(ledARGB, Light.LIGHT_FLASH_TIMED,
3512                        ledOnMS, ledOffMS);
3513            }
3514            if (mStatusBar != null) {
3515                // let SystemUI make an independent decision
3516                mStatusBar.notificationLightPulse(ledARGB, ledOnMS, ledOffMS);
3517            }
3518        }
3519    }
3520
3521    // lock on mNotificationList
3522    int indexOfNotificationLocked(String pkg, String tag, int id, int userId)
3523    {
3524        ArrayList<NotificationRecord> list = mNotificationList;
3525        final int len = list.size();
3526        for (int i=0; i<len; i++) {
3527            NotificationRecord r = list.get(i);
3528            if (notificationMatchesUserId(r, userId) && r.sbn.getId() == id &&
3529                    TextUtils.equals(r.sbn.getTag(), tag) && r.sbn.getPackageName().equals(pkg)) {
3530                return i;
3531            }
3532        }
3533        return -1;
3534    }
3535
3536    // lock on mNotificationList
3537    int indexOfNotificationLocked(String key) {
3538        final int N = mNotificationList.size();
3539        for (int i = 0; i < N; i++) {
3540            if (key.equals(mNotificationList.get(i).getKey())) {
3541                return i;
3542            }
3543        }
3544        return -1;
3545    }
3546
3547    private void updateNotificationPulse() {
3548        synchronized (mNotificationList) {
3549            updateLightsLocked();
3550        }
3551    }
3552
3553    private static boolean isUidSystem(int uid) {
3554        final int appid = UserHandle.getAppId(uid);
3555        return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0);
3556    }
3557
3558    private static boolean isCallerSystem() {
3559        return isUidSystem(Binder.getCallingUid());
3560    }
3561
3562    private static void checkCallerIsSystem() {
3563        if (isCallerSystem()) {
3564            return;
3565        }
3566        throw new SecurityException("Disallowed call for uid " + Binder.getCallingUid());
3567    }
3568
3569    private static void checkCallerIsSystemOrSameApp(String pkg) {
3570        if (isCallerSystem()) {
3571            return;
3572        }
3573        final int uid = Binder.getCallingUid();
3574        try {
3575            ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo(
3576                    pkg, 0, UserHandle.getCallingUserId());
3577            if (ai == null) {
3578                throw new SecurityException("Unknown package " + pkg);
3579            }
3580            if (!UserHandle.isSameApp(ai.uid, uid)) {
3581                throw new SecurityException("Calling uid " + uid + " gave package"
3582                        + pkg + " which is owned by uid " + ai.uid);
3583            }
3584        } catch (RemoteException re) {
3585            throw new SecurityException("Unknown package " + pkg + "\n" + re);
3586        }
3587    }
3588
3589    private static String callStateToString(int state) {
3590        switch (state) {
3591            case TelephonyManager.CALL_STATE_IDLE: return "CALL_STATE_IDLE";
3592            case TelephonyManager.CALL_STATE_RINGING: return "CALL_STATE_RINGING";
3593            case TelephonyManager.CALL_STATE_OFFHOOK: return "CALL_STATE_OFFHOOK";
3594            default: return "CALL_STATE_UNKNOWN_" + state;
3595        }
3596    }
3597
3598    private void listenForCallState() {
3599        TelephonyManager.from(getContext()).listen(new PhoneStateListener() {
3600            @Override
3601            public void onCallStateChanged(int state, String incomingNumber) {
3602                if (mCallState == state) return;
3603                if (DBG) Slog.d(TAG, "Call state changed: " + callStateToString(state));
3604                mCallState = state;
3605            }
3606        }, PhoneStateListener.LISTEN_CALL_STATE);
3607    }
3608
3609    /**
3610     * Generates a NotificationRankingUpdate from 'sbns', considering only
3611     * notifications visible to the given listener.
3612     *
3613     * <p>Caller must hold a lock on mNotificationList.</p>
3614     */
3615    private NotificationRankingUpdate makeRankingUpdateLocked(ManagedServiceInfo info) {
3616        final int N = mNotificationList.size();
3617        ArrayList<String> keys = new ArrayList<String>(N);
3618        ArrayList<String> interceptedKeys = new ArrayList<String>(N);
3619        ArrayList<Integer> importance = new ArrayList<>(N);
3620        Bundle overrideGroupKeys = new Bundle();
3621        Bundle visibilityOverrides = new Bundle();
3622        Bundle suppressedVisualEffects = new Bundle();
3623        Bundle explanation = new Bundle();
3624        for (int i = 0; i < N; i++) {
3625            NotificationRecord record = mNotificationList.get(i);
3626            if (!isVisibleToListener(record.sbn, info)) {
3627                continue;
3628            }
3629            final String key = record.sbn.getKey();
3630            keys.add(key);
3631            importance.add(record.getImportance());
3632            if (record.getImportanceExplanation() != null) {
3633                explanation.putCharSequence(key, record.getImportanceExplanation());
3634            }
3635            if (record.isIntercepted()) {
3636                interceptedKeys.add(key);
3637
3638            }
3639            suppressedVisualEffects.putInt(key, record.getSuppressedVisualEffects());
3640            if (record.getPackageVisibilityOverride()
3641                    != NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE) {
3642                visibilityOverrides.putInt(key, record.getPackageVisibilityOverride());
3643            }
3644            overrideGroupKeys.putString(key, record.sbn.getOverrideGroupKey());
3645        }
3646        final int M = keys.size();
3647        String[] keysAr = keys.toArray(new String[M]);
3648        String[] interceptedKeysAr = interceptedKeys.toArray(new String[interceptedKeys.size()]);
3649        int[] importanceAr = new int[M];
3650        for (int i = 0; i < M; i++) {
3651            importanceAr[i] = importance.get(i);
3652        }
3653        return new NotificationRankingUpdate(keysAr, interceptedKeysAr, visibilityOverrides,
3654                suppressedVisualEffects, importanceAr, explanation, overrideGroupKeys);
3655    }
3656
3657    private boolean isVisibleToListener(StatusBarNotification sbn, ManagedServiceInfo listener) {
3658        if (!listener.enabledAndUserMatches(sbn.getUserId())) {
3659            return false;
3660        }
3661        // TODO: remove this for older listeners.
3662        return true;
3663    }
3664
3665    private boolean isPackageSuspendedForUser(String pkg, int uid) {
3666        int userId = UserHandle.getUserId(uid);
3667        try {
3668            return AppGlobals.getPackageManager().isPackageSuspendedForUser(pkg, userId);
3669        } catch (RemoteException re) {
3670            throw new SecurityException("Could not talk to package manager service");
3671        } catch (IllegalArgumentException ex) {
3672            // Package not found.
3673            return false;
3674        }
3675    }
3676
3677    private class TrimCache {
3678        StatusBarNotification heavy;
3679        StatusBarNotification sbnClone;
3680        StatusBarNotification sbnCloneLight;
3681
3682        TrimCache(StatusBarNotification sbn) {
3683            heavy = sbn;
3684        }
3685
3686        StatusBarNotification ForListener(ManagedServiceInfo info) {
3687            if (mListeners.getOnNotificationPostedTrim(info) == TRIM_LIGHT) {
3688                if (sbnCloneLight == null) {
3689                    sbnCloneLight = heavy.cloneLight();
3690                }
3691                return sbnCloneLight;
3692            } else {
3693                if (sbnClone == null) {
3694                    sbnClone = heavy.clone();
3695                }
3696                return sbnClone;
3697            }
3698        }
3699    }
3700
3701    public class NotificationRankers extends ManagedServices {
3702
3703        public NotificationRankers() {
3704            super(getContext(), mHandler, mNotificationList, mUserProfiles);
3705        }
3706
3707        @Override
3708        protected Config getConfig() {
3709            Config c = new Config();
3710            c.caption = "notification ranker service";
3711            c.serviceInterface = NotificationRankerService.SERVICE_INTERFACE;
3712            c.secureSettingName = null;
3713            c.bindPermission = Manifest.permission.BIND_NOTIFICATION_RANKER_SERVICE;
3714            c.settingsAction = Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS;
3715            c.clientLabel = R.string.notification_ranker_binding_label;
3716            return c;
3717        }
3718
3719        @Override
3720        protected IInterface asInterface(IBinder binder) {
3721            return INotificationListener.Stub.asInterface(binder);
3722        }
3723
3724        @Override
3725        protected boolean checkType(IInterface service) {
3726            return service instanceof INotificationListener;
3727        }
3728
3729        @Override
3730        protected void onServiceAdded(ManagedServiceInfo info) {
3731            mListeners.registerGuestService(info);
3732        }
3733
3734        @Override
3735        protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
3736            mListeners.unregisterService(removed.service, removed.userid);
3737        }
3738
3739        public void onNotificationEnqueued(final NotificationRecord r) {
3740            final StatusBarNotification sbn = r.sbn;
3741            TrimCache trimCache = new TrimCache(sbn);
3742
3743            // mServices is the list inside ManagedServices of all the rankers,
3744            // There should be only one, but it's a list, so while we enforce
3745            // singularity elsewhere, we keep it general here, to avoid surprises.
3746            for (final ManagedServiceInfo info : NotificationRankers.this.mServices) {
3747                boolean sbnVisible = isVisibleToListener(sbn, info);
3748                if (!sbnVisible) {
3749                    continue;
3750                }
3751
3752                final int importance = r.getImportance();
3753                final boolean fromUser = r.isImportanceFromUser();
3754                final StatusBarNotification sbnToPost =  trimCache.ForListener(info);
3755                mHandler.post(new Runnable() {
3756                    @Override
3757                    public void run() {
3758                        notifyEnqueued(info, sbnToPost, importance, fromUser);
3759                    }
3760                });
3761            }
3762        }
3763
3764        private void notifyEnqueued(final ManagedServiceInfo info,
3765                final StatusBarNotification sbn, int importance, boolean fromUser) {
3766            final INotificationListener ranker = (INotificationListener) info.service;
3767            StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
3768            try {
3769                ranker.onNotificationEnqueued(sbnHolder, importance, fromUser);
3770            } catch (RemoteException ex) {
3771                Log.e(TAG, "unable to notify ranker (enqueued): " + ranker, ex);
3772            }
3773        }
3774
3775        public boolean isEnabled() {
3776            return !mServices.isEmpty();
3777        }
3778
3779        @Override
3780        public void onUserSwitched(int user) {
3781            for (ManagedServiceInfo info : mServices) {
3782                unregisterService(info.service, info.userid);
3783            }
3784            registerRanker();
3785        }
3786
3787        @Override
3788        public void onPackagesChanged(boolean queryReplace, String[] pkgList) {
3789            if (DEBUG) Slog.d(TAG, "onPackagesChanged queryReplace=" + queryReplace
3790                    + " pkgList=" + (pkgList == null ? null : Arrays.asList(pkgList)));
3791            if (mRankerServicePackageName == null) {
3792                return;
3793            }
3794
3795            if (pkgList != null && (pkgList.length > 0)) {
3796                for (String pkgName : pkgList) {
3797                    if (mRankerServicePackageName.equals(pkgName)) {
3798                        registerRanker();
3799                    }
3800                }
3801            }
3802        }
3803
3804        protected void registerRanker() {
3805            // Find the updatable ranker and register it.
3806            if (mRankerServicePackageName == null) {
3807                Slog.w(TAG, "could not start ranker service: no package specified!");
3808                return;
3809            }
3810            Set<ComponentName> rankerComponents = queryPackageForServices(
3811                    mRankerServicePackageName, UserHandle.USER_SYSTEM);
3812            Iterator<ComponentName> iterator = rankerComponents.iterator();
3813            if (iterator.hasNext()) {
3814                ComponentName rankerComponent = iterator.next();
3815                if (iterator.hasNext()) {
3816                    Slog.e(TAG, "found multiple ranker services:" + rankerComponents);
3817                } else {
3818                    registerSystemService(rankerComponent, UserHandle.USER_SYSTEM);
3819                }
3820            } else {
3821                Slog.w(TAG, "could not start ranker service: none found");
3822            }
3823        }
3824    }
3825
3826    public class NotificationListeners extends ManagedServices {
3827
3828        private final ArraySet<ManagedServiceInfo> mLightTrimListeners = new ArraySet<>();
3829
3830        public NotificationListeners() {
3831            super(getContext(), mHandler, mNotificationList, mUserProfiles);
3832        }
3833
3834        @Override
3835        protected Config getConfig() {
3836            Config c = new Config();
3837            c.caption = "notification listener";
3838            c.serviceInterface = NotificationListenerService.SERVICE_INTERFACE;
3839            c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_LISTENERS;
3840            c.bindPermission = android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE;
3841            c.settingsAction = Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS;
3842            c.clientLabel = R.string.notification_listener_binding_label;
3843            return c;
3844        }
3845
3846        @Override
3847        protected IInterface asInterface(IBinder binder) {
3848            return INotificationListener.Stub.asInterface(binder);
3849        }
3850
3851        @Override
3852        protected boolean checkType(IInterface service) {
3853            return service instanceof INotificationListener;
3854        }
3855
3856        @Override
3857        public void onServiceAdded(ManagedServiceInfo info) {
3858            final INotificationListener listener = (INotificationListener) info.service;
3859            final NotificationRankingUpdate update;
3860            synchronized (mNotificationList) {
3861                update = makeRankingUpdateLocked(info);
3862            }
3863            try {
3864                listener.onListenerConnected(update);
3865            } catch (RemoteException e) {
3866                // we tried
3867            }
3868        }
3869
3870        @Override
3871        protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
3872            if (removeDisabledHints(removed)) {
3873                updateListenerHintsLocked();
3874                updateEffectsSuppressorLocked();
3875            }
3876            mLightTrimListeners.remove(removed);
3877        }
3878
3879        public void setOnNotificationPostedTrimLocked(ManagedServiceInfo info, int trim) {
3880            if (trim == TRIM_LIGHT) {
3881                mLightTrimListeners.add(info);
3882            } else {
3883                mLightTrimListeners.remove(info);
3884            }
3885        }
3886
3887        public int getOnNotificationPostedTrim(ManagedServiceInfo info) {
3888            return mLightTrimListeners.contains(info) ? TRIM_LIGHT : TRIM_FULL;
3889        }
3890
3891        /**
3892         * asynchronously notify all listeners about a new notification
3893         *
3894         * <p>
3895         * Also takes care of removing a notification that has been visible to a listener before,
3896         * but isn't anymore.
3897         */
3898        public void notifyPostedLocked(StatusBarNotification sbn, StatusBarNotification oldSbn) {
3899            // Lazily initialized snapshots of the notification.
3900            TrimCache trimCache = new TrimCache(sbn);
3901
3902            for (final ManagedServiceInfo info : mServices) {
3903                boolean sbnVisible = isVisibleToListener(sbn, info);
3904                boolean oldSbnVisible = oldSbn != null ? isVisibleToListener(oldSbn, info) : false;
3905                // This notification hasn't been and still isn't visible -> ignore.
3906                if (!oldSbnVisible && !sbnVisible) {
3907                    continue;
3908                }
3909                final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
3910
3911                // This notification became invisible -> remove the old one.
3912                if (oldSbnVisible && !sbnVisible) {
3913                    final StatusBarNotification oldSbnLightClone = oldSbn.cloneLight();
3914                    mHandler.post(new Runnable() {
3915                        @Override
3916                        public void run() {
3917                            notifyRemoved(info, oldSbnLightClone, update);
3918                        }
3919                    });
3920                    continue;
3921                }
3922
3923                final StatusBarNotification sbnToPost =  trimCache.ForListener(info);
3924                mHandler.post(new Runnable() {
3925                    @Override
3926                    public void run() {
3927                        notifyPosted(info, sbnToPost, update);
3928                    }
3929                });
3930            }
3931        }
3932
3933        /**
3934         * asynchronously notify all listeners about a removed notification
3935         */
3936        public void notifyRemovedLocked(StatusBarNotification sbn) {
3937            // make a copy in case changes are made to the underlying Notification object
3938            // NOTE: this copy is lightweight: it doesn't include heavyweight parts of the
3939            // notification
3940            final StatusBarNotification sbnLight = sbn.cloneLight();
3941            for (final ManagedServiceInfo info : mServices) {
3942                if (!isVisibleToListener(sbn, info)) {
3943                    continue;
3944                }
3945                final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
3946                mHandler.post(new Runnable() {
3947                    @Override
3948                    public void run() {
3949                        notifyRemoved(info, sbnLight, update);
3950                    }
3951                });
3952            }
3953        }
3954
3955        /**
3956         * asynchronously notify all listeners about a reordering of notifications
3957         */
3958        public void notifyRankingUpdateLocked() {
3959            for (final ManagedServiceInfo serviceInfo : mServices) {
3960                if (!serviceInfo.isEnabledForCurrentProfiles()) {
3961                    continue;
3962                }
3963                final NotificationRankingUpdate update = makeRankingUpdateLocked(serviceInfo);
3964                mHandler.post(new Runnable() {
3965                    @Override
3966                    public void run() {
3967                        notifyRankingUpdate(serviceInfo, update);
3968                    }
3969                });
3970            }
3971        }
3972
3973        public void notifyListenerHintsChangedLocked(final int hints) {
3974            for (final ManagedServiceInfo serviceInfo : mServices) {
3975                if (!serviceInfo.isEnabledForCurrentProfiles()) {
3976                    continue;
3977                }
3978                mHandler.post(new Runnable() {
3979                    @Override
3980                    public void run() {
3981                        notifyListenerHintsChanged(serviceInfo, hints);
3982                    }
3983                });
3984            }
3985        }
3986
3987        public void notifyInterruptionFilterChanged(final int interruptionFilter) {
3988            for (final ManagedServiceInfo serviceInfo : mServices) {
3989                if (!serviceInfo.isEnabledForCurrentProfiles()) {
3990                    continue;
3991                }
3992                mHandler.post(new Runnable() {
3993                    @Override
3994                    public void run() {
3995                        notifyInterruptionFilterChanged(serviceInfo, interruptionFilter);
3996                    }
3997                });
3998            }
3999        }
4000
4001        private void notifyPosted(final ManagedServiceInfo info,
4002                final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) {
4003            final INotificationListener listener = (INotificationListener)info.service;
4004            StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
4005            try {
4006                listener.onNotificationPosted(sbnHolder, rankingUpdate);
4007            } catch (RemoteException ex) {
4008                Log.e(TAG, "unable to notify listener (posted): " + listener, ex);
4009            }
4010        }
4011
4012        private void notifyRemoved(ManagedServiceInfo info, StatusBarNotification sbn,
4013                NotificationRankingUpdate rankingUpdate) {
4014            if (!info.enabledAndUserMatches(sbn.getUserId())) {
4015                return;
4016            }
4017            final INotificationListener listener = (INotificationListener) info.service;
4018            StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
4019            try {
4020                listener.onNotificationRemoved(sbnHolder, rankingUpdate);
4021            } catch (RemoteException ex) {
4022                Log.e(TAG, "unable to notify listener (removed): " + listener, ex);
4023            }
4024        }
4025
4026        private void notifyRankingUpdate(ManagedServiceInfo info,
4027                                         NotificationRankingUpdate rankingUpdate) {
4028            final INotificationListener listener = (INotificationListener) info.service;
4029            try {
4030                listener.onNotificationRankingUpdate(rankingUpdate);
4031            } catch (RemoteException ex) {
4032                Log.e(TAG, "unable to notify listener (ranking update): " + listener, ex);
4033            }
4034        }
4035
4036        private void notifyListenerHintsChanged(ManagedServiceInfo info, int hints) {
4037            final INotificationListener listener = (INotificationListener) info.service;
4038            try {
4039                listener.onListenerHintsChanged(hints);
4040            } catch (RemoteException ex) {
4041                Log.e(TAG, "unable to notify listener (listener hints): " + listener, ex);
4042            }
4043        }
4044
4045        private void notifyInterruptionFilterChanged(ManagedServiceInfo info,
4046                int interruptionFilter) {
4047            final INotificationListener listener = (INotificationListener) info.service;
4048            try {
4049                listener.onInterruptionFilterChanged(interruptionFilter);
4050            } catch (RemoteException ex) {
4051                Log.e(TAG, "unable to notify listener (interruption filter): " + listener, ex);
4052            }
4053        }
4054
4055        private boolean isListenerPackage(String packageName) {
4056            if (packageName == null) {
4057                return false;
4058            }
4059            // TODO: clean up locking object later
4060            synchronized (mNotificationList) {
4061                for (final ManagedServiceInfo serviceInfo : mServices) {
4062                    if (packageName.equals(serviceInfo.component.getPackageName())) {
4063                        return true;
4064                    }
4065                }
4066            }
4067            return false;
4068        }
4069    }
4070
4071    public static final class DumpFilter {
4072        public boolean filtered = false;
4073        public String pkgFilter;
4074        public boolean zen;
4075        public long since;
4076        public boolean stats;
4077        public boolean redact = true;
4078
4079        public static DumpFilter parseFromArguments(String[] args) {
4080            final DumpFilter filter = new DumpFilter();
4081            for (int ai = 0; ai < args.length; ai++) {
4082                final String a = args[ai];
4083                if ("--noredact".equals(a) || "--reveal".equals(a)) {
4084                    filter.redact = false;
4085                } else if ("p".equals(a) || "pkg".equals(a) || "--package".equals(a)) {
4086                    if (ai < args.length-1) {
4087                        ai++;
4088                        filter.pkgFilter = args[ai].trim().toLowerCase();
4089                        if (filter.pkgFilter.isEmpty()) {
4090                            filter.pkgFilter = null;
4091                        } else {
4092                            filter.filtered = true;
4093                        }
4094                    }
4095                } else if ("--zen".equals(a) || "zen".equals(a)) {
4096                    filter.filtered = true;
4097                    filter.zen = true;
4098                } else if ("--stats".equals(a)) {
4099                    filter.stats = true;
4100                    if (ai < args.length-1) {
4101                        ai++;
4102                        filter.since = Long.valueOf(args[ai]);
4103                    } else {
4104                        filter.since = 0;
4105                    }
4106                }
4107            }
4108            return filter;
4109        }
4110
4111        public boolean matches(StatusBarNotification sbn) {
4112            if (!filtered) return true;
4113            return zen ? true : sbn != null
4114                    && (matches(sbn.getPackageName()) || matches(sbn.getOpPkg()));
4115        }
4116
4117        public boolean matches(ComponentName component) {
4118            if (!filtered) return true;
4119            return zen ? true : component != null && matches(component.getPackageName());
4120        }
4121
4122        public boolean matches(String pkg) {
4123            if (!filtered) return true;
4124            return zen ? true : pkg != null && pkg.toLowerCase().contains(pkgFilter);
4125        }
4126
4127        @Override
4128        public String toString() {
4129            return stats ? "stats" : zen ? "zen" : ('\'' + pkgFilter + '\'');
4130        }
4131    }
4132
4133    /**
4134     * Wrapper for a StatusBarNotification object that allows transfer across a oneway
4135     * binder without sending large amounts of data over a oneway transaction.
4136     */
4137    private static final class StatusBarNotificationHolder
4138            extends IStatusBarNotificationHolder.Stub {
4139        private StatusBarNotification mValue;
4140
4141        public StatusBarNotificationHolder(StatusBarNotification value) {
4142            mValue = value;
4143        }
4144
4145        /** Get the held value and clear it. This function should only be called once per holder */
4146        @Override
4147        public StatusBarNotification get() {
4148            StatusBarNotification value = mValue;
4149            mValue = null;
4150            return value;
4151        }
4152    }
4153
4154    private final class PolicyAccess {
4155        private static final String SEPARATOR = ":";
4156        private final String[] PERM = {
4157            android.Manifest.permission.ACCESS_NOTIFICATION_POLICY
4158        };
4159
4160        public boolean isPackageGranted(String pkg) {
4161            return pkg != null && getGrantedPackages().contains(pkg);
4162        }
4163
4164        public void put(String pkg, boolean granted) {
4165            if (pkg == null) return;
4166            final ArraySet<String> pkgs = getGrantedPackages();
4167            boolean changed;
4168            if (granted) {
4169                changed = pkgs.add(pkg);
4170            } else {
4171                changed = pkgs.remove(pkg);
4172            }
4173            if (!changed) return;
4174            final String setting = TextUtils.join(SEPARATOR, pkgs);
4175            final int currentUser = ActivityManager.getCurrentUser();
4176            Settings.Secure.putStringForUser(getContext().getContentResolver(),
4177                    Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES,
4178                    setting,
4179                    currentUser);
4180            getContext().sendBroadcastAsUser(new Intent(NotificationManager
4181                    .ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED)
4182                .setPackage(pkg)
4183                .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), new UserHandle(currentUser), null);
4184        }
4185
4186        public ArraySet<String> getGrantedPackages() {
4187            final ArraySet<String> pkgs = new ArraySet<>();
4188
4189            long identity = Binder.clearCallingIdentity();
4190            try {
4191                final String setting = Settings.Secure.getStringForUser(
4192                        getContext().getContentResolver(),
4193                        Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES,
4194                        ActivityManager.getCurrentUser());
4195                if (setting != null) {
4196                    final String[] tokens = setting.split(SEPARATOR);
4197                    for (int i = 0; i < tokens.length; i++) {
4198                        String token = tokens[i];
4199                        if (token != null) {
4200                            token = token.trim();
4201                        }
4202                        if (TextUtils.isEmpty(token)) {
4203                            continue;
4204                        }
4205                        pkgs.add(token);
4206                    }
4207                }
4208            } finally {
4209                Binder.restoreCallingIdentity(identity);
4210            }
4211            return pkgs;
4212        }
4213
4214        public String[] getRequestingPackages() throws RemoteException {
4215            final ParceledListSlice list = AppGlobals.getPackageManager()
4216                    .getPackagesHoldingPermissions(PERM, 0 /*flags*/,
4217                            ActivityManager.getCurrentUser());
4218            final List<PackageInfo> pkgs = list.getList();
4219            if (pkgs == null || pkgs.isEmpty()) return new String[0];
4220            final int N = pkgs.size();
4221            final String[] rt = new String[N];
4222            for (int i = 0; i < N; i++) {
4223                rt[i] = pkgs.get(i).packageName;
4224            }
4225            return rt;
4226        }
4227    }
4228}
4229