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