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