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