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