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