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