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