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