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