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