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