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