NotificationManagerService.java revision 6054e614769f03e29ba08475abf01982ace3d6a1
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    private String mSoundNotificationKey;
194    private String mVibrateNotificationKey;
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    // The last key in this list owns the hardware.
214    ArrayList<String> mLights = new ArrayList<>();
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                mSoundNotificationKey = 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                mVibrateNotificationKey = null;
586                identity = Binder.clearCallingIdentity();
587                try {
588                    mVibrator.cancel();
589                } finally {
590                    Binder.restoreCallingIdentity(identity);
591                }
592
593                // light
594                mLights.clear();
595                updateLightsLocked();
596            }
597        }
598
599        @Override
600        public void onPanelHidden() {
601            EventLogTags.writeNotificationPanelHidden();
602        }
603
604        @Override
605        public void onNotificationError(int callingUid, int callingPid, String pkg, String tag, int id,
606                int uid, int initialPid, String message, int userId) {
607            Slog.d(TAG, "onNotification error pkg=" + pkg + " tag=" + tag + " id=" + id
608                    + "; will crashApplication(uid=" + uid + ", pid=" + initialPid + ")");
609            cancelNotification(callingUid, callingPid, pkg, tag, id, 0, 0, false, userId,
610                    REASON_DELEGATE_ERROR, null);
611            long ident = Binder.clearCallingIdentity();
612            try {
613                ActivityManagerNative.getDefault().crashApplication(uid, initialPid, pkg,
614                        "Bad notification posted from package " + pkg
615                        + ": " + message);
616            } catch (RemoteException e) {
617            }
618            Binder.restoreCallingIdentity(ident);
619        }
620
621        @Override
622        public void onNotificationVisibilityChanged(
623                String[] newlyVisibleKeys, String[] noLongerVisibleKeys) {
624            // Using ';' as separator since eventlogs uses ',' to separate
625            // args.
626            EventLogTags.writeNotificationVisibilityChanged(
627                    TextUtils.join(";", newlyVisibleKeys),
628                    TextUtils.join(";", noLongerVisibleKeys));
629            synchronized (mNotificationList) {
630                for (String key : newlyVisibleKeys) {
631                    NotificationRecord r = mNotificationsByKey.get(key);
632                    if (r == null) continue;
633                    r.stats.onVisibilityChanged(true);
634                }
635                // Note that we might receive this event after notifications
636                // have already left the system, e.g. after dismissing from the
637                // shade. Hence not finding notifications in
638                // mNotificationsByKey is not an exceptional condition.
639                for (String key : noLongerVisibleKeys) {
640                    NotificationRecord r = mNotificationsByKey.get(key);
641                    if (r == null) continue;
642                    r.stats.onVisibilityChanged(false);
643                }
644            }
645        }
646
647        @Override
648        public void onNotificationExpansionChanged(String key,
649                boolean userAction, boolean expanded) {
650            EventLogTags.writeNotificationExpansion(key, userAction ? 1 : 0, expanded ? 1 : 0);
651            synchronized (mNotificationList) {
652                NotificationRecord r = mNotificationsByKey.get(key);
653                if (r != null) {
654                    r.stats.onExpansionChanged(userAction, expanded);
655                }
656            }
657        }
658    };
659
660    private final BroadcastReceiver mPackageIntentReceiver = new BroadcastReceiver() {
661        @Override
662        public void onReceive(Context context, Intent intent) {
663            String action = intent.getAction();
664
665            boolean queryRestart = false;
666            boolean queryRemove = false;
667            boolean packageChanged = false;
668            boolean cancelNotifications = true;
669
670            if (action.equals(Intent.ACTION_PACKAGE_ADDED)
671                    || (queryRemove=action.equals(Intent.ACTION_PACKAGE_REMOVED))
672                    || action.equals(Intent.ACTION_PACKAGE_RESTARTED)
673                    || (packageChanged=action.equals(Intent.ACTION_PACKAGE_CHANGED))
674                    || (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART))
675                    || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
676                int changeUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
677                        UserHandle.USER_ALL);
678                String pkgList[] = null;
679                boolean queryReplace = queryRemove &&
680                        intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
681                if (DBG) Slog.i(TAG, "action=" + action + " queryReplace=" + queryReplace);
682                if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
683                    pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
684                } else if (queryRestart) {
685                    pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
686                } else {
687                    Uri uri = intent.getData();
688                    if (uri == null) {
689                        return;
690                    }
691                    String pkgName = uri.getSchemeSpecificPart();
692                    if (pkgName == null) {
693                        return;
694                    }
695                    if (packageChanged) {
696                        // We cancel notifications for packages which have just been disabled
697                        try {
698                            final IPackageManager pm = AppGlobals.getPackageManager();
699                            final int enabled = pm.getApplicationEnabledSetting(pkgName,
700                                    changeUserId != UserHandle.USER_ALL ? changeUserId :
701                                    UserHandle.USER_OWNER);
702                            if (enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED
703                                    || enabled == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
704                                cancelNotifications = false;
705                            }
706                        } catch (IllegalArgumentException e) {
707                            // Package doesn't exist; probably racing with uninstall.
708                            // cancelNotifications is already true, so nothing to do here.
709                            if (DBG) {
710                                Slog.i(TAG, "Exception trying to look up app enabled setting", e);
711                            }
712                        } catch (RemoteException e) {
713                            // Failed to talk to PackageManagerService Should never happen!
714                        }
715                    }
716                    pkgList = new String[]{pkgName};
717                }
718
719                if (pkgList != null && (pkgList.length > 0)) {
720                    for (String pkgName : pkgList) {
721                        if (cancelNotifications) {
722                            cancelAllNotificationsInt(MY_UID, MY_PID, pkgName, 0, 0, !queryRestart,
723                                    changeUserId, REASON_PACKAGE_CHANGED, null);
724                        }
725                    }
726                }
727                mListeners.onPackagesChanged(queryReplace, pkgList);
728                mConditionProviders.onPackagesChanged(queryReplace, pkgList);
729            }
730        }
731    };
732
733    private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
734        @Override
735        public void onReceive(Context context, Intent intent) {
736            String action = intent.getAction();
737
738            if (action.equals(Intent.ACTION_SCREEN_ON)) {
739                // Keep track of screen on/off state, but do not turn off the notification light
740                // until user passes through the lock screen or views the notification.
741                mScreenOn = true;
742            } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
743                mScreenOn = false;
744            } else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) {
745                mInCall = TelephonyManager.EXTRA_STATE_OFFHOOK
746                        .equals(intent.getStringExtra(TelephonyManager.EXTRA_STATE));
747                updateNotificationPulse();
748            } else if (action.equals(Intent.ACTION_USER_STOPPED)) {
749                int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
750                if (userHandle >= 0) {
751                    cancelAllNotificationsInt(MY_UID, MY_PID, null, 0, 0, true, userHandle,
752                            REASON_USER_STOPPED, null);
753                }
754            } else if (action.equals(Intent.ACTION_USER_PRESENT)) {
755                // turn off LED when user passes through lock screen
756                mNotificationLight.turnOff();
757                mStatusBar.notificationLightOff();
758            } else if (action.equals(Intent.ACTION_USER_SWITCHED)) {
759                // reload per-user settings
760                mSettingsObserver.update(null);
761                mUserProfiles.updateCache(context);
762                // Refresh managed services
763                mConditionProviders.onUserSwitched();
764                mListeners.onUserSwitched();
765            } else if (action.equals(Intent.ACTION_USER_ADDED)) {
766                mUserProfiles.updateCache(context);
767            }
768        }
769    };
770
771    class SettingsObserver extends ContentObserver {
772        private final Uri NOTIFICATION_LIGHT_PULSE_URI
773                = Settings.System.getUriFor(Settings.System.NOTIFICATION_LIGHT_PULSE);
774
775        SettingsObserver(Handler handler) {
776            super(handler);
777        }
778
779        void observe() {
780            ContentResolver resolver = getContext().getContentResolver();
781            resolver.registerContentObserver(NOTIFICATION_LIGHT_PULSE_URI,
782                    false, this, UserHandle.USER_ALL);
783            update(null);
784        }
785
786        @Override public void onChange(boolean selfChange, Uri uri) {
787            update(uri);
788        }
789
790        public void update(Uri uri) {
791            ContentResolver resolver = getContext().getContentResolver();
792            if (uri == null || NOTIFICATION_LIGHT_PULSE_URI.equals(uri)) {
793                boolean pulseEnabled = Settings.System.getInt(resolver,
794                            Settings.System.NOTIFICATION_LIGHT_PULSE, 0) != 0;
795                if (mNotificationPulseEnabled != pulseEnabled) {
796                    mNotificationPulseEnabled = pulseEnabled;
797                    updateNotificationPulse();
798                }
799            }
800        }
801    }
802
803    private SettingsObserver mSettingsObserver;
804    private ZenModeHelper mZenModeHelper;
805
806    private final Runnable mBuzzBeepBlinked = new Runnable() {
807        @Override
808        public void run() {
809            mStatusBar.buzzBeepBlinked();
810        }
811    };
812
813    static long[] getLongArray(Resources r, int resid, int maxlen, long[] def) {
814        int[] ar = r.getIntArray(resid);
815        if (ar == null) {
816            return def;
817        }
818        final int len = ar.length > maxlen ? maxlen : ar.length;
819        long[] out = new long[len];
820        for (int i=0; i<len; i++) {
821            out[i] = ar[i];
822        }
823        return out;
824    }
825
826    public NotificationManagerService(Context context) {
827        super(context);
828    }
829
830    @Override
831    public void onStart() {
832        Resources resources = getContext().getResources();
833
834        mAm = ActivityManagerNative.getDefault();
835        mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
836        mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
837
838        mHandler = new WorkerHandler();
839        mRankingThread.start();
840        String[] extractorNames;
841        try {
842            extractorNames = resources.getStringArray(R.array.config_notificationSignalExtractors);
843        } catch (Resources.NotFoundException e) {
844            extractorNames = new String[0];
845        }
846        mRankingHelper = new RankingHelper(getContext(),
847                new RankingWorkerHandler(mRankingThread.getLooper()),
848                extractorNames);
849        mZenModeHelper = new ZenModeHelper(getContext(), mHandler.getLooper());
850        mZenModeHelper.addCallback(new ZenModeHelper.Callback() {
851            @Override
852            public void onConfigChanged() {
853                savePolicyFile();
854            }
855
856            @Override
857            void onZenModeChanged() {
858                synchronized(mNotificationList) {
859                    updateInterruptionFilterLocked();
860                }
861            }
862        });
863        final File systemDir = new File(Environment.getDataDirectory(), "system");
864        mPolicyFile = new AtomicFile(new File(systemDir, "notification_policy.xml"));
865        mUsageStats = new NotificationUsageStats(getContext());
866
867        importOldBlockDb();
868
869        mListeners = new NotificationListeners();
870        mConditionProviders = new ConditionProviders(getContext(),
871                mHandler, mUserProfiles, mZenModeHelper);
872        mStatusBar = getLocalService(StatusBarManagerInternal.class);
873        mStatusBar.setNotificationDelegate(mNotificationDelegate);
874
875        final LightsManager lights = getLocalService(LightsManager.class);
876        mNotificationLight = lights.getLight(LightsManager.LIGHT_ID_NOTIFICATIONS);
877        mAttentionLight = lights.getLight(LightsManager.LIGHT_ID_ATTENTION);
878
879        mDefaultNotificationColor = resources.getColor(
880                R.color.config_defaultNotificationColor);
881        mDefaultNotificationLedOn = resources.getInteger(
882                R.integer.config_defaultNotificationLedOn);
883        mDefaultNotificationLedOff = resources.getInteger(
884                R.integer.config_defaultNotificationLedOff);
885
886        mDefaultVibrationPattern = getLongArray(resources,
887                R.array.config_defaultNotificationVibePattern,
888                VIBRATE_PATTERN_MAXLEN,
889                DEFAULT_VIBRATE_PATTERN);
890
891        mFallbackVibrationPattern = getLongArray(resources,
892                R.array.config_notificationFallbackVibePattern,
893                VIBRATE_PATTERN_MAXLEN,
894                DEFAULT_VIBRATE_PATTERN);
895
896        mUseAttentionLight = resources.getBoolean(R.bool.config_useAttentionLight);
897
898        // Don't start allowing notifications until the setup wizard has run once.
899        // After that, including subsequent boots, init with notifications turned on.
900        // This works on the first boot because the setup wizard will toggle this
901        // flag at least once and we'll go back to 0 after that.
902        if (0 == Settings.Global.getInt(getContext().getContentResolver(),
903                    Settings.Global.DEVICE_PROVISIONED, 0)) {
904            mDisableNotificationEffects = true;
905        }
906        mZenModeHelper.updateZenMode();
907
908        mUserProfiles.updateCache(getContext());
909        listenForCallState();
910
911        // register for various Intents
912        IntentFilter filter = new IntentFilter();
913        filter.addAction(Intent.ACTION_SCREEN_ON);
914        filter.addAction(Intent.ACTION_SCREEN_OFF);
915        filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
916        filter.addAction(Intent.ACTION_USER_PRESENT);
917        filter.addAction(Intent.ACTION_USER_STOPPED);
918        filter.addAction(Intent.ACTION_USER_SWITCHED);
919        filter.addAction(Intent.ACTION_USER_ADDED);
920        getContext().registerReceiver(mIntentReceiver, filter);
921
922        IntentFilter pkgFilter = new IntentFilter();
923        pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
924        pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
925        pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
926        pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
927        pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
928        pkgFilter.addDataScheme("package");
929        getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, pkgFilter, null,
930                null);
931
932        IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
933        getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, sdFilter, null,
934                null);
935
936        mSettingsObserver = new SettingsObserver(mHandler);
937
938        mArchive = new Archive(resources.getInteger(
939                R.integer.config_notificationServiceArchiveSize));
940
941        publishBinderService(Context.NOTIFICATION_SERVICE, mService);
942        publishLocalService(NotificationManagerInternal.class, mInternalService);
943    }
944
945    /**
946     * Read the old XML-based app block database and import those blockages into the AppOps system.
947     */
948    private void importOldBlockDb() {
949        loadPolicyFile();
950
951        PackageManager pm = getContext().getPackageManager();
952        for (String pkg : mBlockedPackages) {
953            PackageInfo info = null;
954            try {
955                info = pm.getPackageInfo(pkg, 0);
956                setNotificationsEnabledForPackageImpl(pkg, info.applicationInfo.uid, false);
957            } catch (NameNotFoundException e) {
958                // forget you
959            }
960        }
961        mBlockedPackages.clear();
962    }
963
964    @Override
965    public void onBootPhase(int phase) {
966        if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
967            // no beeping until we're basically done booting
968            mSystemReady = true;
969
970            // Grab our optional AudioService
971            mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
972            mZenModeHelper.onSystemReady();
973        } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
974            // This observer will force an update when observe is called, causing us to
975            // bind to listener services.
976            mSettingsObserver.observe();
977            mListeners.onBootPhaseAppsCanStart();
978            mConditionProviders.onBootPhaseAppsCanStart();
979        }
980    }
981
982    void setNotificationsEnabledForPackageImpl(String pkg, int uid, boolean enabled) {
983        Slog.v(TAG, (enabled?"en":"dis") + "abling notifications for " + pkg);
984
985        mAppOps.setMode(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg,
986                enabled ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED);
987
988        // Now, cancel any outstanding notifications that are part of a just-disabled app
989        if (ENABLE_BLOCKED_NOTIFICATIONS && !enabled) {
990            cancelAllNotificationsInt(MY_UID, MY_PID, pkg, 0, 0, true, UserHandle.getUserId(uid),
991                    REASON_PACKAGE_BANNED, null);
992        }
993    }
994
995    private void updateListenerHintsLocked() {
996        final int hints = mListenersDisablingEffects.isEmpty() ? 0 : HINT_HOST_DISABLE_EFFECTS;
997        if (hints == mListenerHints) return;
998        mListenerHints = hints;
999        scheduleListenerHintsChanged(hints);
1000    }
1001
1002    private void updateEffectsSuppressorLocked() {
1003        final ComponentName suppressor = !mListenersDisablingEffects.isEmpty()
1004                ? mListenersDisablingEffects.valueAt(0).component : null;
1005        if (Objects.equals(suppressor, mEffectsSuppressor)) return;
1006        mEffectsSuppressor = suppressor;
1007        getContext().sendBroadcast(new Intent(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED)
1008                .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY));
1009    }
1010
1011    private void updateInterruptionFilterLocked() {
1012        int interruptionFilter = mZenModeHelper.getZenModeListenerInterruptionFilter();
1013        if (interruptionFilter == mInterruptionFilter) return;
1014        mInterruptionFilter = interruptionFilter;
1015        scheduleInterruptionFilterChanged(interruptionFilter);
1016    }
1017
1018    private final IBinder mService = new INotificationManager.Stub() {
1019        // Toasts
1020        // ============================================================================
1021
1022        @Override
1023        public void enqueueToast(String pkg, ITransientNotification callback, int duration)
1024        {
1025            if (DBG) {
1026                Slog.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback
1027                        + " duration=" + duration);
1028            }
1029
1030            if (pkg == null || callback == null) {
1031                Slog.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback);
1032                return ;
1033            }
1034
1035            final boolean isSystemToast = isCallerSystem() || ("android".equals(pkg));
1036
1037            if (ENABLE_BLOCKED_TOASTS && !noteNotificationOp(pkg, Binder.getCallingUid())) {
1038                if (!isSystemToast) {
1039                    Slog.e(TAG, "Suppressing toast from package " + pkg + " by user request.");
1040                    return;
1041                }
1042            }
1043
1044            synchronized (mToastQueue) {
1045                int callingPid = Binder.getCallingPid();
1046                long callingId = Binder.clearCallingIdentity();
1047                try {
1048                    ToastRecord record;
1049                    int index = indexOfToastLocked(pkg, callback);
1050                    // If it's already in the queue, we update it in place, we don't
1051                    // move it to the end of the queue.
1052                    if (index >= 0) {
1053                        record = mToastQueue.get(index);
1054                        record.update(duration);
1055                    } else {
1056                        // Limit the number of toasts that any given package except the android
1057                        // package can enqueue.  Prevents DOS attacks and deals with leaks.
1058                        if (!isSystemToast) {
1059                            int count = 0;
1060                            final int N = mToastQueue.size();
1061                            for (int i=0; i<N; i++) {
1062                                 final ToastRecord r = mToastQueue.get(i);
1063                                 if (r.pkg.equals(pkg)) {
1064                                     count++;
1065                                     if (count >= MAX_PACKAGE_NOTIFICATIONS) {
1066                                         Slog.e(TAG, "Package has already posted " + count
1067                                                + " toasts. Not showing more. Package=" + pkg);
1068                                         return;
1069                                     }
1070                                 }
1071                            }
1072                        }
1073
1074                        record = new ToastRecord(callingPid, pkg, callback, duration);
1075                        mToastQueue.add(record);
1076                        index = mToastQueue.size() - 1;
1077                        keepProcessAliveLocked(callingPid);
1078                    }
1079                    // If it's at index 0, it's the current toast.  It doesn't matter if it's
1080                    // new or just been updated.  Call back and tell it to show itself.
1081                    // If the callback fails, this will remove it from the list, so don't
1082                    // assume that it's valid after this.
1083                    if (index == 0) {
1084                        showNextToastLocked();
1085                    }
1086                } finally {
1087                    Binder.restoreCallingIdentity(callingId);
1088                }
1089            }
1090        }
1091
1092        @Override
1093        public void cancelToast(String pkg, ITransientNotification callback) {
1094            Slog.i(TAG, "cancelToast pkg=" + pkg + " callback=" + callback);
1095
1096            if (pkg == null || callback == null) {
1097                Slog.e(TAG, "Not cancelling notification. pkg=" + pkg + " callback=" + callback);
1098                return ;
1099            }
1100
1101            synchronized (mToastQueue) {
1102                long callingId = Binder.clearCallingIdentity();
1103                try {
1104                    int index = indexOfToastLocked(pkg, callback);
1105                    if (index >= 0) {
1106                        cancelToastLocked(index);
1107                    } else {
1108                        Slog.w(TAG, "Toast already cancelled. pkg=" + pkg
1109                                + " callback=" + callback);
1110                    }
1111                } finally {
1112                    Binder.restoreCallingIdentity(callingId);
1113                }
1114            }
1115        }
1116
1117        @Override
1118        public void enqueueNotificationWithTag(String pkg, String opPkg, String tag, int id,
1119                Notification notification, int[] idOut, int userId) throws RemoteException {
1120            enqueueNotificationInternal(pkg, opPkg, Binder.getCallingUid(),
1121                    Binder.getCallingPid(), tag, id, notification, idOut, userId);
1122        }
1123
1124        @Override
1125        public void cancelNotificationWithTag(String pkg, String tag, int id, int userId) {
1126            checkCallerIsSystemOrSameApp(pkg);
1127            userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1128                    Binder.getCallingUid(), userId, true, false, "cancelNotificationWithTag", pkg);
1129            // Don't allow client applications to cancel foreground service notis.
1130            cancelNotification(Binder.getCallingUid(), Binder.getCallingPid(), pkg, tag, id, 0,
1131                    Binder.getCallingUid() == Process.SYSTEM_UID
1132                    ? 0 : Notification.FLAG_FOREGROUND_SERVICE, false, userId, REASON_NOMAN_CANCEL,
1133                    null);
1134        }
1135
1136        @Override
1137        public void cancelAllNotifications(String pkg, int userId) {
1138            checkCallerIsSystemOrSameApp(pkg);
1139
1140            userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1141                    Binder.getCallingUid(), userId, true, false, "cancelAllNotifications", pkg);
1142
1143            // Calling from user space, don't allow the canceling of actively
1144            // running foreground services.
1145            cancelAllNotificationsInt(Binder.getCallingUid(), Binder.getCallingPid(),
1146                    pkg, 0, Notification.FLAG_FOREGROUND_SERVICE, true, userId,
1147                    REASON_NOMAN_CANCEL_ALL, null);
1148        }
1149
1150        @Override
1151        public void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) {
1152            checkCallerIsSystem();
1153
1154            setNotificationsEnabledForPackageImpl(pkg, uid, enabled);
1155        }
1156
1157        /**
1158         * Use this when you just want to know if notifications are OK for this package.
1159         */
1160        @Override
1161        public boolean areNotificationsEnabledForPackage(String pkg, int uid) {
1162            checkCallerIsSystem();
1163            return (mAppOps.checkOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
1164                    == AppOpsManager.MODE_ALLOWED);
1165        }
1166
1167        @Override
1168        public void setPackagePriority(String pkg, int uid, int priority) {
1169            checkCallerIsSystem();
1170            mRankingHelper.setPackagePriority(pkg, uid, priority);
1171            savePolicyFile();
1172        }
1173
1174        @Override
1175        public int getPackagePriority(String pkg, int uid) {
1176            checkCallerIsSystem();
1177            return mRankingHelper.getPackagePriority(pkg, uid);
1178        }
1179
1180        @Override
1181        public void setPackageVisibilityOverride(String pkg, int uid, int visibility) {
1182            checkCallerIsSystem();
1183            mRankingHelper.setPackageVisibilityOverride(pkg, uid, visibility);
1184            savePolicyFile();
1185        }
1186
1187        @Override
1188        public int getPackageVisibilityOverride(String pkg, int uid) {
1189            checkCallerIsSystem();
1190            return mRankingHelper.getPackageVisibilityOverride(pkg, uid);
1191        }
1192
1193        /**
1194         * System-only API for getting a list of current (i.e. not cleared) notifications.
1195         *
1196         * Requires ACCESS_NOTIFICATIONS which is signature|system.
1197         * @returns A list of all the notifications, in natural order.
1198         */
1199        @Override
1200        public StatusBarNotification[] getActiveNotifications(String callingPkg) {
1201            // enforce() will ensure the calling uid has the correct permission
1202            getContext().enforceCallingOrSelfPermission(
1203                    android.Manifest.permission.ACCESS_NOTIFICATIONS,
1204                    "NotificationManagerService.getActiveNotifications");
1205
1206            StatusBarNotification[] tmp = null;
1207            int uid = Binder.getCallingUid();
1208
1209            // noteOp will check to make sure the callingPkg matches the uid
1210            if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
1211                    == AppOpsManager.MODE_ALLOWED) {
1212                synchronized (mNotificationList) {
1213                    tmp = new StatusBarNotification[mNotificationList.size()];
1214                    final int N = mNotificationList.size();
1215                    for (int i=0; i<N; i++) {
1216                        tmp[i] = mNotificationList.get(i).sbn;
1217                    }
1218                }
1219            }
1220            return tmp;
1221        }
1222
1223        /**
1224         * System-only API for getting a list of recent (cleared, no longer shown) notifications.
1225         *
1226         * Requires ACCESS_NOTIFICATIONS which is signature|system.
1227         */
1228        @Override
1229        public StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count) {
1230            // enforce() will ensure the calling uid has the correct permission
1231            getContext().enforceCallingOrSelfPermission(
1232                    android.Manifest.permission.ACCESS_NOTIFICATIONS,
1233                    "NotificationManagerService.getHistoricalNotifications");
1234
1235            StatusBarNotification[] tmp = null;
1236            int uid = Binder.getCallingUid();
1237
1238            // noteOp will check to make sure the callingPkg matches the uid
1239            if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
1240                    == AppOpsManager.MODE_ALLOWED) {
1241                synchronized (mArchive) {
1242                    tmp = mArchive.getArray(count);
1243                }
1244            }
1245            return tmp;
1246        }
1247
1248        /**
1249         * Register a listener binder directly with the notification manager.
1250         *
1251         * Only works with system callers. Apps should extend
1252         * {@link android.service.notification.NotificationListenerService}.
1253         */
1254        @Override
1255        public void registerListener(final INotificationListener listener,
1256                final ComponentName component, final int userid) {
1257            enforceSystemOrSystemUI("INotificationManager.registerListener");
1258            mListeners.registerService(listener, component, userid);
1259        }
1260
1261        /**
1262         * Remove a listener binder directly
1263         */
1264        @Override
1265        public void unregisterListener(INotificationListener listener, int userid) {
1266            mListeners.unregisterService(listener, userid);
1267        }
1268
1269        /**
1270         * Allow an INotificationListener to simulate a "clear all" operation.
1271         *
1272         * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onClearAllNotifications}
1273         *
1274         * @param token The binder for the listener, to check that the caller is allowed
1275         */
1276        @Override
1277        public void cancelNotificationsFromListener(INotificationListener token, String[] keys) {
1278            final int callingUid = Binder.getCallingUid();
1279            final int callingPid = Binder.getCallingPid();
1280            long identity = Binder.clearCallingIdentity();
1281            try {
1282                synchronized (mNotificationList) {
1283                    final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1284                    if (keys != null) {
1285                        final int N = keys.length;
1286                        for (int i = 0; i < N; i++) {
1287                            NotificationRecord r = mNotificationsByKey.get(keys[i]);
1288                            if (r == null) continue;
1289                            final int userId = r.sbn.getUserId();
1290                            if (userId != info.userid && userId != UserHandle.USER_ALL &&
1291                                    !mUserProfiles.isCurrentProfile(userId)) {
1292                                throw new SecurityException("Disallowed call from listener: "
1293                                        + info.service);
1294                            }
1295                            cancelNotificationFromListenerLocked(info, callingUid, callingPid,
1296                                    r.sbn.getPackageName(), r.sbn.getTag(), r.sbn.getId(),
1297                                    userId);
1298                        }
1299                    } else {
1300                        cancelAllLocked(callingUid, callingPid, info.userid,
1301                                REASON_LISTENER_CANCEL_ALL, info, info.supportsProfiles());
1302                    }
1303                }
1304            } finally {
1305                Binder.restoreCallingIdentity(identity);
1306            }
1307        }
1308
1309        private void cancelNotificationFromListenerLocked(ManagedServiceInfo info,
1310                int callingUid, int callingPid, String pkg, String tag, int id, int userId) {
1311            cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
1312                    Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
1313                    true,
1314                    userId, REASON_LISTENER_CANCEL, info);
1315        }
1316
1317        /**
1318         * Allow an INotificationListener to simulate clearing (dismissing) a single notification.
1319         *
1320         * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onNotificationClear}
1321         *
1322         * @param token The binder for the listener, to check that the caller is allowed
1323         */
1324        @Override
1325        public void cancelNotificationFromListener(INotificationListener token, String pkg,
1326                String tag, int id) {
1327            final int callingUid = Binder.getCallingUid();
1328            final int callingPid = Binder.getCallingPid();
1329            long identity = Binder.clearCallingIdentity();
1330            try {
1331                synchronized (mNotificationList) {
1332                    final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1333                    if (info.supportsProfiles()) {
1334                        Log.e(TAG, "Ignoring deprecated cancelNotification(pkg, tag, id) "
1335                                + "from " + info.component
1336                                + " use cancelNotification(key) instead.");
1337                    } else {
1338                        cancelNotificationFromListenerLocked(info, callingUid, callingPid,
1339                                pkg, tag, id, info.userid);
1340                    }
1341                }
1342            } finally {
1343                Binder.restoreCallingIdentity(identity);
1344            }
1345        }
1346
1347        /**
1348         * Allow an INotificationListener to request the list of outstanding notifications seen by
1349         * the current user. Useful when starting up, after which point the listener callbacks
1350         * should be used.
1351         *
1352         * @param token The binder for the listener, to check that the caller is allowed
1353         * @param keys An array of notification keys to fetch, or null to fetch everything
1354         * @returns The return value will contain the notifications specified in keys, in that
1355         *      order, or if keys is null, all the notifications, in natural order.
1356         */
1357        @Override
1358        public ParceledListSlice<StatusBarNotification> getActiveNotificationsFromListener(
1359                INotificationListener token, String[] keys, int trim) {
1360            synchronized (mNotificationList) {
1361                final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1362                final boolean getKeys = keys != null;
1363                final int N = getKeys ? keys.length : mNotificationList.size();
1364                final ArrayList<StatusBarNotification> list
1365                        = new ArrayList<StatusBarNotification>(N);
1366                for (int i=0; i<N; i++) {
1367                    final NotificationRecord r = getKeys
1368                            ? mNotificationsByKey.get(keys[i])
1369                            : mNotificationList.get(i);
1370                    if (r == null) continue;
1371                    StatusBarNotification sbn = r.sbn;
1372                    if (!isVisibleToListener(sbn, info)) continue;
1373                    StatusBarNotification sbnToSend =
1374                            (trim == TRIM_FULL) ? sbn : sbn.cloneLight();
1375                    list.add(sbnToSend);
1376                }
1377                return new ParceledListSlice<StatusBarNotification>(list);
1378            }
1379        }
1380
1381        @Override
1382        public void requestHintsFromListener(INotificationListener token, int hints) {
1383            final long identity = Binder.clearCallingIdentity();
1384            try {
1385                synchronized (mNotificationList) {
1386                    final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1387                    final boolean disableEffects = (hints & HINT_HOST_DISABLE_EFFECTS) != 0;
1388                    if (disableEffects) {
1389                        mListenersDisablingEffects.add(info);
1390                    } else {
1391                        mListenersDisablingEffects.remove(info);
1392                    }
1393                    updateListenerHintsLocked();
1394                    updateEffectsSuppressorLocked();
1395                }
1396            } finally {
1397                Binder.restoreCallingIdentity(identity);
1398            }
1399        }
1400
1401        @Override
1402        public int getHintsFromListener(INotificationListener token) {
1403            synchronized (mNotificationList) {
1404                return mListenerHints;
1405            }
1406        }
1407
1408        @Override
1409        public void requestInterruptionFilterFromListener(INotificationListener token,
1410                int interruptionFilter) throws RemoteException {
1411            final long identity = Binder.clearCallingIdentity();
1412            try {
1413                synchronized (mNotificationList) {
1414                    final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1415                    mZenModeHelper.requestFromListener(info.component, interruptionFilter);
1416                    updateInterruptionFilterLocked();
1417                }
1418            } finally {
1419                Binder.restoreCallingIdentity(identity);
1420            }
1421        }
1422
1423        @Override
1424        public int getInterruptionFilterFromListener(INotificationListener token)
1425                throws RemoteException {
1426            synchronized (mNotificationLight) {
1427                return mInterruptionFilter;
1428            }
1429        }
1430
1431        @Override
1432        public void setOnNotificationPostedTrimFromListener(INotificationListener token, int trim)
1433                throws RemoteException {
1434            synchronized (mNotificationList) {
1435                final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1436                if (info == null) return;
1437                mListeners.setOnNotificationPostedTrimLocked(info, trim);
1438            }
1439        }
1440
1441        @Override
1442        public ZenModeConfig getZenModeConfig() {
1443            enforceSystemOrSystemUI("INotificationManager.getZenModeConfig");
1444            return mZenModeHelper.getConfig();
1445        }
1446
1447        @Override
1448        public boolean setZenModeConfig(ZenModeConfig config) {
1449            checkCallerIsSystem();
1450            return mZenModeHelper.setConfig(config);
1451        }
1452
1453        @Override
1454        public void notifyConditions(String pkg, IConditionProvider provider,
1455                Condition[] conditions) {
1456            final ManagedServiceInfo info = mConditionProviders.checkServiceToken(provider);
1457            checkCallerIsSystemOrSameApp(pkg);
1458            final long identity = Binder.clearCallingIdentity();
1459            try {
1460                mConditionProviders.notifyConditions(pkg, info, conditions);
1461            } finally {
1462                Binder.restoreCallingIdentity(identity);
1463            }
1464        }
1465
1466        @Override
1467        public void requestZenModeConditions(IConditionListener callback, int relevance) {
1468            enforceSystemOrSystemUI("INotificationManager.requestZenModeConditions");
1469            mConditionProviders.requestZenModeConditions(callback, relevance);
1470        }
1471
1472        @Override
1473        public void setZenModeCondition(Condition condition) {
1474            enforceSystemOrSystemUI("INotificationManager.setZenModeCondition");
1475            final long identity = Binder.clearCallingIdentity();
1476            try {
1477                mConditionProviders.setZenModeCondition(condition, "binderCall");
1478            } finally {
1479                Binder.restoreCallingIdentity(identity);
1480            }
1481        }
1482
1483        @Override
1484        public void setAutomaticZenModeConditions(Uri[] conditionIds) {
1485            enforceSystemOrSystemUI("INotificationManager.setAutomaticZenModeConditions");
1486            mConditionProviders.setAutomaticZenModeConditions(conditionIds);
1487        }
1488
1489        @Override
1490        public Condition[] getAutomaticZenModeConditions() {
1491            enforceSystemOrSystemUI("INotificationManager.getAutomaticZenModeConditions");
1492            return mConditionProviders.getAutomaticZenModeConditions();
1493        }
1494
1495        private void enforceSystemOrSystemUI(String message) {
1496            if (isCallerSystem()) return;
1497            getContext().enforceCallingPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
1498                    message);
1499        }
1500
1501        @Override
1502        protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1503            if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
1504                    != PackageManager.PERMISSION_GRANTED) {
1505                pw.println("Permission Denial: can't dump NotificationManager from pid="
1506                        + Binder.getCallingPid()
1507                        + ", uid=" + Binder.getCallingUid());
1508                return;
1509            }
1510
1511            dumpImpl(pw, DumpFilter.parseFromArguments(args));
1512        }
1513
1514        @Override
1515        public ComponentName getEffectsSuppressor() {
1516            enforceSystemOrSystemUI("INotificationManager.getEffectsSuppressor");
1517            return mEffectsSuppressor;
1518        }
1519
1520        @Override
1521        public boolean matchesCallFilter(Bundle extras) {
1522            enforceSystemOrSystemUI("INotificationManager.matchesCallFilter");
1523            return mZenModeHelper.matchesCallFilter(
1524                    UserHandle.getCallingUserHandle(),
1525                    extras,
1526                    mRankingHelper.findExtractor(ValidateNotificationPeople.class),
1527                    MATCHES_CALL_FILTER_CONTACTS_TIMEOUT_MS,
1528                    MATCHES_CALL_FILTER_TIMEOUT_AFFINITY);
1529        }
1530    };
1531
1532    private String[] getActiveNotificationKeys(INotificationListener token) {
1533        final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1534        final ArrayList<String> keys = new ArrayList<String>();
1535        if (info.isEnabledForCurrentProfiles()) {
1536            synchronized (mNotificationList) {
1537                final int N = mNotificationList.size();
1538                for (int i = 0; i < N; i++) {
1539                    final StatusBarNotification sbn = mNotificationList.get(i).sbn;
1540                    if (info.enabledAndUserMatches(sbn.getUserId())) {
1541                        keys.add(sbn.getKey());
1542                    }
1543                }
1544            }
1545        }
1546        return keys.toArray(new String[keys.size()]);
1547    }
1548
1549    private String disableNotificationEffects(NotificationRecord record) {
1550        if (mDisableNotificationEffects) {
1551            return "booleanState";
1552        }
1553        if ((mListenerHints & HINT_HOST_DISABLE_EFFECTS) != 0) {
1554            return "listenerHints";
1555        }
1556        if (mCallState != TelephonyManager.CALL_STATE_IDLE && !mZenModeHelper.isCall(record)) {
1557            return "callState";
1558        }
1559        return null;
1560    }
1561
1562    void dumpImpl(PrintWriter pw, DumpFilter filter) {
1563        pw.print("Current Notification Manager state");
1564        if (filter != null) {
1565            pw.print(" (filtered to "); pw.print(filter); pw.print(")");
1566        }
1567        pw.println(':');
1568        int N;
1569        final boolean zenOnly = filter != null && filter.zen;
1570
1571        if (!zenOnly) {
1572            synchronized (mToastQueue) {
1573                N = mToastQueue.size();
1574                if (N > 0) {
1575                    pw.println("  Toast Queue:");
1576                    for (int i=0; i<N; i++) {
1577                        mToastQueue.get(i).dump(pw, "    ", filter);
1578                    }
1579                    pw.println("  ");
1580                }
1581            }
1582        }
1583
1584        synchronized (mNotificationList) {
1585            if (!zenOnly) {
1586                N = mNotificationList.size();
1587                if (N > 0) {
1588                    pw.println("  Notification List:");
1589                    for (int i=0; i<N; i++) {
1590                        final NotificationRecord nr = mNotificationList.get(i);
1591                        if (filter != null && !filter.matches(nr.sbn)) continue;
1592                        nr.dump(pw, "    ", getContext());
1593                    }
1594                    pw.println("  ");
1595                }
1596
1597                if (filter == null) {
1598                    N = mLights.size();
1599                    if (N > 0) {
1600                        pw.println("  Lights List:");
1601                        for (int i=0; i<N; i++) {
1602                            if (i == N - 1) {
1603                                pw.print("  > ");
1604                            } else {
1605                                pw.print("    ");
1606                            }
1607                            pw.println(mLights.get(i));
1608                        }
1609                        pw.println("  ");
1610                    }
1611                    pw.println("  mUseAttentionLight=" + mUseAttentionLight);
1612                    pw.println("  mNotificationPulseEnabled=" + mNotificationPulseEnabled);
1613                    pw.println("  mSoundNotificationKey=" + mSoundNotificationKey);
1614                    pw.println("  mVibrateNotificationKey=" + mVibrateNotificationKey);
1615                    pw.println("  mDisableNotificationEffects=" + mDisableNotificationEffects);
1616                    pw.println("  mCallState=" + callStateToString(mCallState));
1617                    pw.println("  mSystemReady=" + mSystemReady);
1618                }
1619                pw.println("  mArchive=" + mArchive.toString());
1620                Iterator<StatusBarNotification> iter = mArchive.descendingIterator();
1621                int i=0;
1622                while (iter.hasNext()) {
1623                    final StatusBarNotification sbn = iter.next();
1624                    if (filter != null && !filter.matches(sbn)) continue;
1625                    pw.println("    " + sbn);
1626                    if (++i >= 5) {
1627                        if (iter.hasNext()) pw.println("    ...");
1628                        break;
1629                    }
1630                }
1631            }
1632
1633            if (!zenOnly) {
1634                pw.println("\n  Usage Stats:");
1635                mUsageStats.dump(pw, "    ", filter);
1636            }
1637
1638            if (filter == null || zenOnly) {
1639                pw.println("\n  Zen Mode:");
1640                mZenModeHelper.dump(pw, "    ");
1641
1642                pw.println("\n  Zen Log:");
1643                ZenLog.dump(pw, "    ");
1644            }
1645
1646            if (!zenOnly) {
1647                pw.println("\n  Ranking Config:");
1648                mRankingHelper.dump(pw, "    ", filter);
1649
1650                pw.println("\n  Notification listeners:");
1651                mListeners.dump(pw, filter);
1652                pw.print("    mListenerHints: "); pw.println(mListenerHints);
1653                pw.print("    mListenersDisablingEffects: (");
1654                N = mListenersDisablingEffects.size();
1655                for (int i = 0; i < N; i++) {
1656                    final ManagedServiceInfo listener = mListenersDisablingEffects.valueAt(i);
1657                    if (i > 0) pw.print(',');
1658                    pw.print(listener.component);
1659                }
1660                pw.println(')');
1661            }
1662
1663            pw.println("\n  Condition providers:");
1664            mConditionProviders.dump(pw, filter);
1665        }
1666    }
1667
1668    /**
1669     * The private API only accessible to the system process.
1670     */
1671    private final NotificationManagerInternal mInternalService = new NotificationManagerInternal() {
1672        @Override
1673        public void enqueueNotification(String pkg, String opPkg, int callingUid, int callingPid,
1674                String tag, int id, Notification notification, int[] idReceived, int userId) {
1675            enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, notification,
1676                    idReceived, userId);
1677        }
1678
1679        @Override
1680        public void removeForegroundServiceFlagFromNotification(String pkg, int notificationId,
1681                int userId) {
1682            checkCallerIsSystem();
1683            synchronized (mNotificationList) {
1684                int i = indexOfNotificationLocked(pkg, null, notificationId, userId);
1685                if (i < 0) {
1686                    Log.d(TAG, "stripForegroundServiceFlag: Could not find notification with "
1687                            + "pkg=" + pkg + " / id=" + notificationId + " / userId=" + userId);
1688                    return;
1689                }
1690                NotificationRecord r = mNotificationList.get(i);
1691                StatusBarNotification sbn = r.sbn;
1692                // NoMan adds flags FLAG_NO_CLEAR and FLAG_ONGOING_EVENT when it sees
1693                // FLAG_FOREGROUND_SERVICE. Hence it's not enough to remove FLAG_FOREGROUND_SERVICE,
1694                // we have to revert to the flags we received initially *and* force remove
1695                // FLAG_FOREGROUND_SERVICE.
1696                sbn.getNotification().flags =
1697                        (r.mOriginalFlags & ~Notification.FLAG_FOREGROUND_SERVICE);
1698                mRankingHelper.sort(mNotificationList);
1699                mListeners.notifyPostedLocked(sbn, sbn /* oldSbn */);
1700            }
1701        }
1702    };
1703
1704    void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,
1705            final int callingPid, final String tag, final int id, final Notification notification,
1706            int[] idOut, int incomingUserId) {
1707        if (DBG) {
1708            Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id
1709                    + " notification=" + notification);
1710        }
1711        checkCallerIsSystemOrSameApp(pkg);
1712        final boolean isSystemNotification = isUidSystem(callingUid) || ("android".equals(pkg));
1713        final boolean isNotificationFromListener = mListeners.isListenerPackage(pkg);
1714
1715        final int userId = ActivityManager.handleIncomingUser(callingPid,
1716                callingUid, incomingUserId, true, false, "enqueueNotification", pkg);
1717        final UserHandle user = new UserHandle(userId);
1718
1719        // Limit the number of notifications that any given package except the android
1720        // package or a registered listener can enqueue.  Prevents DOS attacks and deals with leaks.
1721        if (!isSystemNotification && !isNotificationFromListener) {
1722            synchronized (mNotificationList) {
1723                int count = 0;
1724                final int N = mNotificationList.size();
1725                for (int i=0; i<N; i++) {
1726                    final NotificationRecord r = mNotificationList.get(i);
1727                    if (r.sbn.getPackageName().equals(pkg) && r.sbn.getUserId() == userId) {
1728                        count++;
1729                        if (count >= MAX_PACKAGE_NOTIFICATIONS) {
1730                            Slog.e(TAG, "Package has already posted " + count
1731                                    + " notifications.  Not showing more.  package=" + pkg);
1732                            return;
1733                        }
1734                    }
1735                }
1736            }
1737        }
1738
1739        if (pkg == null || notification == null) {
1740            throw new IllegalArgumentException("null not allowed: pkg=" + pkg
1741                    + " id=" + id + " notification=" + notification);
1742        }
1743        if (notification.icon != 0) {
1744            if (!notification.isValid()) {
1745                throw new IllegalArgumentException("Invalid notification (): pkg=" + pkg
1746                        + " id=" + id + " notification=" + notification);
1747            }
1748        }
1749
1750        mHandler.post(new Runnable() {
1751            @Override
1752            public void run() {
1753
1754                synchronized (mNotificationList) {
1755
1756                    // === Scoring ===
1757
1758                    // 0. Sanitize inputs
1759                    notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN,
1760                            Notification.PRIORITY_MAX);
1761                    // Migrate notification flags to scores
1762                    if (0 != (notification.flags & Notification.FLAG_HIGH_PRIORITY)) {
1763                        if (notification.priority < Notification.PRIORITY_MAX) {
1764                            notification.priority = Notification.PRIORITY_MAX;
1765                        }
1766                    } else if (SCORE_ONGOING_HIGHER &&
1767                            0 != (notification.flags & Notification.FLAG_ONGOING_EVENT)) {
1768                        if (notification.priority < Notification.PRIORITY_HIGH) {
1769                            notification.priority = Notification.PRIORITY_HIGH;
1770                        }
1771                    }
1772
1773                    // 1. initial score: buckets of 10, around the app [-20..20]
1774                    final int score = notification.priority * NOTIFICATION_PRIORITY_MULTIPLIER;
1775
1776                    // 2. extract ranking signals from the notification data
1777                    final StatusBarNotification n = new StatusBarNotification(
1778                            pkg, opPkg, id, tag, callingUid, callingPid, score, notification,
1779                            user);
1780                    NotificationRecord r = new NotificationRecord(n, score);
1781                    NotificationRecord old = mNotificationsByKey.get(n.getKey());
1782                    if (old != null) {
1783                        // Retain ranking information from previous record
1784                        r.copyRankingInformation(old);
1785                    }
1786                    mRankingHelper.extractSignals(r);
1787
1788                    // This conditional is a dirty hack to limit the logging done on
1789                    //     behalf of the download manager without affecting other apps.
1790                    if (!pkg.equals("com.android.providers.downloads")
1791                            || Log.isLoggable("DownloadManager", Log.VERBOSE)) {
1792                        EventLogTags.writeNotificationEnqueue(callingUid, callingPid,
1793                                pkg, id, tag, userId, notification.toString(),
1794                                (old != null) ? 1 : 0);
1795                    }
1796                    // 3. Apply local rules
1797
1798                    // blocked apps
1799                    if (ENABLE_BLOCKED_NOTIFICATIONS && !noteNotificationOp(pkg, callingUid)) {
1800                        if (!isSystemNotification) {
1801                            r.score = JUNK_SCORE;
1802                            Slog.e(TAG, "Suppressing notification from package " + pkg
1803                                    + " by user request.");
1804                        }
1805                    }
1806
1807                    if (r.score < SCORE_DISPLAY_THRESHOLD) {
1808                        // Notification will be blocked because the score is too low.
1809                        return;
1810                    }
1811
1812                    // Clear out group children of the old notification if the update causes the
1813                    // group summary to go away. This happens when the old notification was a
1814                    // summary and the new one isn't, or when the old notification was a summary
1815                    // and its group key changed.
1816                    if (old != null && old.getNotification().isGroupSummary() &&
1817                            (!notification.isGroupSummary() ||
1818                                    !old.getGroupKey().equals(r.getGroupKey()))) {
1819                        cancelGroupChildrenLocked(old, callingUid, callingPid, null);
1820                    }
1821
1822                    int index = indexOfNotificationLocked(n.getKey());
1823                    if (index < 0) {
1824                        mNotificationList.add(r);
1825                        mUsageStats.registerPostedByApp(r);
1826                    } else {
1827                        old = mNotificationList.get(index);
1828                        mNotificationList.set(index, r);
1829                        mUsageStats.registerUpdatedByApp(r, old);
1830                        // Make sure we don't lose the foreground service state.
1831                        notification.flags |=
1832                                old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;
1833                        r.isUpdate = true;
1834                    }
1835
1836                    mNotificationsByKey.put(n.getKey(), r);
1837
1838                    // Ensure if this is a foreground service that the proper additional
1839                    // flags are set.
1840                    if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
1841                        notification.flags |= Notification.FLAG_ONGOING_EVENT
1842                                | Notification.FLAG_NO_CLEAR;
1843                    }
1844
1845                    applyZenModeLocked(r);
1846                    mRankingHelper.sort(mNotificationList);
1847
1848                    if (notification.icon != 0) {
1849                        StatusBarNotification oldSbn = (old != null) ? old.sbn : null;
1850                        mListeners.notifyPostedLocked(n, oldSbn);
1851                    } else {
1852                        Slog.e(TAG, "Not posting notification with icon==0: " + notification);
1853                        if (old != null && !old.isCanceled) {
1854                            mListeners.notifyRemovedLocked(n);
1855                        }
1856                        // ATTENTION: in a future release we will bail out here
1857                        // so that we do not play sounds, show lights, etc. for invalid
1858                        // notifications
1859                        Slog.e(TAG, "WARNING: In a future release this will crash the app: "
1860                                + n.getPackageName());
1861                    }
1862
1863                    buzzBeepBlinkLocked(r);
1864                }
1865            }
1866        });
1867
1868        idOut[0] = id;
1869    }
1870
1871    private void buzzBeepBlinkLocked(NotificationRecord record) {
1872        boolean buzzBeepBlinked = false;
1873        final Notification notification = record.sbn.getNotification();
1874
1875        // Should this notification make noise, vibe, or use the LED?
1876        final boolean aboveThreshold = record.score >= SCORE_INTERRUPTION_THRESHOLD;
1877        final boolean canInterrupt = aboveThreshold && !record.isIntercepted();
1878        if (DBG || record.isIntercepted())
1879            Slog.v(TAG,
1880                    "pkg=" + record.sbn.getPackageName() + " canInterrupt=" + canInterrupt +
1881                            " intercept=" + record.isIntercepted()
1882            );
1883
1884        final int currentUser;
1885        final long token = Binder.clearCallingIdentity();
1886        try {
1887            currentUser = ActivityManager.getCurrentUser();
1888        } finally {
1889            Binder.restoreCallingIdentity(token);
1890        }
1891
1892        // If we're not supposed to beep, vibrate, etc. then don't.
1893        final String disableEffects = disableNotificationEffects(record);
1894        if (disableEffects != null) {
1895            ZenLog.traceDisableEffects(record, disableEffects);
1896        }
1897        if (disableEffects == null
1898                && (!(record.isUpdate
1899                    && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 ))
1900                && (record.getUserId() == UserHandle.USER_ALL ||
1901                    record.getUserId() == currentUser ||
1902                    mUserProfiles.isCurrentProfile(record.getUserId()))
1903                && canInterrupt
1904                && mSystemReady
1905                && mAudioManager != null) {
1906            if (DBG) Slog.v(TAG, "Interrupting!");
1907
1908            sendAccessibilityEvent(notification, record.sbn.getPackageName());
1909
1910            // sound
1911
1912            // should we use the default notification sound? (indicated either by
1913            // DEFAULT_SOUND or because notification.sound is pointing at
1914            // Settings.System.NOTIFICATION_SOUND)
1915            final boolean useDefaultSound =
1916                   (notification.defaults & Notification.DEFAULT_SOUND) != 0 ||
1917                           Settings.System.DEFAULT_NOTIFICATION_URI
1918                                   .equals(notification.sound);
1919
1920            Uri soundUri = null;
1921            boolean hasValidSound = false;
1922
1923            if (useDefaultSound) {
1924                soundUri = Settings.System.DEFAULT_NOTIFICATION_URI;
1925
1926                // check to see if the default notification sound is silent
1927                ContentResolver resolver = getContext().getContentResolver();
1928                hasValidSound = Settings.System.getString(resolver,
1929                       Settings.System.NOTIFICATION_SOUND) != null;
1930            } else if (notification.sound != null) {
1931                soundUri = notification.sound;
1932                hasValidSound = (soundUri != null);
1933            }
1934
1935            if (hasValidSound) {
1936                boolean looping =
1937                        (notification.flags & Notification.FLAG_INSISTENT) != 0;
1938                AudioAttributes audioAttributes = audioAttributesForNotification(notification);
1939                mSoundNotificationKey = record.getKey();
1940                // do not play notifications if stream volume is 0 (typically because
1941                // ringer mode is silent) or if there is a user of exclusive audio focus
1942                if ((mAudioManager.getStreamVolume(
1943                        AudioAttributes.toLegacyStreamType(audioAttributes)) != 0)
1944                            && !mAudioManager.isAudioFocusExclusive()) {
1945                    final long identity = Binder.clearCallingIdentity();
1946                    try {
1947                        final IRingtonePlayer player =
1948                                mAudioManager.getRingtonePlayer();
1949                        if (player != null) {
1950                            if (DBG) Slog.v(TAG, "Playing sound " + soundUri
1951                                    + " with attributes " + audioAttributes);
1952                            player.playAsync(soundUri, record.sbn.getUser(), looping,
1953                                    audioAttributes);
1954                            buzzBeepBlinked = true;
1955                        }
1956                    } catch (RemoteException e) {
1957                    } finally {
1958                        Binder.restoreCallingIdentity(identity);
1959                    }
1960                }
1961            }
1962
1963            // vibrate
1964            // Does the notification want to specify its own vibration?
1965            final boolean hasCustomVibrate = notification.vibrate != null;
1966
1967            // new in 4.2: if there was supposed to be a sound and we're in vibrate
1968            // mode, and no other vibration is specified, we fall back to vibration
1969            final boolean convertSoundToVibration =
1970                       !hasCustomVibrate
1971                    && hasValidSound
1972                    && (mAudioManager.getRingerModeInternal()
1973                               == AudioManager.RINGER_MODE_VIBRATE);
1974
1975            // The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback.
1976            final boolean useDefaultVibrate =
1977                    (notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
1978
1979            if ((useDefaultVibrate || convertSoundToVibration || hasCustomVibrate)
1980                    && !(mAudioManager.getRingerModeInternal()
1981                            == AudioManager.RINGER_MODE_SILENT)) {
1982                mVibrateNotificationKey = record.getKey();
1983
1984                if (useDefaultVibrate || convertSoundToVibration) {
1985                    // Escalate privileges so we can use the vibrator even if the
1986                    // notifying app does not have the VIBRATE permission.
1987                    long identity = Binder.clearCallingIdentity();
1988                    try {
1989                        mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
1990                            useDefaultVibrate ? mDefaultVibrationPattern
1991                                : mFallbackVibrationPattern,
1992                            ((notification.flags & Notification.FLAG_INSISTENT) != 0)
1993                                ? 0: -1, audioAttributesForNotification(notification));
1994                        buzzBeepBlinked = true;
1995                    } finally {
1996                        Binder.restoreCallingIdentity(identity);
1997                    }
1998                } else if (notification.vibrate.length > 1) {
1999                    // If you want your own vibration pattern, you need the VIBRATE
2000                    // permission
2001                    mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
2002                            notification.vibrate,
2003                        ((notification.flags & Notification.FLAG_INSISTENT) != 0)
2004                                ? 0: -1, audioAttributesForNotification(notification));
2005                    buzzBeepBlinked = true;
2006                }
2007            }
2008        }
2009
2010        // light
2011        // release the light
2012        boolean wasShowLights = mLights.remove(record.getKey());
2013        if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0 && aboveThreshold) {
2014            mLights.add(record.getKey());
2015            updateLightsLocked();
2016            if (mUseAttentionLight) {
2017                mAttentionLight.pulse();
2018            }
2019            buzzBeepBlinked = true;
2020        } else if (wasShowLights) {
2021            updateLightsLocked();
2022        }
2023        if (buzzBeepBlinked) {
2024            mHandler.post(mBuzzBeepBlinked);
2025        }
2026    }
2027
2028    private static AudioAttributes audioAttributesForNotification(Notification n) {
2029        if (n.audioAttributes != null
2030                && !Notification.AUDIO_ATTRIBUTES_DEFAULT.equals(n.audioAttributes)) {
2031            // the audio attributes are set and different from the default, use them
2032            return n.audioAttributes;
2033        } else if (n.audioStreamType >= 0 && n.audioStreamType < AudioSystem.getNumStreamTypes()) {
2034            // the stream type is valid, use it
2035            return new AudioAttributes.Builder()
2036                    .setInternalLegacyStreamType(n.audioStreamType)
2037                    .build();
2038        } else if (n.audioStreamType == AudioSystem.STREAM_DEFAULT) {
2039            return Notification.AUDIO_ATTRIBUTES_DEFAULT;
2040        } else {
2041            Log.w(TAG, String.format("Invalid stream type: %d", n.audioStreamType));
2042            return Notification.AUDIO_ATTRIBUTES_DEFAULT;
2043        }
2044    }
2045
2046    void showNextToastLocked() {
2047        ToastRecord record = mToastQueue.get(0);
2048        while (record != null) {
2049            if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback);
2050            try {
2051                record.callback.show();
2052                scheduleTimeoutLocked(record);
2053                return;
2054            } catch (RemoteException e) {
2055                Slog.w(TAG, "Object died trying to show notification " + record.callback
2056                        + " in package " + record.pkg);
2057                // remove it from the list and let the process die
2058                int index = mToastQueue.indexOf(record);
2059                if (index >= 0) {
2060                    mToastQueue.remove(index);
2061                }
2062                keepProcessAliveLocked(record.pid);
2063                if (mToastQueue.size() > 0) {
2064                    record = mToastQueue.get(0);
2065                } else {
2066                    record = null;
2067                }
2068            }
2069        }
2070    }
2071
2072    void cancelToastLocked(int index) {
2073        ToastRecord record = mToastQueue.get(index);
2074        try {
2075            record.callback.hide();
2076        } catch (RemoteException e) {
2077            Slog.w(TAG, "Object died trying to hide notification " + record.callback
2078                    + " in package " + record.pkg);
2079            // don't worry about this, we're about to remove it from
2080            // the list anyway
2081        }
2082        mToastQueue.remove(index);
2083        keepProcessAliveLocked(record.pid);
2084        if (mToastQueue.size() > 0) {
2085            // Show the next one. If the callback fails, this will remove
2086            // it from the list, so don't assume that the list hasn't changed
2087            // after this point.
2088            showNextToastLocked();
2089        }
2090    }
2091
2092    private void scheduleTimeoutLocked(ToastRecord r)
2093    {
2094        mHandler.removeCallbacksAndMessages(r);
2095        Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r);
2096        long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY;
2097        mHandler.sendMessageDelayed(m, delay);
2098    }
2099
2100    private void handleTimeout(ToastRecord record)
2101    {
2102        if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback);
2103        synchronized (mToastQueue) {
2104            int index = indexOfToastLocked(record.pkg, record.callback);
2105            if (index >= 0) {
2106                cancelToastLocked(index);
2107            }
2108        }
2109    }
2110
2111    // lock on mToastQueue
2112    int indexOfToastLocked(String pkg, ITransientNotification callback)
2113    {
2114        IBinder cbak = callback.asBinder();
2115        ArrayList<ToastRecord> list = mToastQueue;
2116        int len = list.size();
2117        for (int i=0; i<len; i++) {
2118            ToastRecord r = list.get(i);
2119            if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) {
2120                return i;
2121            }
2122        }
2123        return -1;
2124    }
2125
2126    // lock on mToastQueue
2127    void keepProcessAliveLocked(int pid)
2128    {
2129        int toastCount = 0; // toasts from this pid
2130        ArrayList<ToastRecord> list = mToastQueue;
2131        int N = list.size();
2132        for (int i=0; i<N; i++) {
2133            ToastRecord r = list.get(i);
2134            if (r.pid == pid) {
2135                toastCount++;
2136            }
2137        }
2138        try {
2139            mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0);
2140        } catch (RemoteException e) {
2141            // Shouldn't happen.
2142        }
2143    }
2144
2145    private void handleRankingReconsideration(Message message) {
2146        if (!(message.obj instanceof RankingReconsideration)) return;
2147        RankingReconsideration recon = (RankingReconsideration) message.obj;
2148        recon.run();
2149        boolean changed;
2150        synchronized (mNotificationList) {
2151            final NotificationRecord record = mNotificationsByKey.get(recon.getKey());
2152            if (record == null) {
2153                return;
2154            }
2155            int indexBefore = findNotificationRecordIndexLocked(record);
2156            boolean interceptBefore = record.isIntercepted();
2157            int visibilityBefore = record.getPackageVisibilityOverride();
2158            recon.applyChangesLocked(record);
2159            applyZenModeLocked(record);
2160            mRankingHelper.sort(mNotificationList);
2161            int indexAfter = findNotificationRecordIndexLocked(record);
2162            boolean interceptAfter = record.isIntercepted();
2163            int visibilityAfter = record.getPackageVisibilityOverride();
2164            changed = indexBefore != indexAfter || interceptBefore != interceptAfter
2165                    || visibilityBefore != visibilityAfter;
2166            if (interceptBefore && !interceptAfter) {
2167                buzzBeepBlinkLocked(record);
2168            }
2169        }
2170        if (changed) {
2171            scheduleSendRankingUpdate();
2172        }
2173    }
2174
2175    private void handleRankingConfigChange() {
2176        synchronized (mNotificationList) {
2177            final int N = mNotificationList.size();
2178            ArrayList<String> orderBefore = new ArrayList<String>(N);
2179            int[] visibilities = new int[N];
2180            for (int i = 0; i < N; i++) {
2181                final NotificationRecord r = mNotificationList.get(i);
2182                orderBefore.add(r.getKey());
2183                visibilities[i] = r.getPackageVisibilityOverride();
2184                mRankingHelper.extractSignals(r);
2185            }
2186            for (int i = 0; i < N; i++) {
2187                mRankingHelper.sort(mNotificationList);
2188                final NotificationRecord r = mNotificationList.get(i);
2189                if (!orderBefore.get(i).equals(r.getKey())
2190                        || visibilities[i] != r.getPackageVisibilityOverride()) {
2191                    scheduleSendRankingUpdate();
2192                    return;
2193                }
2194            }
2195        }
2196    }
2197
2198    // let zen mode evaluate this record
2199    private void applyZenModeLocked(NotificationRecord record) {
2200        record.setIntercepted(mZenModeHelper.shouldIntercept(record));
2201    }
2202
2203    // lock on mNotificationList
2204    private int findNotificationRecordIndexLocked(NotificationRecord target) {
2205        return mRankingHelper.indexOf(mNotificationList, target);
2206    }
2207
2208    private void scheduleSendRankingUpdate() {
2209        mHandler.removeMessages(MESSAGE_SEND_RANKING_UPDATE);
2210        Message m = Message.obtain(mHandler, MESSAGE_SEND_RANKING_UPDATE);
2211        mHandler.sendMessage(m);
2212    }
2213
2214    private void handleSendRankingUpdate() {
2215        synchronized (mNotificationList) {
2216            mListeners.notifyRankingUpdateLocked();
2217        }
2218    }
2219
2220    private void scheduleListenerHintsChanged(int state) {
2221        mHandler.removeMessages(MESSAGE_LISTENER_HINTS_CHANGED);
2222        mHandler.obtainMessage(MESSAGE_LISTENER_HINTS_CHANGED, state, 0).sendToTarget();
2223    }
2224
2225    private void scheduleInterruptionFilterChanged(int listenerInterruptionFilter) {
2226        mHandler.removeMessages(MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED);
2227        mHandler.obtainMessage(
2228                MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED,
2229                listenerInterruptionFilter,
2230                0).sendToTarget();
2231    }
2232
2233    private void handleListenerHintsChanged(int hints) {
2234        synchronized (mNotificationList) {
2235            mListeners.notifyListenerHintsChangedLocked(hints);
2236        }
2237    }
2238
2239    private void handleListenerInterruptionFilterChanged(int interruptionFilter) {
2240        synchronized (mNotificationList) {
2241            mListeners.notifyInterruptionFilterChanged(interruptionFilter);
2242        }
2243    }
2244
2245    private final class WorkerHandler extends Handler
2246    {
2247        @Override
2248        public void handleMessage(Message msg)
2249        {
2250            switch (msg.what)
2251            {
2252                case MESSAGE_TIMEOUT:
2253                    handleTimeout((ToastRecord)msg.obj);
2254                    break;
2255                case MESSAGE_SAVE_POLICY_FILE:
2256                    handleSavePolicyFile();
2257                    break;
2258                case MESSAGE_SEND_RANKING_UPDATE:
2259                    handleSendRankingUpdate();
2260                    break;
2261                case MESSAGE_LISTENER_HINTS_CHANGED:
2262                    handleListenerHintsChanged(msg.arg1);
2263                    break;
2264                case MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED:
2265                    handleListenerInterruptionFilterChanged(msg.arg1);
2266                    break;
2267            }
2268        }
2269
2270    }
2271
2272    private final class RankingWorkerHandler extends Handler
2273    {
2274        public RankingWorkerHandler(Looper looper) {
2275            super(looper);
2276        }
2277
2278        @Override
2279        public void handleMessage(Message msg) {
2280            switch (msg.what) {
2281                case MESSAGE_RECONSIDER_RANKING:
2282                    handleRankingReconsideration(msg);
2283                    break;
2284                case MESSAGE_RANKING_CONFIG_CHANGE:
2285                    handleRankingConfigChange();
2286                    break;
2287            }
2288        }
2289    }
2290
2291    // Notifications
2292    // ============================================================================
2293    static int clamp(int x, int low, int high) {
2294        return (x < low) ? low : ((x > high) ? high : x);
2295    }
2296
2297    void sendAccessibilityEvent(Notification notification, CharSequence packageName) {
2298        AccessibilityManager manager = AccessibilityManager.getInstance(getContext());
2299        if (!manager.isEnabled()) {
2300            return;
2301        }
2302
2303        AccessibilityEvent event =
2304            AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
2305        event.setPackageName(packageName);
2306        event.setClassName(Notification.class.getName());
2307        event.setParcelableData(notification);
2308        CharSequence tickerText = notification.tickerText;
2309        if (!TextUtils.isEmpty(tickerText)) {
2310            event.getText().add(tickerText);
2311        }
2312
2313        manager.sendAccessibilityEvent(event);
2314    }
2315
2316    private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason) {
2317        // tell the app
2318        if (sendDelete) {
2319            if (r.getNotification().deleteIntent != null) {
2320                try {
2321                    r.getNotification().deleteIntent.send();
2322                } catch (PendingIntent.CanceledException ex) {
2323                    // do nothing - there's no relevant way to recover, and
2324                    //     no reason to let this propagate
2325                    Slog.w(TAG, "canceled PendingIntent for " + r.sbn.getPackageName(), ex);
2326                }
2327            }
2328        }
2329
2330        // status bar
2331        if (r.getNotification().icon != 0) {
2332            r.isCanceled = true;
2333            mListeners.notifyRemovedLocked(r.sbn);
2334        }
2335
2336        final String canceledKey = r.getKey();
2337
2338        // sound
2339        if (canceledKey.equals(mSoundNotificationKey)) {
2340            mSoundNotificationKey = null;
2341            final long identity = Binder.clearCallingIdentity();
2342            try {
2343                final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
2344                if (player != null) {
2345                    player.stopAsync();
2346                }
2347            } catch (RemoteException e) {
2348            } finally {
2349                Binder.restoreCallingIdentity(identity);
2350            }
2351        }
2352
2353        // vibrate
2354        if (canceledKey.equals(mVibrateNotificationKey)) {
2355            mVibrateNotificationKey = null;
2356            long identity = Binder.clearCallingIdentity();
2357            try {
2358                mVibrator.cancel();
2359            }
2360            finally {
2361                Binder.restoreCallingIdentity(identity);
2362            }
2363        }
2364
2365        // light
2366        mLights.remove(canceledKey);
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(canceledKey, 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        NotificationRecord ledNotification = null;
2600        while (ledNotification == null && !mLights.isEmpty()) {
2601            final String owner = mLights.get(mLights.size() - 1);
2602            ledNotification = mNotificationsByKey.get(owner);
2603            if (ledNotification == null) {
2604                Slog.wtfStack(TAG, "LED Notification does not exist: " + owner);
2605                mLights.remove(owner);
2606            }
2607        }
2608
2609        // Don't flash while we are in a call or screen is on
2610        if (ledNotification == null || mInCall || mScreenOn) {
2611            mNotificationLight.turnOff();
2612            mStatusBar.notificationLightOff();
2613        } else {
2614            final Notification ledno = ledNotification.sbn.getNotification();
2615            int ledARGB = ledno.ledARGB;
2616            int ledOnMS = ledno.ledOnMS;
2617            int ledOffMS = ledno.ledOffMS;
2618            if ((ledno.defaults & Notification.DEFAULT_LIGHTS) != 0) {
2619                ledARGB = mDefaultNotificationColor;
2620                ledOnMS = mDefaultNotificationLedOn;
2621                ledOffMS = mDefaultNotificationLedOff;
2622            }
2623            if (mNotificationPulseEnabled) {
2624                // pulse repeatedly
2625                mNotificationLight.setFlashing(ledARGB, Light.LIGHT_FLASH_TIMED,
2626                        ledOnMS, ledOffMS);
2627            }
2628            // let SystemUI make an independent decision
2629            mStatusBar.notificationLightPulse(ledARGB, ledOnMS, ledOffMS);
2630        }
2631    }
2632
2633    // lock on mNotificationList
2634    int indexOfNotificationLocked(String pkg, String tag, int id, int userId)
2635    {
2636        ArrayList<NotificationRecord> list = mNotificationList;
2637        final int len = list.size();
2638        for (int i=0; i<len; i++) {
2639            NotificationRecord r = list.get(i);
2640            if (!notificationMatchesUserId(r, userId) || r.sbn.getId() != id) {
2641                continue;
2642            }
2643            if (tag == null) {
2644                if (r.sbn.getTag() != null) {
2645                    continue;
2646                }
2647            } else {
2648                if (!tag.equals(r.sbn.getTag())) {
2649                    continue;
2650                }
2651            }
2652            if (r.sbn.getPackageName().equals(pkg)) {
2653                return i;
2654            }
2655        }
2656        return -1;
2657    }
2658
2659    // lock on mNotificationList
2660    int indexOfNotificationLocked(String key) {
2661        final int N = mNotificationList.size();
2662        for (int i = 0; i < N; i++) {
2663            if (key.equals(mNotificationList.get(i).getKey())) {
2664                return i;
2665            }
2666        }
2667        return -1;
2668    }
2669
2670    private void updateNotificationPulse() {
2671        synchronized (mNotificationList) {
2672            updateLightsLocked();
2673        }
2674    }
2675
2676    private static boolean isUidSystem(int uid) {
2677        final int appid = UserHandle.getAppId(uid);
2678        return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0);
2679    }
2680
2681    private static boolean isCallerSystem() {
2682        return isUidSystem(Binder.getCallingUid());
2683    }
2684
2685    private static void checkCallerIsSystem() {
2686        if (isCallerSystem()) {
2687            return;
2688        }
2689        throw new SecurityException("Disallowed call for uid " + Binder.getCallingUid());
2690    }
2691
2692    private static void checkCallerIsSystemOrSameApp(String pkg) {
2693        if (isCallerSystem()) {
2694            return;
2695        }
2696        final int uid = Binder.getCallingUid();
2697        try {
2698            ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo(
2699                    pkg, 0, UserHandle.getCallingUserId());
2700            if (ai == null) {
2701                throw new SecurityException("Unknown package " + pkg);
2702            }
2703            if (!UserHandle.isSameApp(ai.uid, uid)) {
2704                throw new SecurityException("Calling uid " + uid + " gave package"
2705                        + pkg + " which is owned by uid " + ai.uid);
2706            }
2707        } catch (RemoteException re) {
2708            throw new SecurityException("Unknown package " + pkg + "\n" + re);
2709        }
2710    }
2711
2712    private static String callStateToString(int state) {
2713        switch (state) {
2714            case TelephonyManager.CALL_STATE_IDLE: return "CALL_STATE_IDLE";
2715            case TelephonyManager.CALL_STATE_RINGING: return "CALL_STATE_RINGING";
2716            case TelephonyManager.CALL_STATE_OFFHOOK: return "CALL_STATE_OFFHOOK";
2717            default: return "CALL_STATE_UNKNOWN_" + state;
2718        }
2719    }
2720
2721    private void listenForCallState() {
2722        TelephonyManager.from(getContext()).listen(new PhoneStateListener() {
2723            @Override
2724            public void onCallStateChanged(int state, String incomingNumber) {
2725                if (mCallState == state) return;
2726                if (DBG) Slog.d(TAG, "Call state changed: " + callStateToString(state));
2727                mCallState = state;
2728            }
2729        }, PhoneStateListener.LISTEN_CALL_STATE);
2730    }
2731
2732    /**
2733     * Generates a NotificationRankingUpdate from 'sbns', considering only
2734     * notifications visible to the given listener.
2735     *
2736     * <p>Caller must hold a lock on mNotificationList.</p>
2737     */
2738    private NotificationRankingUpdate makeRankingUpdateLocked(ManagedServiceInfo info) {
2739        int speedBumpIndex = -1;
2740        final int N = mNotificationList.size();
2741        ArrayList<String> keys = new ArrayList<String>(N);
2742        ArrayList<String> interceptedKeys = new ArrayList<String>(N);
2743        Bundle visibilityOverrides = new Bundle();
2744        for (int i = 0; i < N; i++) {
2745            NotificationRecord record = mNotificationList.get(i);
2746            if (!isVisibleToListener(record.sbn, info)) {
2747                continue;
2748            }
2749            keys.add(record.sbn.getKey());
2750            if (record.isIntercepted()) {
2751                interceptedKeys.add(record.sbn.getKey());
2752            }
2753            if (record.getPackageVisibilityOverride()
2754                    != NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE) {
2755                visibilityOverrides.putInt(record.sbn.getKey(),
2756                        record.getPackageVisibilityOverride());
2757            }
2758            // Find first min-prio notification for speedbump placement.
2759            if (speedBumpIndex == -1 &&
2760                    // Intrusiveness trumps priority, hence ignore intrusives.
2761                    !record.isRecentlyIntrusive() &&
2762                    // Currently, package priority is either PRIORITY_DEFAULT or PRIORITY_MAX, so
2763                    // scanning for PRIORITY_MIN within the package bucket PRIORITY_DEFAULT
2764                    // (or lower as a safeguard) is sufficient to find the speedbump index.
2765                    // We'll have to revisit this when more package priority buckets are introduced.
2766                    record.getPackagePriority() <= Notification.PRIORITY_DEFAULT &&
2767                    record.sbn.getNotification().priority == Notification.PRIORITY_MIN) {
2768                speedBumpIndex = keys.size() - 1;
2769            }
2770        }
2771        String[] keysAr = keys.toArray(new String[keys.size()]);
2772        String[] interceptedKeysAr = interceptedKeys.toArray(new String[interceptedKeys.size()]);
2773        return new NotificationRankingUpdate(keysAr, interceptedKeysAr, visibilityOverrides,
2774                speedBumpIndex);
2775    }
2776
2777    private boolean isVisibleToListener(StatusBarNotification sbn, ManagedServiceInfo listener) {
2778        if (!listener.enabledAndUserMatches(sbn.getUserId())) {
2779            return false;
2780        }
2781        // TODO: remove this for older listeners.
2782        return true;
2783    }
2784
2785    public class NotificationListeners extends ManagedServices {
2786
2787        private final ArraySet<ManagedServiceInfo> mLightTrimListeners = new ArraySet<>();
2788
2789        public NotificationListeners() {
2790            super(getContext(), mHandler, mNotificationList, mUserProfiles);
2791        }
2792
2793        @Override
2794        protected Config getConfig() {
2795            Config c = new Config();
2796            c.caption = "notification listener";
2797            c.serviceInterface = NotificationListenerService.SERVICE_INTERFACE;
2798            c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_LISTENERS;
2799            c.bindPermission = android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE;
2800            c.settingsAction = Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS;
2801            c.clientLabel = R.string.notification_listener_binding_label;
2802            return c;
2803        }
2804
2805        @Override
2806        protected IInterface asInterface(IBinder binder) {
2807            return INotificationListener.Stub.asInterface(binder);
2808        }
2809
2810        @Override
2811        public void onServiceAdded(ManagedServiceInfo info) {
2812            final INotificationListener listener = (INotificationListener) info.service;
2813            final NotificationRankingUpdate update;
2814            synchronized (mNotificationList) {
2815                update = makeRankingUpdateLocked(info);
2816            }
2817            try {
2818                listener.onListenerConnected(update);
2819            } catch (RemoteException e) {
2820                // we tried
2821            }
2822        }
2823
2824        @Override
2825        protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
2826            if (mListenersDisablingEffects.remove(removed)) {
2827                updateListenerHintsLocked();
2828            }
2829            mLightTrimListeners.remove(removed);
2830        }
2831
2832        public void setOnNotificationPostedTrimLocked(ManagedServiceInfo info, int trim) {
2833            if (trim == TRIM_LIGHT) {
2834                mLightTrimListeners.add(info);
2835            } else {
2836                mLightTrimListeners.remove(info);
2837            }
2838        }
2839
2840        public int getOnNotificationPostedTrim(ManagedServiceInfo info) {
2841            return mLightTrimListeners.contains(info) ? TRIM_LIGHT : TRIM_FULL;
2842
2843        }
2844
2845        /**
2846         * asynchronously notify all listeners about a new notification
2847         *
2848         * <p>
2849         * Also takes care of removing a notification that has been visible to a listener before,
2850         * but isn't anymore.
2851         */
2852        public void notifyPostedLocked(StatusBarNotification sbn, StatusBarNotification oldSbn) {
2853            // Lazily initialized snapshots of the notification.
2854            StatusBarNotification sbnClone = null;
2855            StatusBarNotification sbnCloneLight = null;
2856
2857            for (final ManagedServiceInfo info : mServices) {
2858                boolean sbnVisible = isVisibleToListener(sbn, info);
2859                boolean oldSbnVisible = oldSbn != null ? isVisibleToListener(oldSbn, info) : false;
2860                // This notification hasn't been and still isn't visible -> ignore.
2861                if (!oldSbnVisible && !sbnVisible) {
2862                    continue;
2863                }
2864                final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
2865
2866                // This notification became invisible -> remove the old one.
2867                if (oldSbnVisible && !sbnVisible) {
2868                    final StatusBarNotification oldSbnLightClone = oldSbn.cloneLight();
2869                    mHandler.post(new Runnable() {
2870                        @Override
2871                        public void run() {
2872                            notifyRemoved(info, oldSbnLightClone, update);
2873                        }
2874                    });
2875                    continue;
2876                }
2877
2878                final int trim = mListeners.getOnNotificationPostedTrim(info);
2879
2880                if (trim == TRIM_LIGHT && sbnCloneLight == null) {
2881                    sbnCloneLight = sbn.cloneLight();
2882                } else if (trim == TRIM_FULL && sbnClone == null) {
2883                    sbnClone = sbn.clone();
2884                }
2885                final StatusBarNotification sbnToPost =
2886                        (trim == TRIM_FULL) ? sbnClone : sbnCloneLight;
2887
2888                mHandler.post(new Runnable() {
2889                    @Override
2890                    public void run() {
2891                        notifyPosted(info, sbnToPost, update);
2892                    }
2893                });
2894            }
2895        }
2896
2897        /**
2898         * asynchronously notify all listeners about a removed notification
2899         */
2900        public void notifyRemovedLocked(StatusBarNotification sbn) {
2901            // make a copy in case changes are made to the underlying Notification object
2902            // NOTE: this copy is lightweight: it doesn't include heavyweight parts of the
2903            // notification
2904            final StatusBarNotification sbnLight = sbn.cloneLight();
2905            for (final ManagedServiceInfo info : mServices) {
2906                if (!isVisibleToListener(sbn, info)) {
2907                    continue;
2908                }
2909                final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
2910                mHandler.post(new Runnable() {
2911                    @Override
2912                    public void run() {
2913                        notifyRemoved(info, sbnLight, update);
2914                    }
2915                });
2916            }
2917        }
2918
2919        /**
2920         * asynchronously notify all listeners about a reordering of notifications
2921         */
2922        public void notifyRankingUpdateLocked() {
2923            for (final ManagedServiceInfo serviceInfo : mServices) {
2924                if (!serviceInfo.isEnabledForCurrentProfiles()) {
2925                    continue;
2926                }
2927                final NotificationRankingUpdate update = makeRankingUpdateLocked(serviceInfo);
2928                mHandler.post(new Runnable() {
2929                    @Override
2930                    public void run() {
2931                        notifyRankingUpdate(serviceInfo, update);
2932                    }
2933                });
2934            }
2935        }
2936
2937        public void notifyListenerHintsChangedLocked(final int hints) {
2938            for (final ManagedServiceInfo serviceInfo : mServices) {
2939                if (!serviceInfo.isEnabledForCurrentProfiles()) {
2940                    continue;
2941                }
2942                mHandler.post(new Runnable() {
2943                    @Override
2944                    public void run() {
2945                        notifyListenerHintsChanged(serviceInfo, hints);
2946                    }
2947                });
2948            }
2949        }
2950
2951        public void notifyInterruptionFilterChanged(final int interruptionFilter) {
2952            for (final ManagedServiceInfo serviceInfo : mServices) {
2953                if (!serviceInfo.isEnabledForCurrentProfiles()) {
2954                    continue;
2955                }
2956                mHandler.post(new Runnable() {
2957                    @Override
2958                    public void run() {
2959                        notifyInterruptionFilterChanged(serviceInfo, interruptionFilter);
2960                    }
2961                });
2962            }
2963        }
2964
2965        private void notifyPosted(final ManagedServiceInfo info,
2966                final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) {
2967            final INotificationListener listener = (INotificationListener)info.service;
2968            StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
2969            try {
2970                listener.onNotificationPosted(sbnHolder, rankingUpdate);
2971            } catch (RemoteException ex) {
2972                Log.e(TAG, "unable to notify listener (posted): " + listener, ex);
2973            }
2974        }
2975
2976        private void notifyRemoved(ManagedServiceInfo info, StatusBarNotification sbn,
2977                NotificationRankingUpdate rankingUpdate) {
2978            if (!info.enabledAndUserMatches(sbn.getUserId())) {
2979                return;
2980            }
2981            final INotificationListener listener = (INotificationListener) info.service;
2982            StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
2983            try {
2984                listener.onNotificationRemoved(sbnHolder, rankingUpdate);
2985            } catch (RemoteException ex) {
2986                Log.e(TAG, "unable to notify listener (removed): " + listener, ex);
2987            }
2988        }
2989
2990        private void notifyRankingUpdate(ManagedServiceInfo info,
2991                                         NotificationRankingUpdate rankingUpdate) {
2992            final INotificationListener listener = (INotificationListener) info.service;
2993            try {
2994                listener.onNotificationRankingUpdate(rankingUpdate);
2995            } catch (RemoteException ex) {
2996                Log.e(TAG, "unable to notify listener (ranking update): " + listener, ex);
2997            }
2998        }
2999
3000        private void notifyListenerHintsChanged(ManagedServiceInfo info, int hints) {
3001            final INotificationListener listener = (INotificationListener) info.service;
3002            try {
3003                listener.onListenerHintsChanged(hints);
3004            } catch (RemoteException ex) {
3005                Log.e(TAG, "unable to notify listener (listener hints): " + listener, ex);
3006            }
3007        }
3008
3009        private void notifyInterruptionFilterChanged(ManagedServiceInfo info,
3010                int interruptionFilter) {
3011            final INotificationListener listener = (INotificationListener) info.service;
3012            try {
3013                listener.onInterruptionFilterChanged(interruptionFilter);
3014            } catch (RemoteException ex) {
3015                Log.e(TAG, "unable to notify listener (interruption filter): " + listener, ex);
3016            }
3017        }
3018
3019        private boolean isListenerPackage(String packageName) {
3020            if (packageName == null) {
3021                return false;
3022            }
3023            // TODO: clean up locking object later
3024            synchronized (mNotificationList) {
3025                for (final ManagedServiceInfo serviceInfo : mServices) {
3026                    if (packageName.equals(serviceInfo.component.getPackageName())) {
3027                        return true;
3028                    }
3029                }
3030            }
3031            return false;
3032        }
3033    }
3034
3035    public static final class DumpFilter {
3036        public String pkgFilter;
3037        public boolean zen;
3038
3039        public static DumpFilter parseFromArguments(String[] args) {
3040            if (args != null && args.length == 2 && "p".equals(args[0])
3041                    && args[1] != null && !args[1].trim().isEmpty()) {
3042                final DumpFilter filter = new DumpFilter();
3043                filter.pkgFilter = args[1].trim().toLowerCase();
3044                return filter;
3045            }
3046            if (args != null && args.length == 1 && "zen".equals(args[0])) {
3047                final DumpFilter filter = new DumpFilter();
3048                filter.zen = true;
3049                return filter;
3050            }
3051            return null;
3052        }
3053
3054        public boolean matches(StatusBarNotification sbn) {
3055            return zen ? true : sbn != null
3056                    && (matches(sbn.getPackageName()) || matches(sbn.getOpPkg()));
3057        }
3058
3059        public boolean matches(ComponentName component) {
3060            return zen ? true : component != null && matches(component.getPackageName());
3061        }
3062
3063        public boolean matches(String pkg) {
3064            return zen ? true : pkg != null && pkg.toLowerCase().contains(pkgFilter);
3065        }
3066
3067        @Override
3068        public String toString() {
3069            return zen ? "zen" : ('\'' + pkgFilter + '\'');
3070        }
3071    }
3072
3073    /**
3074     * Wrapper for a StatusBarNotification object that allows transfer across a oneway
3075     * binder without sending large amounts of data over a oneway transaction.
3076     */
3077    private static final class StatusBarNotificationHolder
3078            extends IStatusBarNotificationHolder.Stub {
3079        private StatusBarNotification mValue;
3080
3081        public StatusBarNotificationHolder(StatusBarNotification value) {
3082            mValue = value;
3083        }
3084
3085        /** Get the held value and clear it. This function should only be called once per holder */
3086        @Override
3087        public StatusBarNotification get() {
3088            StatusBarNotification value = mValue;
3089            mValue = null;
3090            return value;
3091        }
3092    }
3093}
3094