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