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