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