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