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