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