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