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