NotificationManagerService.java revision 012bc7bb9fa1d0731be7b094a3d0631e01b03fbe
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.get(0);
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            n.sbn.setOverrideGroupKey(adjustment.getSignals().getString(
2183                    Adjustment.GROUP_KEY_OVERRIDE_KEY, null));
2184        }
2185    }
2186
2187    // Clears the 'fake' auto-bunding summary.
2188    private void maybeClearAutobundleSummaryLocked(Adjustment adjustment) {
2189        if (adjustment.getSignals() != null
2190                && adjustment.getSignals().containsKey(Adjustment.NEEDS_AUTOGROUPING_KEY)
2191                && !adjustment.getSignals().getBoolean(Adjustment.NEEDS_AUTOGROUPING_KEY, false)) {
2192            if (mAutobundledSummaries.containsKey(adjustment.getPackage())) {
2193                // Clear summary.
2194                final NotificationRecord removed = mNotificationsByKey.get(
2195                        mAutobundledSummaries.remove(adjustment.getPackage()));
2196                if (removed != null) {
2197                    mNotificationList.remove(removed);
2198                    cancelNotificationLocked(removed, false, REASON_UNAUTOBUNDLED);
2199                }
2200            }
2201        }
2202    }
2203
2204    // Posts a 'fake' summary for a package that has exceeded the solo-notification limit.
2205    private void maybeAddAutobundleSummary(Adjustment adjustment) {
2206        if (adjustment.getSignals() != null
2207                && adjustment.getSignals().getBoolean(Adjustment.NEEDS_AUTOGROUPING_KEY, false)) {
2208            final String newAutoBundleKey =
2209                    adjustment.getSignals().getString(Adjustment.GROUP_KEY_OVERRIDE_KEY, null);
2210            int userId = -1;
2211            NotificationRecord summaryRecord = null;
2212            synchronized (mNotificationList) {
2213                if (!mAutobundledSummaries.containsKey(adjustment.getPackage())
2214                        && newAutoBundleKey != null) {
2215                    // Add summary
2216                    final StatusBarNotification adjustedSbn
2217                            = mNotificationsByKey.get(adjustment.getKey()).sbn;
2218
2219                    final ApplicationInfo appInfo =
2220                            adjustedSbn.getNotification().extras.getParcelable(
2221                                    Notification.EXTRA_BUILDER_APPLICATION_INFO);
2222                    final Bundle extras = new Bundle();
2223                    extras.putParcelable(Notification.EXTRA_BUILDER_APPLICATION_INFO, appInfo);
2224                    final Notification summaryNotification =
2225                            new Notification.Builder(getContext()).setSmallIcon(
2226                                    adjustedSbn.getNotification().getSmallIcon())
2227                                    .setGroupSummary(true)
2228                                    .setGroup(newAutoBundleKey)
2229                                    .setFlag(Notification.FLAG_AUTOGROUP_SUMMARY, true)
2230                                    .setFlag(Notification.FLAG_GROUP_SUMMARY, true)
2231                                    .build();
2232                    summaryNotification.extras.putAll(extras);
2233                    final StatusBarNotification summarySbn =
2234                            new StatusBarNotification(adjustedSbn.getPackageName(),
2235                                    adjustedSbn.getOpPkg(),
2236                                    Integer.MAX_VALUE, Adjustment.GROUP_KEY_OVERRIDE_KEY,
2237                                    adjustedSbn.getUid(), adjustedSbn.getInitialPid(),
2238                                    summaryNotification, adjustedSbn.getUser(), newAutoBundleKey,
2239                                    System.currentTimeMillis());
2240                    summaryRecord = new NotificationRecord(getContext(), summarySbn);
2241                    mAutobundledSummaries.put(adjustment.getPackage(), summarySbn.getKey());
2242                    userId = adjustedSbn.getUser().getIdentifier();
2243                }
2244            }
2245            if (summaryRecord != null) {
2246                mHandler.post(new EnqueueNotificationRunnable(userId, summaryRecord));
2247            }
2248        }
2249    }
2250
2251    private String disableNotificationEffects(NotificationRecord record) {
2252        if (mDisableNotificationEffects) {
2253            return "booleanState";
2254        }
2255        if ((mListenerHints & HINT_HOST_DISABLE_EFFECTS) != 0) {
2256            return "listenerHints";
2257        }
2258        if (mCallState != TelephonyManager.CALL_STATE_IDLE && !mZenModeHelper.isCall(record)) {
2259            return "callState";
2260        }
2261        return null;
2262    };
2263
2264    private void dumpJson(PrintWriter pw, DumpFilter filter) {
2265        JSONObject dump = new JSONObject();
2266        try {
2267            dump.put("service", "Notification Manager");
2268            dump.put("bans", mRankingHelper.dumpBansJson(filter));
2269            dump.put("ranking", mRankingHelper.dumpJson(filter));
2270            dump.put("stats", mUsageStats.dumpJson(filter));
2271        } catch (JSONException e) {
2272            e.printStackTrace();
2273        }
2274        pw.println(dump);
2275    }
2276
2277    void dumpImpl(PrintWriter pw, DumpFilter filter) {
2278        pw.print("Current Notification Manager state");
2279        if (filter.filtered) {
2280            pw.print(" (filtered to "); pw.print(filter); pw.print(")");
2281        }
2282        pw.println(':');
2283        int N;
2284        final boolean zenOnly = filter.filtered && filter.zen;
2285
2286        if (!zenOnly) {
2287            synchronized (mToastQueue) {
2288                N = mToastQueue.size();
2289                if (N > 0) {
2290                    pw.println("  Toast Queue:");
2291                    for (int i=0; i<N; i++) {
2292                        mToastQueue.get(i).dump(pw, "    ", filter);
2293                    }
2294                    pw.println("  ");
2295                }
2296            }
2297        }
2298
2299        synchronized (mNotificationList) {
2300            if (!zenOnly) {
2301                N = mNotificationList.size();
2302                if (N > 0) {
2303                    pw.println("  Notification List:");
2304                    for (int i=0; i<N; i++) {
2305                        final NotificationRecord nr = mNotificationList.get(i);
2306                        if (filter.filtered && !filter.matches(nr.sbn)) continue;
2307                        nr.dump(pw, "    ", getContext(), filter.redact);
2308                    }
2309                    pw.println("  ");
2310                }
2311
2312                if (!filter.filtered) {
2313                    N = mLights.size();
2314                    if (N > 0) {
2315                        pw.println("  Lights List:");
2316                        for (int i=0; i<N; i++) {
2317                            if (i == N - 1) {
2318                                pw.print("  > ");
2319                            } else {
2320                                pw.print("    ");
2321                            }
2322                            pw.println(mLights.get(i));
2323                        }
2324                        pw.println("  ");
2325                    }
2326                    pw.println("  mUseAttentionLight=" + mUseAttentionLight);
2327                    pw.println("  mNotificationPulseEnabled=" + mNotificationPulseEnabled);
2328                    pw.println("  mSoundNotificationKey=" + mSoundNotificationKey);
2329                    pw.println("  mVibrateNotificationKey=" + mVibrateNotificationKey);
2330                    pw.println("  mDisableNotificationEffects=" + mDisableNotificationEffects);
2331                    pw.println("  mCallState=" + callStateToString(mCallState));
2332                    pw.println("  mSystemReady=" + mSystemReady);
2333                }
2334                pw.println("  mArchive=" + mArchive.toString());
2335                Iterator<StatusBarNotification> iter = mArchive.descendingIterator();
2336                int i=0;
2337                while (iter.hasNext()) {
2338                    final StatusBarNotification sbn = iter.next();
2339                    if (filter != null && !filter.matches(sbn)) continue;
2340                    pw.println("    " + sbn);
2341                    if (++i >= 5) {
2342                        if (iter.hasNext()) pw.println("    ...");
2343                        break;
2344                    }
2345                }
2346            }
2347
2348            if (!zenOnly) {
2349                pw.println("\n  Usage Stats:");
2350                mUsageStats.dump(pw, "    ", filter);
2351            }
2352
2353            if (!filter.filtered || zenOnly) {
2354                pw.println("\n  Zen Mode:");
2355                pw.print("    mInterruptionFilter="); pw.println(mInterruptionFilter);
2356                mZenModeHelper.dump(pw, "    ");
2357
2358                pw.println("\n  Zen Log:");
2359                ZenLog.dump(pw, "    ");
2360            }
2361
2362            if (!zenOnly) {
2363                pw.println("\n  Ranking Config:");
2364                mRankingHelper.dump(pw, "    ", filter);
2365
2366                pw.println("\n  Notification listeners:");
2367                mListeners.dump(pw, filter);
2368                pw.print("    mListenerHints: "); pw.println(mListenerHints);
2369                pw.print("    mListenersDisablingEffects: (");
2370                N = mListenersDisablingEffects.size();
2371                for (int i = 0; i < N; i++) {
2372                    final int hint = mListenersDisablingEffects.keyAt(i);
2373                    if (i > 0) pw.print(';');
2374                    pw.print("hint[" + hint + "]:");
2375
2376                    final ArraySet<ManagedServiceInfo> listeners =
2377                            mListenersDisablingEffects.valueAt(i);
2378                    final int listenerSize = listeners.size();
2379
2380                    for (int j = 0; j < listenerSize; j++) {
2381                        if (i > 0) pw.print(',');
2382                        final ManagedServiceInfo listener = listeners.valueAt(i);
2383                        pw.print(listener.component);
2384                    }
2385                }
2386                pw.println(')');
2387                pw.println("\n  mRankerServicePackageName: " + mRankerServicePackageName);
2388                pw.println("\n  Notification ranker services:");
2389                mRankerServices.dump(pw, filter);
2390            }
2391            pw.println("\n  Policy access:");
2392            pw.print("    mPolicyAccess: "); pw.println(mPolicyAccess);
2393
2394            pw.println("\n  Condition providers:");
2395            mConditionProviders.dump(pw, filter);
2396
2397            pw.println("\n  Group summaries:");
2398            for (Entry<String, NotificationRecord> entry : mSummaryByGroupKey.entrySet()) {
2399                NotificationRecord r = entry.getValue();
2400                pw.println("    " + entry.getKey() + " -> " + r.getKey());
2401                if (mNotificationsByKey.get(r.getKey()) != r) {
2402                    pw.println("!!!!!!LEAK: Record not found in mNotificationsByKey.");
2403                    r.dump(pw, "      ", getContext(), filter.redact);
2404                }
2405            }
2406        }
2407    }
2408
2409    /**
2410     * The private API only accessible to the system process.
2411     */
2412    private final NotificationManagerInternal mInternalService = new NotificationManagerInternal() {
2413        @Override
2414        public void enqueueNotification(String pkg, String opPkg, int callingUid, int callingPid,
2415                String tag, int id, Notification notification, int[] idReceived, int userId) {
2416            enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, notification,
2417                    idReceived, userId);
2418        }
2419
2420        @Override
2421        public void removeForegroundServiceFlagFromNotification(String pkg, int notificationId,
2422                int userId) {
2423            checkCallerIsSystem();
2424            synchronized (mNotificationList) {
2425                int i = indexOfNotificationLocked(pkg, null, notificationId, userId);
2426                if (i < 0) {
2427                    Log.d(TAG, "stripForegroundServiceFlag: Could not find notification with "
2428                            + "pkg=" + pkg + " / id=" + notificationId + " / userId=" + userId);
2429                    return;
2430                }
2431                NotificationRecord r = mNotificationList.get(i);
2432                StatusBarNotification sbn = r.sbn;
2433                // NoMan adds flags FLAG_NO_CLEAR and FLAG_ONGOING_EVENT when it sees
2434                // FLAG_FOREGROUND_SERVICE. Hence it's not enough to remove FLAG_FOREGROUND_SERVICE,
2435                // we have to revert to the flags we received initially *and* force remove
2436                // FLAG_FOREGROUND_SERVICE.
2437                sbn.getNotification().flags =
2438                        (r.mOriginalFlags & ~Notification.FLAG_FOREGROUND_SERVICE);
2439                mRankingHelper.sort(mNotificationList);
2440                mListeners.notifyPostedLocked(sbn, sbn /* oldSbn */);
2441            }
2442        }
2443    };
2444
2445    void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,
2446            final int callingPid, final String tag, final int id, final Notification notification,
2447            int[] idOut, int incomingUserId) {
2448        if (DBG) {
2449            Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id
2450                    + " notification=" + notification);
2451        }
2452        checkCallerIsSystemOrSameApp(pkg);
2453        final boolean isSystemNotification = isUidSystem(callingUid) || ("android".equals(pkg));
2454        final boolean isNotificationFromListener = mListeners.isListenerPackage(pkg);
2455
2456        final int userId = ActivityManager.handleIncomingUser(callingPid,
2457                callingUid, incomingUserId, true, false, "enqueueNotification", pkg);
2458        final UserHandle user = new UserHandle(userId);
2459
2460        // Fix the notification as best we can.
2461        try {
2462            final ApplicationInfo ai = getContext().getPackageManager().getApplicationInfoAsUser(
2463                    pkg, PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
2464                    (userId == UserHandle.USER_ALL) ? UserHandle.USER_SYSTEM : userId);
2465            Notification.addFieldsFromContext(ai, userId, notification);
2466        } catch (NameNotFoundException e) {
2467            Slog.e(TAG, "Cannot create a context for sending app", e);
2468            return;
2469        }
2470
2471        // Limit the number of notifications that any given package except the android
2472        // package or a registered listener can enqueue.  Prevents DOS attacks and deals with leaks.
2473        if (!isSystemNotification && !isNotificationFromListener) {
2474            synchronized (mNotificationList) {
2475                int count = 0;
2476                final int N = mNotificationList.size();
2477                for (int i=0; i<N; i++) {
2478                    final NotificationRecord r = mNotificationList.get(i);
2479                    if (r.sbn.getPackageName().equals(pkg) && r.sbn.getUserId() == userId) {
2480                        if (r.sbn.getId() == id && TextUtils.equals(r.sbn.getTag(), tag)) {
2481                            break;  // Allow updating existing notification
2482                        }
2483                        count++;
2484                        if (count >= MAX_PACKAGE_NOTIFICATIONS) {
2485                            Slog.e(TAG, "Package has already posted " + count
2486                                    + " notifications.  Not showing more.  package=" + pkg);
2487                            return;
2488                        }
2489                    }
2490                }
2491            }
2492        }
2493
2494        if (pkg == null || notification == null) {
2495            throw new IllegalArgumentException("null not allowed: pkg=" + pkg
2496                    + " id=" + id + " notification=" + notification);
2497        }
2498
2499        // Sanitize inputs
2500        notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN,
2501                Notification.PRIORITY_MAX);
2502
2503        // setup local book-keeping
2504        final StatusBarNotification n = new StatusBarNotification(
2505                pkg, opPkg, id, tag, callingUid, callingPid, 0, notification,
2506                user);
2507        final NotificationRecord r = new NotificationRecord(getContext(), n);
2508        mHandler.post(new EnqueueNotificationRunnable(userId, r));
2509
2510        idOut[0] = id;
2511    }
2512
2513    private class EnqueueNotificationRunnable implements Runnable {
2514        private final NotificationRecord r;
2515        private final int userId;
2516
2517        EnqueueNotificationRunnable(int userId, NotificationRecord r) {
2518            this.userId = userId;
2519            this.r = r;
2520        };
2521
2522        @Override
2523        public void run() {
2524
2525            synchronized (mNotificationList) {
2526                final StatusBarNotification n = r.sbn;
2527                if (DBG) Slog.d(TAG, "EnqueueNotificationRunnable.run for: " + n.getKey());
2528                NotificationRecord old = mNotificationsByKey.get(n.getKey());
2529                if (old != null) {
2530                    // Retain ranking information from previous record
2531                    r.copyRankingInformation(old);
2532                }
2533
2534                final int callingUid = n.getUid();
2535                final int callingPid = n.getInitialPid();
2536                final Notification notification = n.getNotification();
2537                final String pkg = n.getPackageName();
2538                final int id = n.getId();
2539                final String tag = n.getTag();
2540                final boolean isSystemNotification = isUidSystem(callingUid) ||
2541                        ("android".equals(pkg));
2542
2543                // Handle grouped notifications and bail out early if we
2544                // can to avoid extracting signals.
2545                handleGroupedNotificationLocked(r, old, callingUid, callingPid);
2546                boolean ignoreNotification =
2547                        removeUnusedGroupedNotificationLocked(r, old, callingUid, callingPid);
2548                if (DBG) Slog.d(TAG, "ignoreNotification is " + ignoreNotification);
2549
2550                // This conditional is a dirty hack to limit the logging done on
2551                //     behalf of the download manager without affecting other apps.
2552                if (!pkg.equals("com.android.providers.downloads")
2553                        || Log.isLoggable("DownloadManager", Log.VERBOSE)) {
2554                    int enqueueStatus = EVENTLOG_ENQUEUE_STATUS_NEW;
2555                    if (ignoreNotification) {
2556                        enqueueStatus = EVENTLOG_ENQUEUE_STATUS_IGNORED;
2557                    } else if (old != null) {
2558                        enqueueStatus = EVENTLOG_ENQUEUE_STATUS_UPDATE;
2559                    }
2560                    EventLogTags.writeNotificationEnqueue(callingUid, callingPid,
2561                            pkg, id, tag, userId, notification.toString(),
2562                            enqueueStatus);
2563                }
2564
2565                if (ignoreNotification) {
2566                    return;
2567                }
2568
2569                mRankingHelper.extractSignals(r);
2570
2571                final boolean isPackageSuspended = isPackageSuspendedForUser(pkg, callingUid);
2572
2573                // blocked apps
2574                if (r.getImportance() == NotificationListenerService.Ranking.IMPORTANCE_NONE
2575                        || !noteNotificationOp(pkg, callingUid) || isPackageSuspended) {
2576                    if (!isSystemNotification) {
2577                        if (isPackageSuspended) {
2578                            Slog.e(TAG, "Suppressing notification from package due to package "
2579                                    + "suspended by administrator.");
2580                            mUsageStats.registerSuspendedByAdmin(r);
2581                        } else {
2582                            Slog.e(TAG, "Suppressing notification from package by user request.");
2583                            mUsageStats.registerBlocked(r);
2584                        }
2585                        return;
2586                    }
2587                }
2588
2589                // tell the ranker service about the notification
2590                if (mRankerServices.isEnabled()) {
2591                    mRankerServices.onNotificationEnqueued(r);
2592                    // TODO delay the code below here for 100ms or until there is an answer
2593                }
2594
2595
2596                int index = indexOfNotificationLocked(n.getKey());
2597                if (index < 0) {
2598                    mNotificationList.add(r);
2599                    mUsageStats.registerPostedByApp(r);
2600                } else {
2601                    old = mNotificationList.get(index);
2602                    mNotificationList.set(index, r);
2603                    mUsageStats.registerUpdatedByApp(r, old);
2604                    // Make sure we don't lose the foreground service state.
2605                    notification.flags |=
2606                            old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;
2607                    r.isUpdate = true;
2608                }
2609
2610                mNotificationsByKey.put(n.getKey(), r);
2611
2612                // Ensure if this is a foreground service that the proper additional
2613                // flags are set.
2614                if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
2615                    notification.flags |= Notification.FLAG_ONGOING_EVENT
2616                            | Notification.FLAG_NO_CLEAR;
2617                }
2618
2619                applyZenModeLocked(r);
2620                mRankingHelper.sort(mNotificationList);
2621
2622                if (notification.getSmallIcon() != null) {
2623                    StatusBarNotification oldSbn = (old != null) ? old.sbn : null;
2624                    mListeners.notifyPostedLocked(n, oldSbn);
2625                } else {
2626                    Slog.e(TAG, "Not posting notification without small icon: " + notification);
2627                    if (old != null && !old.isCanceled) {
2628                        mListeners.notifyRemovedLocked(n);
2629                    }
2630                    // ATTENTION: in a future release we will bail out here
2631                    // so that we do not play sounds, show lights, etc. for invalid
2632                    // notifications
2633                    Slog.e(TAG, "WARNING: In a future release this will crash the app: "
2634                            + n.getPackageName());
2635                }
2636
2637                buzzBeepBlinkLocked(r);
2638            }
2639        }
2640    }
2641
2642    /**
2643     * Ensures that grouped notification receive their special treatment.
2644     *
2645     * <p>Cancels group children if the new notification causes a group to lose
2646     * its summary.</p>
2647     *
2648     * <p>Updates mSummaryByGroupKey.</p>
2649     */
2650    private void handleGroupedNotificationLocked(NotificationRecord r, NotificationRecord old,
2651            int callingUid, int callingPid) {
2652        StatusBarNotification sbn = r.sbn;
2653        Notification n = sbn.getNotification();
2654        String group = sbn.getGroupKey();
2655        boolean isSummary = n.isGroupSummary();
2656
2657        Notification oldN = old != null ? old.sbn.getNotification() : null;
2658        String oldGroup = old != null ? old.sbn.getGroupKey() : null;
2659        boolean oldIsSummary = old != null && oldN.isGroupSummary();
2660
2661        if (oldIsSummary) {
2662            NotificationRecord removedSummary = mSummaryByGroupKey.remove(oldGroup);
2663            if (removedSummary != old) {
2664                String removedKey =
2665                        removedSummary != null ? removedSummary.getKey() : "<null>";
2666                Slog.w(TAG, "Removed summary didn't match old notification: old=" + old.getKey() +
2667                        ", removed=" + removedKey);
2668            }
2669        }
2670        if (isSummary) {
2671            mSummaryByGroupKey.put(group, r);
2672        }
2673
2674        // Clear out group children of the old notification if the update
2675        // causes the group summary to go away. This happens when the old
2676        // notification was a summary and the new one isn't, or when the old
2677        // notification was a summary and its group key changed.
2678        if (oldIsSummary && (!isSummary || !oldGroup.equals(group))) {
2679            cancelGroupChildrenLocked(old, callingUid, callingPid, null,
2680                    REASON_GROUP_SUMMARY_CANCELED);
2681        }
2682    }
2683
2684    /**
2685     * Performs group notification optimizations if SysUI is the only active
2686     * notification listener and returns whether the given notification should
2687     * be ignored.
2688     *
2689     * <p>Returns true if the given notification is a child of a group with a
2690     * summary, which means that SysUI will never show it, and hence the new
2691     * notification can be safely ignored. Also cancels any previous instance
2692     * of the ignored notification.</p>
2693     *
2694     * <p>For summaries, cancels all children of that group, as SysUI will
2695     * never show them anymore.</p>
2696     *
2697     * @return true if the given notification can be ignored as an optimization
2698     */
2699    private boolean removeUnusedGroupedNotificationLocked(NotificationRecord r,
2700            NotificationRecord old, int callingUid, int callingPid) {
2701        if (!ENABLE_CHILD_NOTIFICATIONS) {
2702            // No optimizations are possible if listeners want groups.
2703            if (mListeners.notificationGroupsDesired()) {
2704                return false;
2705            }
2706
2707            StatusBarNotification sbn = r.sbn;
2708            String group = sbn.getGroupKey();
2709            boolean isSummary = sbn.getNotification().isGroupSummary();
2710            boolean isChild = !isSummary && sbn.isGroup();
2711
2712            NotificationRecord summary = mSummaryByGroupKey.get(group);
2713            if (isChild && summary != null) {
2714                // Child with an active summary -> ignore
2715                if (DBG) {
2716                    Slog.d(TAG, "Ignoring group child " + sbn.getKey() + " due to existing summary "
2717                            + summary.getKey());
2718                }
2719                // Make sure we don't leave an old version of the notification around.
2720                if (old != null) {
2721                    if (DBG) {
2722                        Slog.d(TAG, "Canceling old version of ignored group child " + sbn.getKey());
2723                    }
2724                    cancelNotificationLocked(old, false, REASON_GROUP_OPTIMIZATION);
2725                }
2726                return true;
2727            } else if (isSummary) {
2728                // Summary -> cancel children
2729                cancelGroupChildrenLocked(r, callingUid, callingPid, null,
2730                        REASON_GROUP_OPTIMIZATION);
2731            }
2732        }
2733        return false;
2734    }
2735
2736    @VisibleForTesting
2737    void buzzBeepBlinkLocked(NotificationRecord record) {
2738        boolean buzz = false;
2739        boolean beep = false;
2740        boolean blink = false;
2741
2742        final Notification notification = record.sbn.getNotification();
2743        final String key = record.getKey();
2744
2745        // Should this notification make noise, vibe, or use the LED?
2746        final boolean aboveThreshold = record.getImportance() >= IMPORTANCE_DEFAULT;
2747        final boolean canInterrupt = aboveThreshold && !record.isIntercepted();
2748        if (DBG || record.isIntercepted())
2749            Slog.v(TAG,
2750                    "pkg=" + record.sbn.getPackageName() + " canInterrupt=" + canInterrupt +
2751                            " intercept=" + record.isIntercepted()
2752            );
2753
2754        final int currentUser;
2755        final long token = Binder.clearCallingIdentity();
2756        try {
2757            currentUser = ActivityManager.getCurrentUser();
2758        } finally {
2759            Binder.restoreCallingIdentity(token);
2760        }
2761
2762        // If we're not supposed to beep, vibrate, etc. then don't.
2763        final String disableEffects = disableNotificationEffects(record);
2764        if (disableEffects != null) {
2765            ZenLog.traceDisableEffects(record, disableEffects);
2766        }
2767
2768        // Remember if this notification already owns the notification channels.
2769        boolean wasBeep = key != null && key.equals(mSoundNotificationKey);
2770        boolean wasBuzz = key != null && key.equals(mVibrateNotificationKey);
2771
2772        // These are set inside the conditional if the notification is allowed to make noise.
2773        boolean hasValidVibrate = false;
2774        boolean hasValidSound = false;
2775        if (disableEffects == null
2776                && (record.getUserId() == UserHandle.USER_ALL ||
2777                    record.getUserId() == currentUser ||
2778                    mUserProfiles.isCurrentProfile(record.getUserId()))
2779                && canInterrupt
2780                && mSystemReady
2781                && mAudioManager != null) {
2782            if (DBG) Slog.v(TAG, "Interrupting!");
2783
2784            // should we use the default notification sound? (indicated either by
2785            // DEFAULT_SOUND or because notification.sound is pointing at
2786            // Settings.System.NOTIFICATION_SOUND)
2787            final boolean useDefaultSound =
2788                   (notification.defaults & Notification.DEFAULT_SOUND) != 0 ||
2789                           Settings.System.DEFAULT_NOTIFICATION_URI
2790                                   .equals(notification.sound);
2791
2792            Uri soundUri = null;
2793            if (useDefaultSound) {
2794                soundUri = Settings.System.DEFAULT_NOTIFICATION_URI;
2795
2796                // check to see if the default notification sound is silent
2797                ContentResolver resolver = getContext().getContentResolver();
2798                hasValidSound = Settings.System.getString(resolver,
2799                       Settings.System.NOTIFICATION_SOUND) != null;
2800            } else if (notification.sound != null) {
2801                soundUri = notification.sound;
2802                hasValidSound = (soundUri != null);
2803            }
2804
2805            // Does the notification want to specify its own vibration?
2806            final boolean hasCustomVibrate = notification.vibrate != null;
2807
2808            // new in 4.2: if there was supposed to be a sound and we're in vibrate
2809            // mode, and no other vibration is specified, we fall back to vibration
2810            final boolean convertSoundToVibration =
2811                    !hasCustomVibrate
2812                            && hasValidSound
2813                            && (mAudioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_VIBRATE);
2814
2815            // The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback.
2816            final boolean useDefaultVibrate =
2817                    (notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
2818
2819            hasValidVibrate = useDefaultVibrate || convertSoundToVibration ||
2820                    hasCustomVibrate;
2821
2822            // We can alert, and we're allowed to alert, but if the developer asked us to only do
2823            // it once, and we already have, then don't.
2824            if (!(record.isUpdate
2825                    && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0)) {
2826
2827                sendAccessibilityEvent(notification, record.sbn.getPackageName());
2828
2829                if (hasValidSound) {
2830                    boolean looping =
2831                            (notification.flags & Notification.FLAG_INSISTENT) != 0;
2832                    AudioAttributes audioAttributes = audioAttributesForNotification(notification);
2833                    mSoundNotificationKey = key;
2834                    // do not play notifications if stream volume is 0 (typically because
2835                    // ringer mode is silent) or if there is a user of exclusive audio focus
2836                    if ((mAudioManager.getStreamVolume(
2837                            AudioAttributes.toLegacyStreamType(audioAttributes)) != 0)
2838                            && !mAudioManager.isAudioFocusExclusive()) {
2839                        final long identity = Binder.clearCallingIdentity();
2840                        try {
2841                            final IRingtonePlayer player =
2842                                    mAudioManager.getRingtonePlayer();
2843                            if (player != null) {
2844                                if (DBG) Slog.v(TAG, "Playing sound " + soundUri
2845                                        + " with attributes " + audioAttributes);
2846                                player.playAsync(soundUri, record.sbn.getUser(), looping,
2847                                        audioAttributes);
2848                                beep = true;
2849                            }
2850                        } catch (RemoteException e) {
2851                        } finally {
2852                            Binder.restoreCallingIdentity(identity);
2853                        }
2854                    }
2855                }
2856
2857                if (hasValidVibrate && !(mAudioManager.getRingerModeInternal()
2858                        == AudioManager.RINGER_MODE_SILENT)) {
2859                    mVibrateNotificationKey = key;
2860
2861                    if (useDefaultVibrate || convertSoundToVibration) {
2862                        // Escalate privileges so we can use the vibrator even if the
2863                        // notifying app does not have the VIBRATE permission.
2864                        long identity = Binder.clearCallingIdentity();
2865                        try {
2866                            mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
2867                                    useDefaultVibrate ? mDefaultVibrationPattern
2868                                            : mFallbackVibrationPattern,
2869                                    ((notification.flags & Notification.FLAG_INSISTENT) != 0)
2870                                            ? 0: -1, audioAttributesForNotification(notification));
2871                            buzz = true;
2872                        } finally {
2873                            Binder.restoreCallingIdentity(identity);
2874                        }
2875                    } else if (notification.vibrate.length > 1) {
2876                        // If you want your own vibration pattern, you need the VIBRATE
2877                        // permission
2878                        mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
2879                                notification.vibrate,
2880                                ((notification.flags & Notification.FLAG_INSISTENT) != 0)
2881                                        ? 0: -1, audioAttributesForNotification(notification));
2882                        buzz = true;
2883                    }
2884                }
2885            }
2886
2887        }
2888        // If a notification is updated to remove the actively playing sound or vibrate,
2889        // cancel that feedback now
2890        if (wasBeep && !hasValidSound) {
2891            clearSoundLocked();
2892        }
2893        if (wasBuzz && !hasValidVibrate) {
2894            clearVibrateLocked();
2895        }
2896
2897        // light
2898        // release the light
2899        boolean wasShowLights = mLights.remove(key);
2900        if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0 && aboveThreshold
2901                && ((record.getSuppressedVisualEffects()
2902                & NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_OFF) == 0)) {
2903            mLights.add(key);
2904            updateLightsLocked();
2905            if (mUseAttentionLight) {
2906                mAttentionLight.pulse();
2907            }
2908            blink = true;
2909        } else if (wasShowLights) {
2910            updateLightsLocked();
2911        }
2912        if (buzz || beep || blink) {
2913            if (((record.getSuppressedVisualEffects()
2914                    & NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_OFF) != 0)) {
2915                if (DBG) Slog.v(TAG, "Suppressed SystemUI from triggering screen on");
2916            } else {
2917                EventLogTags.writeNotificationAlert(key,
2918                        buzz ? 1 : 0, beep ? 1 : 0, blink ? 1 : 0);
2919                mHandler.post(mBuzzBeepBlinked);
2920            }
2921        }
2922    }
2923
2924    private static AudioAttributes audioAttributesForNotification(Notification n) {
2925        if (n.audioAttributes != null
2926                && !Notification.AUDIO_ATTRIBUTES_DEFAULT.equals(n.audioAttributes)) {
2927            // the audio attributes are set and different from the default, use them
2928            return n.audioAttributes;
2929        } else if (n.audioStreamType >= 0 && n.audioStreamType < AudioSystem.getNumStreamTypes()) {
2930            // the stream type is valid, use it
2931            return new AudioAttributes.Builder()
2932                    .setInternalLegacyStreamType(n.audioStreamType)
2933                    .build();
2934        } else if (n.audioStreamType == AudioSystem.STREAM_DEFAULT) {
2935            return Notification.AUDIO_ATTRIBUTES_DEFAULT;
2936        } else {
2937            Log.w(TAG, String.format("Invalid stream type: %d", n.audioStreamType));
2938            return Notification.AUDIO_ATTRIBUTES_DEFAULT;
2939        }
2940    }
2941
2942    void showNextToastLocked() {
2943        ToastRecord record = mToastQueue.get(0);
2944        while (record != null) {
2945            if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback);
2946            try {
2947                record.callback.show();
2948                scheduleTimeoutLocked(record);
2949                return;
2950            } catch (RemoteException e) {
2951                Slog.w(TAG, "Object died trying to show notification " + record.callback
2952                        + " in package " + record.pkg);
2953                // remove it from the list and let the process die
2954                int index = mToastQueue.indexOf(record);
2955                if (index >= 0) {
2956                    mToastQueue.remove(index);
2957                }
2958                keepProcessAliveLocked(record.pid);
2959                if (mToastQueue.size() > 0) {
2960                    record = mToastQueue.get(0);
2961                } else {
2962                    record = null;
2963                }
2964            }
2965        }
2966    }
2967
2968    void cancelToastLocked(int index) {
2969        ToastRecord record = mToastQueue.get(index);
2970        try {
2971            record.callback.hide();
2972        } catch (RemoteException e) {
2973            Slog.w(TAG, "Object died trying to hide notification " + record.callback
2974                    + " in package " + record.pkg);
2975            // don't worry about this, we're about to remove it from
2976            // the list anyway
2977        }
2978        mToastQueue.remove(index);
2979        keepProcessAliveLocked(record.pid);
2980        if (mToastQueue.size() > 0) {
2981            // Show the next one. If the callback fails, this will remove
2982            // it from the list, so don't assume that the list hasn't changed
2983            // after this point.
2984            showNextToastLocked();
2985        }
2986    }
2987
2988    private void scheduleTimeoutLocked(ToastRecord r)
2989    {
2990        mHandler.removeCallbacksAndMessages(r);
2991        Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r);
2992        long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY;
2993        mHandler.sendMessageDelayed(m, delay);
2994    }
2995
2996    private void handleTimeout(ToastRecord record)
2997    {
2998        if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback);
2999        synchronized (mToastQueue) {
3000            int index = indexOfToastLocked(record.pkg, record.callback);
3001            if (index >= 0) {
3002                cancelToastLocked(index);
3003            }
3004        }
3005    }
3006
3007    // lock on mToastQueue
3008    int indexOfToastLocked(String pkg, ITransientNotification callback)
3009    {
3010        IBinder cbak = callback.asBinder();
3011        ArrayList<ToastRecord> list = mToastQueue;
3012        int len = list.size();
3013        for (int i=0; i<len; i++) {
3014            ToastRecord r = list.get(i);
3015            if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) {
3016                return i;
3017            }
3018        }
3019        return -1;
3020    }
3021
3022    // lock on mToastQueue
3023    void keepProcessAliveLocked(int pid)
3024    {
3025        int toastCount = 0; // toasts from this pid
3026        ArrayList<ToastRecord> list = mToastQueue;
3027        int N = list.size();
3028        for (int i=0; i<N; i++) {
3029            ToastRecord r = list.get(i);
3030            if (r.pid == pid) {
3031                toastCount++;
3032            }
3033        }
3034        try {
3035            mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0);
3036        } catch (RemoteException e) {
3037            // Shouldn't happen.
3038        }
3039    }
3040
3041    private void handleRankingReconsideration(Message message) {
3042        if (!(message.obj instanceof RankingReconsideration)) return;
3043        RankingReconsideration recon = (RankingReconsideration) message.obj;
3044        recon.run();
3045        boolean changed;
3046        synchronized (mNotificationList) {
3047            final NotificationRecord record = mNotificationsByKey.get(recon.getKey());
3048            if (record == null) {
3049                return;
3050            }
3051            int indexBefore = findNotificationRecordIndexLocked(record);
3052            boolean interceptBefore = record.isIntercepted();
3053            int visibilityBefore = record.getPackageVisibilityOverride();
3054            recon.applyChangesLocked(record);
3055            applyZenModeLocked(record);
3056            mRankingHelper.sort(mNotificationList);
3057            int indexAfter = findNotificationRecordIndexLocked(record);
3058            boolean interceptAfter = record.isIntercepted();
3059            int visibilityAfter = record.getPackageVisibilityOverride();
3060            changed = indexBefore != indexAfter || interceptBefore != interceptAfter
3061                    || visibilityBefore != visibilityAfter;
3062            if (interceptBefore && !interceptAfter) {
3063                buzzBeepBlinkLocked(record);
3064            }
3065        }
3066        if (changed) {
3067            scheduleSendRankingUpdate();
3068        }
3069    }
3070
3071    private void handleRankingSort() {
3072        synchronized (mNotificationList) {
3073            final int N = mNotificationList.size();
3074            ArrayList<String> orderBefore = new ArrayList<String>(N);
3075            ArrayList<String> groupOverrideBefore = new ArrayList<>(N);
3076            int[] visibilities = new int[N];
3077            int[] importances = new int[N];
3078            for (int i = 0; i < N; i++) {
3079                final NotificationRecord r = mNotificationList.get(i);
3080                orderBefore.add(r.getKey());
3081                groupOverrideBefore.add(r.sbn.getGroupKey());
3082                visibilities[i] = r.getPackageVisibilityOverride();
3083                importances[i] = r.getImportance();
3084                mRankingHelper.extractSignals(r);
3085            }
3086            mRankingHelper.sort(mNotificationList);
3087            for (int i = 0; i < N; i++) {
3088                final NotificationRecord r = mNotificationList.get(i);
3089                if (!orderBefore.get(i).equals(r.getKey())
3090                        || visibilities[i] != r.getPackageVisibilityOverride()
3091                        || importances[i] != r.getImportance()
3092                        || !groupOverrideBefore.get(i).equals(r.sbn.getGroupKey())) {
3093                    scheduleSendRankingUpdate();
3094                    return;
3095                }
3096            }
3097        }
3098    }
3099
3100    // let zen mode evaluate this record
3101    private void applyZenModeLocked(NotificationRecord record) {
3102        record.setIntercepted(mZenModeHelper.shouldIntercept(record));
3103        if (record.isIntercepted()) {
3104            int suppressed = (mZenModeHelper.shouldSuppressWhenScreenOff()
3105                    ? SUPPRESSED_EFFECT_SCREEN_OFF : 0)
3106                    | (mZenModeHelper.shouldSuppressWhenScreenOn()
3107                    ? SUPPRESSED_EFFECT_SCREEN_ON : 0);
3108            record.setSuppressedVisualEffects(suppressed);
3109        }
3110    }
3111
3112    // lock on mNotificationList
3113    private int findNotificationRecordIndexLocked(NotificationRecord target) {
3114        return mRankingHelper.indexOf(mNotificationList, target);
3115    }
3116
3117    private void scheduleSendRankingUpdate() {
3118        if (!mHandler.hasMessages(MESSAGE_SEND_RANKING_UPDATE)) {
3119            Message m = Message.obtain(mHandler, MESSAGE_SEND_RANKING_UPDATE);
3120            mHandler.sendMessage(m);
3121        }
3122    }
3123
3124    private void handleSendRankingUpdate() {
3125        synchronized (mNotificationList) {
3126            mListeners.notifyRankingUpdateLocked();
3127        }
3128    }
3129
3130    private void scheduleListenerHintsChanged(int state) {
3131        mHandler.removeMessages(MESSAGE_LISTENER_HINTS_CHANGED);
3132        mHandler.obtainMessage(MESSAGE_LISTENER_HINTS_CHANGED, state, 0).sendToTarget();
3133    }
3134
3135    private void scheduleInterruptionFilterChanged(int listenerInterruptionFilter) {
3136        mHandler.removeMessages(MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED);
3137        mHandler.obtainMessage(
3138                MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED,
3139                listenerInterruptionFilter,
3140                0).sendToTarget();
3141    }
3142
3143    private void handleListenerHintsChanged(int hints) {
3144        synchronized (mNotificationList) {
3145            mListeners.notifyListenerHintsChangedLocked(hints);
3146        }
3147    }
3148
3149    private void handleListenerInterruptionFilterChanged(int interruptionFilter) {
3150        synchronized (mNotificationList) {
3151            mListeners.notifyInterruptionFilterChanged(interruptionFilter);
3152        }
3153    }
3154
3155    private final class WorkerHandler extends Handler
3156    {
3157        @Override
3158        public void handleMessage(Message msg)
3159        {
3160            switch (msg.what)
3161            {
3162                case MESSAGE_TIMEOUT:
3163                    handleTimeout((ToastRecord)msg.obj);
3164                    break;
3165                case MESSAGE_SAVE_POLICY_FILE:
3166                    handleSavePolicyFile();
3167                    break;
3168                case MESSAGE_SEND_RANKING_UPDATE:
3169                    handleSendRankingUpdate();
3170                    break;
3171                case MESSAGE_LISTENER_HINTS_CHANGED:
3172                    handleListenerHintsChanged(msg.arg1);
3173                    break;
3174                case MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED:
3175                    handleListenerInterruptionFilterChanged(msg.arg1);
3176                    break;
3177            }
3178        }
3179
3180    }
3181
3182    private final class RankingHandlerWorker extends Handler implements RankingHandler
3183    {
3184        public RankingHandlerWorker(Looper looper) {
3185            super(looper);
3186        }
3187
3188        @Override
3189        public void handleMessage(Message msg) {
3190            switch (msg.what) {
3191                case MESSAGE_RECONSIDER_RANKING:
3192                    handleRankingReconsideration(msg);
3193                    break;
3194                case MESSAGE_RANKING_SORT:
3195                    handleRankingSort();
3196                    break;
3197            }
3198        }
3199
3200        public void requestSort() {
3201            removeMessages(MESSAGE_RANKING_SORT);
3202            sendEmptyMessage(MESSAGE_RANKING_SORT);
3203        }
3204
3205        public void requestReconsideration(RankingReconsideration recon) {
3206            Message m = Message.obtain(this,
3207                    NotificationManagerService.MESSAGE_RECONSIDER_RANKING, recon);
3208            long delay = recon.getDelay(TimeUnit.MILLISECONDS);
3209            sendMessageDelayed(m, delay);
3210        }
3211    }
3212
3213    // Notifications
3214    // ============================================================================
3215    static int clamp(int x, int low, int high) {
3216        return (x < low) ? low : ((x > high) ? high : x);
3217    }
3218
3219    void sendAccessibilityEvent(Notification notification, CharSequence packageName) {
3220        AccessibilityManager manager = AccessibilityManager.getInstance(getContext());
3221        if (!manager.isEnabled()) {
3222            return;
3223        }
3224
3225        AccessibilityEvent event =
3226            AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
3227        event.setPackageName(packageName);
3228        event.setClassName(Notification.class.getName());
3229        event.setParcelableData(notification);
3230        CharSequence tickerText = notification.tickerText;
3231        if (!TextUtils.isEmpty(tickerText)) {
3232            event.getText().add(tickerText);
3233        }
3234
3235        manager.sendAccessibilityEvent(event);
3236    }
3237
3238    private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason) {
3239        // tell the app
3240        if (sendDelete) {
3241            if (r.getNotification().deleteIntent != null) {
3242                try {
3243                    r.getNotification().deleteIntent.send();
3244                } catch (PendingIntent.CanceledException ex) {
3245                    // do nothing - there's no relevant way to recover, and
3246                    //     no reason to let this propagate
3247                    Slog.w(TAG, "canceled PendingIntent for " + r.sbn.getPackageName(), ex);
3248                }
3249            }
3250        }
3251
3252        // status bar
3253        if (r.getNotification().getSmallIcon() != null) {
3254            r.isCanceled = true;
3255            mListeners.notifyRemovedLocked(r.sbn);
3256        }
3257
3258        final String canceledKey = r.getKey();
3259
3260        // sound
3261        if (canceledKey.equals(mSoundNotificationKey)) {
3262            mSoundNotificationKey = null;
3263            final long identity = Binder.clearCallingIdentity();
3264            try {
3265                final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
3266                if (player != null) {
3267                    player.stopAsync();
3268                }
3269            } catch (RemoteException e) {
3270            } finally {
3271                Binder.restoreCallingIdentity(identity);
3272            }
3273        }
3274
3275        // vibrate
3276        if (canceledKey.equals(mVibrateNotificationKey)) {
3277            mVibrateNotificationKey = null;
3278            long identity = Binder.clearCallingIdentity();
3279            try {
3280                mVibrator.cancel();
3281            }
3282            finally {
3283                Binder.restoreCallingIdentity(identity);
3284            }
3285        }
3286
3287        // light
3288        mLights.remove(canceledKey);
3289
3290        // Record usage stats
3291        // TODO: add unbundling stats?
3292        switch (reason) {
3293            case REASON_DELEGATE_CANCEL:
3294            case REASON_DELEGATE_CANCEL_ALL:
3295            case REASON_LISTENER_CANCEL:
3296            case REASON_LISTENER_CANCEL_ALL:
3297                mUsageStats.registerDismissedByUser(r);
3298                break;
3299            case REASON_APP_CANCEL:
3300            case REASON_APP_CANCEL_ALL:
3301                mUsageStats.registerRemovedByApp(r);
3302                break;
3303        }
3304
3305        mNotificationsByKey.remove(r.sbn.getKey());
3306        String groupKey = r.getGroupKey();
3307        NotificationRecord groupSummary = mSummaryByGroupKey.get(groupKey);
3308        if (groupSummary != null && groupSummary.getKey().equals(r.getKey())) {
3309            mSummaryByGroupKey.remove(groupKey);
3310        }
3311        if (r.sbn.getKey().equals(mAutobundledSummaries.get(r.sbn.getPackageName()))) {
3312            mAutobundledSummaries.remove(r.sbn.getPackageName());
3313        }
3314
3315        // Save it for users of getHistoricalNotifications()
3316        mArchive.record(r.sbn);
3317
3318        final long now = System.currentTimeMillis();
3319        EventLogTags.writeNotificationCanceled(canceledKey, reason,
3320                r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
3321    }
3322
3323    /**
3324     * Cancels a notification ONLY if it has all of the {@code mustHaveFlags}
3325     * and none of the {@code mustNotHaveFlags}.
3326     */
3327    void cancelNotification(final int callingUid, final int callingPid,
3328            final String pkg, final String tag, final int id,
3329            final int mustHaveFlags, final int mustNotHaveFlags, final boolean sendDelete,
3330            final int userId, final int reason, final ManagedServiceInfo listener) {
3331        // In enqueueNotificationInternal notifications are added by scheduling the
3332        // work on the worker handler. Hence, we also schedule the cancel on this
3333        // handler to avoid a scenario where an add notification call followed by a
3334        // remove notification call ends up in not removing the notification.
3335        mHandler.post(new Runnable() {
3336            @Override
3337            public void run() {
3338                String listenerName = listener == null ? null : listener.component.toShortString();
3339                if (DBG) EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, id, tag,
3340                        userId, mustHaveFlags, mustNotHaveFlags, reason, listenerName);
3341
3342                synchronized (mNotificationList) {
3343                    int index = indexOfNotificationLocked(pkg, tag, id, userId);
3344                    if (index >= 0) {
3345                        NotificationRecord r = mNotificationList.get(index);
3346
3347                        // Ideally we'd do this in the caller of this method. However, that would
3348                        // require the caller to also find the notification.
3349                        if (reason == REASON_DELEGATE_CLICK) {
3350                            mUsageStats.registerClickedByUser(r);
3351                        }
3352
3353                        if ((r.getNotification().flags & mustHaveFlags) != mustHaveFlags) {
3354                            return;
3355                        }
3356                        if ((r.getNotification().flags & mustNotHaveFlags) != 0) {
3357                            return;
3358                        }
3359
3360                        mNotificationList.remove(index);
3361
3362                        cancelNotificationLocked(r, sendDelete, reason);
3363                        cancelGroupChildrenLocked(r, callingUid, callingPid, listenerName,
3364                                REASON_GROUP_SUMMARY_CANCELED);
3365                        updateLightsLocked();
3366                    }
3367                }
3368            }
3369        });
3370    }
3371
3372    /**
3373     * Determine whether the userId applies to the notification in question, either because
3374     * they match exactly, or one of them is USER_ALL (which is treated as a wildcard).
3375     */
3376    private boolean notificationMatchesUserId(NotificationRecord r, int userId) {
3377        return
3378                // looking for USER_ALL notifications? match everything
3379                   userId == UserHandle.USER_ALL
3380                // a notification sent to USER_ALL matches any query
3381                || r.getUserId() == UserHandle.USER_ALL
3382                // an exact user match
3383                || r.getUserId() == userId;
3384    }
3385
3386    /**
3387     * Determine whether the userId applies to the notification in question, either because
3388     * they match exactly, or one of them is USER_ALL (which is treated as a wildcard) or
3389     * because it matches one of the users profiles.
3390     */
3391    private boolean notificationMatchesCurrentProfiles(NotificationRecord r, int userId) {
3392        return notificationMatchesUserId(r, userId)
3393                || mUserProfiles.isCurrentProfile(r.getUserId());
3394    }
3395
3396    /**
3397     * Cancels all notifications from a given package that have all of the
3398     * {@code mustHaveFlags}.
3399     */
3400    boolean cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, int mustHaveFlags,
3401            int mustNotHaveFlags, boolean doit, int userId, int reason,
3402            ManagedServiceInfo listener) {
3403        String listenerName = listener == null ? null : listener.component.toShortString();
3404        EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
3405                pkg, userId, mustHaveFlags, mustNotHaveFlags, reason,
3406                listenerName);
3407
3408        synchronized (mNotificationList) {
3409            final int N = mNotificationList.size();
3410            ArrayList<NotificationRecord> canceledNotifications = null;
3411            for (int i = N-1; i >= 0; --i) {
3412                NotificationRecord r = mNotificationList.get(i);
3413                if (!notificationMatchesUserId(r, userId)) {
3414                    continue;
3415                }
3416                // Don't remove notifications to all, if there's no package name specified
3417                if (r.getUserId() == UserHandle.USER_ALL && pkg == null) {
3418                    continue;
3419                }
3420                if ((r.getFlags() & mustHaveFlags) != mustHaveFlags) {
3421                    continue;
3422                }
3423                if ((r.getFlags() & mustNotHaveFlags) != 0) {
3424                    continue;
3425                }
3426                if (pkg != null && !r.sbn.getPackageName().equals(pkg)) {
3427                    continue;
3428                }
3429                if (canceledNotifications == null) {
3430                    canceledNotifications = new ArrayList<>();
3431                }
3432                canceledNotifications.add(r);
3433                if (!doit) {
3434                    return true;
3435                }
3436                mNotificationList.remove(i);
3437                cancelNotificationLocked(r, false, reason);
3438            }
3439            if (doit && canceledNotifications != null) {
3440                final int M = canceledNotifications.size();
3441                for (int i = 0; i < M; i++) {
3442                    cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid,
3443                            listenerName, REASON_GROUP_SUMMARY_CANCELED);
3444                }
3445            }
3446            if (canceledNotifications != null) {
3447                updateLightsLocked();
3448            }
3449            return canceledNotifications != null;
3450        }
3451    }
3452
3453    void cancelAllLocked(int callingUid, int callingPid, int userId, int reason,
3454            ManagedServiceInfo listener, boolean includeCurrentProfiles) {
3455        String listenerName = listener == null ? null : listener.component.toShortString();
3456        EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
3457                null, userId, 0, 0, reason, listenerName);
3458
3459        ArrayList<NotificationRecord> canceledNotifications = null;
3460        final int N = mNotificationList.size();
3461        for (int i=N-1; i>=0; i--) {
3462            NotificationRecord r = mNotificationList.get(i);
3463            if (includeCurrentProfiles) {
3464                if (!notificationMatchesCurrentProfiles(r, userId)) {
3465                    continue;
3466                }
3467            } else {
3468                if (!notificationMatchesUserId(r, userId)) {
3469                    continue;
3470                }
3471            }
3472
3473            if ((r.getFlags() & (Notification.FLAG_ONGOING_EVENT
3474                            | Notification.FLAG_NO_CLEAR)) == 0) {
3475                mNotificationList.remove(i);
3476                cancelNotificationLocked(r, true, reason);
3477                // Make a note so we can cancel children later.
3478                if (canceledNotifications == null) {
3479                    canceledNotifications = new ArrayList<>();
3480                }
3481                canceledNotifications.add(r);
3482            }
3483        }
3484        int M = canceledNotifications != null ? canceledNotifications.size() : 0;
3485        for (int i = 0; i < M; i++) {
3486            cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid,
3487                    listenerName, REASON_GROUP_SUMMARY_CANCELED);
3488        }
3489        updateLightsLocked();
3490    }
3491
3492    // Warning: The caller is responsible for invoking updateLightsLocked().
3493    private void cancelGroupChildrenLocked(NotificationRecord r, int callingUid, int callingPid,
3494            String listenerName, int reason) {
3495        Notification n = r.getNotification();
3496        if (!n.isGroupSummary()) {
3497            return;
3498        }
3499
3500        String pkg = r.sbn.getPackageName();
3501        int userId = r.getUserId();
3502
3503        if (pkg == null) {
3504            if (DBG) Log.e(TAG, "No package for group summary: " + r.getKey());
3505            return;
3506        }
3507
3508        final int N = mNotificationList.size();
3509        for (int i = N - 1; i >= 0; i--) {
3510            NotificationRecord childR = mNotificationList.get(i);
3511            StatusBarNotification childSbn = childR.sbn;
3512            if ((childSbn.isGroup() && !childSbn.getNotification().isGroupSummary()) &&
3513                    childR.getGroupKey().equals(r.getGroupKey())) {
3514                EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, childSbn.getId(),
3515                        childSbn.getTag(), userId, 0, 0, reason, listenerName);
3516                mNotificationList.remove(i);
3517                cancelNotificationLocked(childR, false, reason);
3518            }
3519        }
3520    }
3521
3522    // lock on mNotificationList
3523    void updateLightsLocked()
3524    {
3525        // handle notification lights
3526        NotificationRecord ledNotification = null;
3527        while (ledNotification == null && !mLights.isEmpty()) {
3528            final String owner = mLights.get(mLights.size() - 1);
3529            ledNotification = mNotificationsByKey.get(owner);
3530            if (ledNotification == null) {
3531                Slog.wtfStack(TAG, "LED Notification does not exist: " + owner);
3532                mLights.remove(owner);
3533            }
3534        }
3535
3536        // Don't flash while we are in a call or screen is on
3537        if (ledNotification == null || mInCall || mScreenOn) {
3538            mNotificationLight.turnOff();
3539            if (mStatusBar != null) {
3540                mStatusBar.notificationLightOff();
3541            }
3542        } else {
3543            final Notification ledno = ledNotification.sbn.getNotification();
3544            int ledARGB = ledno.ledARGB;
3545            int ledOnMS = ledno.ledOnMS;
3546            int ledOffMS = ledno.ledOffMS;
3547            if ((ledno.defaults & Notification.DEFAULT_LIGHTS) != 0) {
3548                ledARGB = mDefaultNotificationColor;
3549                ledOnMS = mDefaultNotificationLedOn;
3550                ledOffMS = mDefaultNotificationLedOff;
3551            }
3552            if (mNotificationPulseEnabled) {
3553                // pulse repeatedly
3554                mNotificationLight.setFlashing(ledARGB, Light.LIGHT_FLASH_TIMED,
3555                        ledOnMS, ledOffMS);
3556            }
3557            if (mStatusBar != null) {
3558                // let SystemUI make an independent decision
3559                mStatusBar.notificationLightPulse(ledARGB, ledOnMS, ledOffMS);
3560            }
3561        }
3562    }
3563
3564    // lock on mNotificationList
3565    int indexOfNotificationLocked(String pkg, String tag, int id, int userId)
3566    {
3567        ArrayList<NotificationRecord> list = mNotificationList;
3568        final int len = list.size();
3569        for (int i=0; i<len; i++) {
3570            NotificationRecord r = list.get(i);
3571            if (notificationMatchesUserId(r, userId) && r.sbn.getId() == id &&
3572                    TextUtils.equals(r.sbn.getTag(), tag) && r.sbn.getPackageName().equals(pkg)) {
3573                return i;
3574            }
3575        }
3576        return -1;
3577    }
3578
3579    // lock on mNotificationList
3580    int indexOfNotificationLocked(String key) {
3581        final int N = mNotificationList.size();
3582        for (int i = 0; i < N; i++) {
3583            if (key.equals(mNotificationList.get(i).getKey())) {
3584                return i;
3585            }
3586        }
3587        return -1;
3588    }
3589
3590    private void updateNotificationPulse() {
3591        synchronized (mNotificationList) {
3592            updateLightsLocked();
3593        }
3594    }
3595
3596    private static boolean isUidSystem(int uid) {
3597        final int appid = UserHandle.getAppId(uid);
3598        return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0);
3599    }
3600
3601    private static boolean isCallerSystem() {
3602        return isUidSystem(Binder.getCallingUid());
3603    }
3604
3605    private static void checkCallerIsSystem() {
3606        if (isCallerSystem()) {
3607            return;
3608        }
3609        throw new SecurityException("Disallowed call for uid " + Binder.getCallingUid());
3610    }
3611
3612    private static void checkCallerIsSystemOrSameApp(String pkg) {
3613        if (isCallerSystem()) {
3614            return;
3615        }
3616        final int uid = Binder.getCallingUid();
3617        try {
3618            ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo(
3619                    pkg, 0, UserHandle.getCallingUserId());
3620            if (ai == null) {
3621                throw new SecurityException("Unknown package " + pkg);
3622            }
3623            if (!UserHandle.isSameApp(ai.uid, uid)) {
3624                throw new SecurityException("Calling uid " + uid + " gave package"
3625                        + pkg + " which is owned by uid " + ai.uid);
3626            }
3627        } catch (RemoteException re) {
3628            throw new SecurityException("Unknown package " + pkg + "\n" + re);
3629        }
3630    }
3631
3632    private static String callStateToString(int state) {
3633        switch (state) {
3634            case TelephonyManager.CALL_STATE_IDLE: return "CALL_STATE_IDLE";
3635            case TelephonyManager.CALL_STATE_RINGING: return "CALL_STATE_RINGING";
3636            case TelephonyManager.CALL_STATE_OFFHOOK: return "CALL_STATE_OFFHOOK";
3637            default: return "CALL_STATE_UNKNOWN_" + state;
3638        }
3639    }
3640
3641    private void listenForCallState() {
3642        TelephonyManager.from(getContext()).listen(new PhoneStateListener() {
3643            @Override
3644            public void onCallStateChanged(int state, String incomingNumber) {
3645                if (mCallState == state) return;
3646                if (DBG) Slog.d(TAG, "Call state changed: " + callStateToString(state));
3647                mCallState = state;
3648            }
3649        }, PhoneStateListener.LISTEN_CALL_STATE);
3650    }
3651
3652    /**
3653     * Generates a NotificationRankingUpdate from 'sbns', considering only
3654     * notifications visible to the given listener.
3655     *
3656     * <p>Caller must hold a lock on mNotificationList.</p>
3657     */
3658    private NotificationRankingUpdate makeRankingUpdateLocked(ManagedServiceInfo info) {
3659        final int N = mNotificationList.size();
3660        ArrayList<String> keys = new ArrayList<String>(N);
3661        ArrayList<String> interceptedKeys = new ArrayList<String>(N);
3662        ArrayList<Integer> importance = new ArrayList<>(N);
3663        Bundle overrideGroupKeys = new Bundle();
3664        Bundle visibilityOverrides = new Bundle();
3665        Bundle suppressedVisualEffects = new Bundle();
3666        Bundle explanation = new Bundle();
3667        for (int i = 0; i < N; i++) {
3668            NotificationRecord record = mNotificationList.get(i);
3669            if (!isVisibleToListener(record.sbn, info)) {
3670                continue;
3671            }
3672            final String key = record.sbn.getKey();
3673            keys.add(key);
3674            importance.add(record.getImportance());
3675            if (record.getImportanceExplanation() != null) {
3676                explanation.putCharSequence(key, record.getImportanceExplanation());
3677            }
3678            if (record.isIntercepted()) {
3679                interceptedKeys.add(key);
3680
3681            }
3682            suppressedVisualEffects.putInt(key, record.getSuppressedVisualEffects());
3683            if (record.getPackageVisibilityOverride()
3684                    != NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE) {
3685                visibilityOverrides.putInt(key, record.getPackageVisibilityOverride());
3686            }
3687            overrideGroupKeys.putString(key, record.sbn.getOverrideGroupKey());
3688        }
3689        final int M = keys.size();
3690        String[] keysAr = keys.toArray(new String[M]);
3691        String[] interceptedKeysAr = interceptedKeys.toArray(new String[interceptedKeys.size()]);
3692        int[] importanceAr = new int[M];
3693        for (int i = 0; i < M; i++) {
3694            importanceAr[i] = importance.get(i);
3695        }
3696        return new NotificationRankingUpdate(keysAr, interceptedKeysAr, visibilityOverrides,
3697                suppressedVisualEffects, importanceAr, explanation, overrideGroupKeys);
3698    }
3699
3700    private boolean isVisibleToListener(StatusBarNotification sbn, ManagedServiceInfo listener) {
3701        if (!listener.enabledAndUserMatches(sbn.getUserId())) {
3702            return false;
3703        }
3704        // TODO: remove this for older listeners.
3705        return true;
3706    }
3707
3708    private boolean isPackageSuspendedForUser(String pkg, int uid) {
3709        int userId = UserHandle.getUserId(uid);
3710        try {
3711            return AppGlobals.getPackageManager().isPackageSuspendedForUser(pkg, userId);
3712        } catch (RemoteException re) {
3713            throw new SecurityException("Could not talk to package manager service");
3714        } catch (IllegalArgumentException ex) {
3715            // Package not found.
3716            return false;
3717        }
3718    }
3719
3720    private class TrimCache {
3721        StatusBarNotification heavy;
3722        StatusBarNotification sbnClone;
3723        StatusBarNotification sbnCloneLight;
3724
3725        TrimCache(StatusBarNotification sbn) {
3726            heavy = sbn;
3727        }
3728
3729        StatusBarNotification ForListener(ManagedServiceInfo info) {
3730            if (mListeners.getOnNotificationPostedTrim(info) == TRIM_LIGHT) {
3731                if (sbnCloneLight == null) {
3732                    sbnCloneLight = heavy.cloneLight();
3733                }
3734                return sbnCloneLight;
3735            } else {
3736                if (sbnClone == null) {
3737                    sbnClone = heavy.clone();
3738                }
3739                return sbnClone;
3740            }
3741        }
3742    }
3743
3744    public class NotificationRankers extends ManagedServices {
3745
3746        public NotificationRankers() {
3747            super(getContext(), mHandler, mNotificationList, mUserProfiles);
3748        }
3749
3750        @Override
3751        protected Config getConfig() {
3752            Config c = new Config();
3753            c.caption = "notification ranker service";
3754            c.serviceInterface = NotificationRankerService.SERVICE_INTERFACE;
3755            c.secureSettingName = null;
3756            c.bindPermission = Manifest.permission.BIND_NOTIFICATION_RANKER_SERVICE;
3757            c.settingsAction = Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS;
3758            c.clientLabel = R.string.notification_ranker_binding_label;
3759            return c;
3760        }
3761
3762        @Override
3763        protected IInterface asInterface(IBinder binder) {
3764            return INotificationListener.Stub.asInterface(binder);
3765        }
3766
3767        @Override
3768        protected boolean checkType(IInterface service) {
3769            return service instanceof INotificationListener;
3770        }
3771
3772        @Override
3773        protected void onServiceAdded(ManagedServiceInfo info) {
3774            mListeners.registerGuestService(info);
3775        }
3776
3777        @Override
3778        protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
3779            mListeners.unregisterService(removed.service, removed.userid);
3780        }
3781
3782        public void onNotificationEnqueued(final NotificationRecord r) {
3783            final StatusBarNotification sbn = r.sbn;
3784            TrimCache trimCache = new TrimCache(sbn);
3785
3786            // mServices is the list inside ManagedServices of all the rankers,
3787            // There should be only one, but it's a list, so while we enforce
3788            // singularity elsewhere, we keep it general here, to avoid surprises.
3789            for (final ManagedServiceInfo info : NotificationRankers.this.mServices) {
3790                boolean sbnVisible = isVisibleToListener(sbn, info);
3791                if (!sbnVisible) {
3792                    continue;
3793                }
3794
3795                final int importance = r.getImportance();
3796                final boolean fromUser = r.isImportanceFromUser();
3797                final StatusBarNotification sbnToPost =  trimCache.ForListener(info);
3798                mHandler.post(new Runnable() {
3799                    @Override
3800                    public void run() {
3801                        notifyEnqueued(info, sbnToPost, importance, fromUser);
3802                    }
3803                });
3804            }
3805        }
3806
3807        private void notifyEnqueued(final ManagedServiceInfo info,
3808                final StatusBarNotification sbn, int importance, boolean fromUser) {
3809            final INotificationListener ranker = (INotificationListener) info.service;
3810            StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
3811            try {
3812                ranker.onNotificationEnqueued(sbnHolder, importance, fromUser);
3813            } catch (RemoteException ex) {
3814                Log.e(TAG, "unable to notify ranker (enqueued): " + ranker, ex);
3815            }
3816        }
3817
3818        public boolean isEnabled() {
3819            return !mServices.isEmpty();
3820        }
3821
3822        @Override
3823        public void onUserSwitched(int user) {
3824            for (ManagedServiceInfo info : mServices) {
3825                unregisterService(info.service, info.userid);
3826            }
3827            registerRanker();
3828        }
3829
3830        @Override
3831        public void onPackagesChanged(boolean queryReplace, String[] pkgList) {
3832            if (DEBUG) Slog.d(TAG, "onPackagesChanged queryReplace=" + queryReplace
3833                    + " pkgList=" + (pkgList == null ? null : Arrays.asList(pkgList)));
3834            if (mRankerServicePackageName == null) {
3835                return;
3836            }
3837
3838            if (pkgList != null && (pkgList.length > 0)) {
3839                for (String pkgName : pkgList) {
3840                    if (mRankerServicePackageName.equals(pkgName)) {
3841                        registerRanker();
3842                    }
3843                }
3844            }
3845        }
3846
3847        protected void registerRanker() {
3848            // Find the updatable ranker and register it.
3849            if (mRankerServicePackageName == null) {
3850                Slog.w(TAG, "could not start ranker service: no package specified!");
3851                return;
3852            }
3853            Set<ComponentName> rankerComponents = queryPackageForServices(
3854                    mRankerServicePackageName, UserHandle.USER_SYSTEM, null);
3855            Iterator<ComponentName> iterator = rankerComponents.iterator();
3856            if (iterator.hasNext()) {
3857                ComponentName rankerComponent = iterator.next();
3858                if (iterator.hasNext()) {
3859                    Slog.e(TAG, "found multiple ranker services:" + rankerComponents);
3860                } else {
3861                    registerSystemService(rankerComponent, UserHandle.USER_SYSTEM);
3862                }
3863            } else {
3864                Slog.w(TAG, "could not start ranker service: none found");
3865            }
3866        }
3867    }
3868
3869    public class NotificationListeners extends ManagedServices {
3870
3871        private final ArraySet<ManagedServiceInfo> mLightTrimListeners = new ArraySet<>();
3872        private boolean mNotificationGroupsDesired;
3873
3874        public NotificationListeners() {
3875            super(getContext(), mHandler, mNotificationList, mUserProfiles);
3876        }
3877
3878        @Override
3879        protected Config getConfig() {
3880            Config c = new Config();
3881            c.caption = "notification listener";
3882            c.serviceInterface = NotificationListenerService.SERVICE_INTERFACE;
3883            c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_LISTENERS;
3884            c.bindPermission = android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE;
3885            c.settingsAction = Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS;
3886            c.clientLabel = R.string.notification_listener_binding_label;
3887            return c;
3888        }
3889
3890        @Override
3891        protected IInterface asInterface(IBinder binder) {
3892            return INotificationListener.Stub.asInterface(binder);
3893        }
3894
3895        @Override
3896        protected boolean checkType(IInterface service) {
3897            return service instanceof INotificationListener;
3898        }
3899
3900        @Override
3901        public void onServiceAdded(ManagedServiceInfo info) {
3902            final INotificationListener listener = (INotificationListener) info.service;
3903            final NotificationRankingUpdate update;
3904            synchronized (mNotificationList) {
3905                updateNotificationGroupsDesiredLocked();
3906                update = makeRankingUpdateLocked(info);
3907            }
3908            try {
3909                listener.onListenerConnected(update);
3910            } catch (RemoteException e) {
3911                // we tried
3912            }
3913        }
3914
3915        @Override
3916        protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
3917            if (removeDisabledHints(removed)) {
3918                updateListenerHintsLocked();
3919                updateEffectsSuppressorLocked();
3920            }
3921            mLightTrimListeners.remove(removed);
3922            updateNotificationGroupsDesiredLocked();
3923        }
3924
3925        public void setOnNotificationPostedTrimLocked(ManagedServiceInfo info, int trim) {
3926            if (trim == TRIM_LIGHT) {
3927                mLightTrimListeners.add(info);
3928            } else {
3929                mLightTrimListeners.remove(info);
3930            }
3931        }
3932
3933        public int getOnNotificationPostedTrim(ManagedServiceInfo info) {
3934            return mLightTrimListeners.contains(info) ? TRIM_LIGHT : TRIM_FULL;
3935        }
3936
3937        /**
3938         * asynchronously notify all listeners about a new notification
3939         *
3940         * <p>
3941         * Also takes care of removing a notification that has been visible to a listener before,
3942         * but isn't anymore.
3943         */
3944        public void notifyPostedLocked(StatusBarNotification sbn, StatusBarNotification oldSbn) {
3945            // Lazily initialized snapshots of the notification.
3946            TrimCache trimCache = new TrimCache(sbn);
3947
3948            for (final ManagedServiceInfo info : mServices) {
3949                boolean sbnVisible = isVisibleToListener(sbn, info);
3950                boolean oldSbnVisible = oldSbn != null ? isVisibleToListener(oldSbn, info) : false;
3951                // This notification hasn't been and still isn't visible -> ignore.
3952                if (!oldSbnVisible && !sbnVisible) {
3953                    continue;
3954                }
3955                final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
3956
3957                // This notification became invisible -> remove the old one.
3958                if (oldSbnVisible && !sbnVisible) {
3959                    final StatusBarNotification oldSbnLightClone = oldSbn.cloneLight();
3960                    mHandler.post(new Runnable() {
3961                        @Override
3962                        public void run() {
3963                            notifyRemoved(info, oldSbnLightClone, update);
3964                        }
3965                    });
3966                    continue;
3967                }
3968
3969                final StatusBarNotification sbnToPost =  trimCache.ForListener(info);
3970                mHandler.post(new Runnable() {
3971                    @Override
3972                    public void run() {
3973                        notifyPosted(info, sbnToPost, update);
3974                    }
3975                });
3976            }
3977        }
3978
3979        /**
3980         * asynchronously notify all listeners about a removed notification
3981         */
3982        public void notifyRemovedLocked(StatusBarNotification sbn) {
3983            // make a copy in case changes are made to the underlying Notification object
3984            // NOTE: this copy is lightweight: it doesn't include heavyweight parts of the
3985            // notification
3986            final StatusBarNotification sbnLight = sbn.cloneLight();
3987            for (final ManagedServiceInfo info : mServices) {
3988                if (!isVisibleToListener(sbn, info)) {
3989                    continue;
3990                }
3991                final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
3992                mHandler.post(new Runnable() {
3993                    @Override
3994                    public void run() {
3995                        notifyRemoved(info, sbnLight, update);
3996                    }
3997                });
3998            }
3999        }
4000
4001        /**
4002         * asynchronously notify all listeners about a reordering of notifications
4003         */
4004        public void notifyRankingUpdateLocked() {
4005            for (final ManagedServiceInfo serviceInfo : mServices) {
4006                if (!serviceInfo.isEnabledForCurrentProfiles()) {
4007                    continue;
4008                }
4009                final NotificationRankingUpdate update = makeRankingUpdateLocked(serviceInfo);
4010                mHandler.post(new Runnable() {
4011                    @Override
4012                    public void run() {
4013                        notifyRankingUpdate(serviceInfo, update);
4014                    }
4015                });
4016            }
4017        }
4018
4019        public void notifyListenerHintsChangedLocked(final int hints) {
4020            for (final ManagedServiceInfo serviceInfo : mServices) {
4021                if (!serviceInfo.isEnabledForCurrentProfiles()) {
4022                    continue;
4023                }
4024                mHandler.post(new Runnable() {
4025                    @Override
4026                    public void run() {
4027                        notifyListenerHintsChanged(serviceInfo, hints);
4028                    }
4029                });
4030            }
4031        }
4032
4033        public void notifyInterruptionFilterChanged(final int interruptionFilter) {
4034            for (final ManagedServiceInfo serviceInfo : mServices) {
4035                if (!serviceInfo.isEnabledForCurrentProfiles()) {
4036                    continue;
4037                }
4038                mHandler.post(new Runnable() {
4039                    @Override
4040                    public void run() {
4041                        notifyInterruptionFilterChanged(serviceInfo, interruptionFilter);
4042                    }
4043                });
4044            }
4045        }
4046
4047        private void notifyPosted(final ManagedServiceInfo info,
4048                final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) {
4049            final INotificationListener listener = (INotificationListener)info.service;
4050            StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
4051            try {
4052                listener.onNotificationPosted(sbnHolder, rankingUpdate);
4053            } catch (RemoteException ex) {
4054                Log.e(TAG, "unable to notify listener (posted): " + listener, ex);
4055            }
4056        }
4057
4058        private void notifyRemoved(ManagedServiceInfo info, StatusBarNotification sbn,
4059                NotificationRankingUpdate rankingUpdate) {
4060            if (!info.enabledAndUserMatches(sbn.getUserId())) {
4061                return;
4062            }
4063            final INotificationListener listener = (INotificationListener) info.service;
4064            StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
4065            try {
4066                listener.onNotificationRemoved(sbnHolder, rankingUpdate);
4067            } catch (RemoteException ex) {
4068                Log.e(TAG, "unable to notify listener (removed): " + listener, ex);
4069            }
4070        }
4071
4072        private void notifyRankingUpdate(ManagedServiceInfo info,
4073                                         NotificationRankingUpdate rankingUpdate) {
4074            final INotificationListener listener = (INotificationListener) info.service;
4075            try {
4076                listener.onNotificationRankingUpdate(rankingUpdate);
4077            } catch (RemoteException ex) {
4078                Log.e(TAG, "unable to notify listener (ranking update): " + listener, ex);
4079            }
4080        }
4081
4082        private void notifyListenerHintsChanged(ManagedServiceInfo info, int hints) {
4083            final INotificationListener listener = (INotificationListener) info.service;
4084            try {
4085                listener.onListenerHintsChanged(hints);
4086            } catch (RemoteException ex) {
4087                Log.e(TAG, "unable to notify listener (listener hints): " + listener, ex);
4088            }
4089        }
4090
4091        private void notifyInterruptionFilterChanged(ManagedServiceInfo info,
4092                int interruptionFilter) {
4093            final INotificationListener listener = (INotificationListener) info.service;
4094            try {
4095                listener.onInterruptionFilterChanged(interruptionFilter);
4096            } catch (RemoteException ex) {
4097                Log.e(TAG, "unable to notify listener (interruption filter): " + listener, ex);
4098            }
4099        }
4100
4101        private boolean isListenerPackage(String packageName) {
4102            if (packageName == null) {
4103                return false;
4104            }
4105            // TODO: clean up locking object later
4106            synchronized (mNotificationList) {
4107                for (final ManagedServiceInfo serviceInfo : mServices) {
4108                    if (packageName.equals(serviceInfo.component.getPackageName())) {
4109                        return true;
4110                    }
4111                }
4112            }
4113            return false;
4114        }
4115
4116        /**
4117         * Returns whether any of the currently registered listeners wants to receive notification
4118         * groups.
4119         *
4120         * <p>Currently we assume groups are desired by non-SystemUI listeners.</p>
4121         */
4122        public boolean notificationGroupsDesired() {
4123            return mNotificationGroupsDesired;
4124        }
4125
4126        private void updateNotificationGroupsDesiredLocked() {
4127            mNotificationGroupsDesired = true;
4128            // No listeners, no groups.
4129            if (mServices.isEmpty()) {
4130                mNotificationGroupsDesired = false;
4131                return;
4132            }
4133            // One listener: Check whether it's SysUI.
4134            if (mServices.size() == 1 &&
4135                    mServices.get(0).component.getPackageName().equals("com.android.systemui")) {
4136                mNotificationGroupsDesired = false;
4137                return;
4138            }
4139        }
4140    }
4141
4142    public static final class DumpFilter {
4143        public boolean filtered = false;
4144        public String pkgFilter;
4145        public boolean zen;
4146        public long since;
4147        public boolean stats;
4148        public boolean redact = true;
4149
4150        public static DumpFilter parseFromArguments(String[] args) {
4151            final DumpFilter filter = new DumpFilter();
4152            for (int ai = 0; ai < args.length; ai++) {
4153                final String a = args[ai];
4154                if ("--noredact".equals(a) || "--reveal".equals(a)) {
4155                    filter.redact = false;
4156                } else if ("p".equals(a) || "pkg".equals(a) || "--package".equals(a)) {
4157                    if (ai < args.length-1) {
4158                        ai++;
4159                        filter.pkgFilter = args[ai].trim().toLowerCase();
4160                        if (filter.pkgFilter.isEmpty()) {
4161                            filter.pkgFilter = null;
4162                        } else {
4163                            filter.filtered = true;
4164                        }
4165                    }
4166                } else if ("--zen".equals(a) || "zen".equals(a)) {
4167                    filter.filtered = true;
4168                    filter.zen = true;
4169                } else if ("--stats".equals(a)) {
4170                    filter.stats = true;
4171                    if (ai < args.length-1) {
4172                        ai++;
4173                        filter.since = Long.valueOf(args[ai]);
4174                    } else {
4175                        filter.since = 0;
4176                    }
4177                }
4178            }
4179            return filter;
4180        }
4181
4182        public boolean matches(StatusBarNotification sbn) {
4183            if (!filtered) return true;
4184            return zen ? true : sbn != null
4185                    && (matches(sbn.getPackageName()) || matches(sbn.getOpPkg()));
4186        }
4187
4188        public boolean matches(ComponentName component) {
4189            if (!filtered) return true;
4190            return zen ? true : component != null && matches(component.getPackageName());
4191        }
4192
4193        public boolean matches(String pkg) {
4194            if (!filtered) return true;
4195            return zen ? true : pkg != null && pkg.toLowerCase().contains(pkgFilter);
4196        }
4197
4198        @Override
4199        public String toString() {
4200            return stats ? "stats" : zen ? "zen" : ('\'' + pkgFilter + '\'');
4201        }
4202    }
4203
4204    /**
4205     * Wrapper for a StatusBarNotification object that allows transfer across a oneway
4206     * binder without sending large amounts of data over a oneway transaction.
4207     */
4208    private static final class StatusBarNotificationHolder
4209            extends IStatusBarNotificationHolder.Stub {
4210        private StatusBarNotification mValue;
4211
4212        public StatusBarNotificationHolder(StatusBarNotification value) {
4213            mValue = value;
4214        }
4215
4216        /** Get the held value and clear it. This function should only be called once per holder */
4217        @Override
4218        public StatusBarNotification get() {
4219            StatusBarNotification value = mValue;
4220            mValue = null;
4221            return value;
4222        }
4223    }
4224
4225    private final class PolicyAccess {
4226        private static final String SEPARATOR = ":";
4227        private final String[] PERM = {
4228            android.Manifest.permission.ACCESS_NOTIFICATION_POLICY
4229        };
4230
4231        public boolean isPackageGranted(String pkg) {
4232            return pkg != null && getGrantedPackages().contains(pkg);
4233        }
4234
4235        public void put(String pkg, boolean granted) {
4236            if (pkg == null) return;
4237            final ArraySet<String> pkgs = getGrantedPackages();
4238            boolean changed;
4239            if (granted) {
4240                changed = pkgs.add(pkg);
4241            } else {
4242                changed = pkgs.remove(pkg);
4243            }
4244            if (!changed) return;
4245            final String setting = TextUtils.join(SEPARATOR, pkgs);
4246            final int currentUser = ActivityManager.getCurrentUser();
4247            Settings.Secure.putStringForUser(getContext().getContentResolver(),
4248                    Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES,
4249                    setting,
4250                    currentUser);
4251            getContext().sendBroadcastAsUser(new Intent(NotificationManager
4252                    .ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED)
4253                .setPackage(pkg)
4254                .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), new UserHandle(currentUser), null);
4255        }
4256
4257        public ArraySet<String> getGrantedPackages() {
4258            final ArraySet<String> pkgs = new ArraySet<>();
4259
4260            long identity = Binder.clearCallingIdentity();
4261            try {
4262                final String setting = Settings.Secure.getStringForUser(
4263                        getContext().getContentResolver(),
4264                        Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES,
4265                        ActivityManager.getCurrentUser());
4266                if (setting != null) {
4267                    final String[] tokens = setting.split(SEPARATOR);
4268                    for (int i = 0; i < tokens.length; i++) {
4269                        String token = tokens[i];
4270                        if (token != null) {
4271                            token = token.trim();
4272                        }
4273                        if (TextUtils.isEmpty(token)) {
4274                            continue;
4275                        }
4276                        pkgs.add(token);
4277                    }
4278                }
4279            } finally {
4280                Binder.restoreCallingIdentity(identity);
4281            }
4282            return pkgs;
4283        }
4284
4285        public String[] getRequestingPackages() throws RemoteException {
4286            final ParceledListSlice list = AppGlobals.getPackageManager()
4287                    .getPackagesHoldingPermissions(PERM, 0 /*flags*/,
4288                            ActivityManager.getCurrentUser());
4289            final List<PackageInfo> pkgs = list.getList();
4290            if (pkgs == null || pkgs.isEmpty()) return new String[0];
4291            final int N = pkgs.size();
4292            final String[] rt = new String[N];
4293            for (int i = 0; i < N; i++) {
4294                rt[i] = pkgs.get(i).packageName;
4295            }
4296            return rt;
4297        }
4298    }
4299}
4300