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