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