NotificationManagerService.java revision cad5768e22e5132efb2e5179f4988a763c7a8d9e
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        return new AudioAttributes.Builder().setLegacyStreamType(n.audioStreamType).build();
1816    }
1817
1818    void showNextToastLocked() {
1819        ToastRecord record = mToastQueue.get(0);
1820        while (record != null) {
1821            if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback);
1822            try {
1823                record.callback.show();
1824                scheduleTimeoutLocked(record);
1825                return;
1826            } catch (RemoteException e) {
1827                Slog.w(TAG, "Object died trying to show notification " + record.callback
1828                        + " in package " + record.pkg);
1829                // remove it from the list and let the process die
1830                int index = mToastQueue.indexOf(record);
1831                if (index >= 0) {
1832                    mToastQueue.remove(index);
1833                }
1834                keepProcessAliveLocked(record.pid);
1835                if (mToastQueue.size() > 0) {
1836                    record = mToastQueue.get(0);
1837                } else {
1838                    record = null;
1839                }
1840            }
1841        }
1842    }
1843
1844    void cancelToastLocked(int index) {
1845        ToastRecord record = mToastQueue.get(index);
1846        try {
1847            record.callback.hide();
1848        } catch (RemoteException e) {
1849            Slog.w(TAG, "Object died trying to hide notification " + record.callback
1850                    + " in package " + record.pkg);
1851            // don't worry about this, we're about to remove it from
1852            // the list anyway
1853        }
1854        mToastQueue.remove(index);
1855        keepProcessAliveLocked(record.pid);
1856        if (mToastQueue.size() > 0) {
1857            // Show the next one. If the callback fails, this will remove
1858            // it from the list, so don't assume that the list hasn't changed
1859            // after this point.
1860            showNextToastLocked();
1861        }
1862    }
1863
1864    private void scheduleTimeoutLocked(ToastRecord r)
1865    {
1866        mHandler.removeCallbacksAndMessages(r);
1867        Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r);
1868        long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY;
1869        mHandler.sendMessageDelayed(m, delay);
1870    }
1871
1872    private void handleTimeout(ToastRecord record)
1873    {
1874        if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback);
1875        synchronized (mToastQueue) {
1876            int index = indexOfToastLocked(record.pkg, record.callback);
1877            if (index >= 0) {
1878                cancelToastLocked(index);
1879            }
1880        }
1881    }
1882
1883    // lock on mToastQueue
1884    int indexOfToastLocked(String pkg, ITransientNotification callback)
1885    {
1886        IBinder cbak = callback.asBinder();
1887        ArrayList<ToastRecord> list = mToastQueue;
1888        int len = list.size();
1889        for (int i=0; i<len; i++) {
1890            ToastRecord r = list.get(i);
1891            if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) {
1892                return i;
1893            }
1894        }
1895        return -1;
1896    }
1897
1898    // lock on mToastQueue
1899    void keepProcessAliveLocked(int pid)
1900    {
1901        int toastCount = 0; // toasts from this pid
1902        ArrayList<ToastRecord> list = mToastQueue;
1903        int N = list.size();
1904        for (int i=0; i<N; i++) {
1905            ToastRecord r = list.get(i);
1906            if (r.pid == pid) {
1907                toastCount++;
1908            }
1909        }
1910        try {
1911            mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0);
1912        } catch (RemoteException e) {
1913            // Shouldn't happen.
1914        }
1915    }
1916
1917    private void handleRankingReconsideration(Message message) {
1918        if (!(message.obj instanceof RankingReconsideration)) return;
1919        RankingReconsideration recon = (RankingReconsideration) message.obj;
1920        recon.run();
1921        boolean changed;
1922        synchronized (mNotificationList) {
1923            final NotificationRecord record = mNotificationsByKey.get(recon.getKey());
1924            if (record == null) {
1925                return;
1926            }
1927            int indexBefore = findNotificationRecordIndexLocked(record);
1928            boolean interceptBefore = record.isIntercepted();
1929            recon.applyChangesLocked(record);
1930            applyZenModeLocked(record);
1931            mRankingHelper.sort(mNotificationList);
1932            int indexAfter = findNotificationRecordIndexLocked(record);
1933            boolean interceptAfter = record.isIntercepted();
1934            changed = indexBefore != indexAfter || interceptBefore != interceptAfter;
1935            if (interceptBefore && !interceptAfter) {
1936                buzzBeepBlinkLocked(record);
1937            }
1938        }
1939        if (changed) {
1940            scheduleSendRankingUpdate();
1941        }
1942    }
1943
1944    private void handleRankingConfigChange() {
1945        synchronized (mNotificationList) {
1946            final int N = mNotificationList.size();
1947            ArrayList<String> orderBefore = new ArrayList<String>(N);
1948            for (int i = 0; i < N; i++) {
1949                final NotificationRecord r = mNotificationList.get(i);
1950                orderBefore.add(r.getKey());
1951                mRankingHelper.extractSignals(r);
1952            }
1953            mRankingHelper.sort(mNotificationList);
1954            for (int i = 0; i < N; i++) {
1955                if (!orderBefore.get(i).equals(mNotificationList.get(i).getKey())) {
1956                    scheduleSendRankingUpdate();
1957                    return;
1958                }
1959            }
1960        }
1961    }
1962
1963    // let zen mode evaluate this record
1964    private void applyZenModeLocked(NotificationRecord record) {
1965        record.setIntercepted(mZenModeHelper.shouldIntercept(record));
1966    }
1967
1968    // lock on mNotificationList
1969    private int findNotificationRecordIndexLocked(NotificationRecord target) {
1970        return mRankingHelper.indexOf(mNotificationList, target);
1971    }
1972
1973    private void scheduleSendRankingUpdate() {
1974        mHandler.removeMessages(MESSAGE_SEND_RANKING_UPDATE);
1975        Message m = Message.obtain(mHandler, MESSAGE_SEND_RANKING_UPDATE);
1976        mHandler.sendMessage(m);
1977    }
1978
1979    private void handleSendRankingUpdate() {
1980        synchronized (mNotificationList) {
1981            mListeners.notifyRankingUpdateLocked();
1982        }
1983    }
1984
1985    private void scheduleListenerFlagsChanged(int state) {
1986        mHandler.removeMessages(MESSAGE_LISTENER_FLAGS_CHANGED);
1987        mHandler.obtainMessage(MESSAGE_LISTENER_FLAGS_CHANGED, state, 0).sendToTarget();
1988    }
1989
1990    private void handleListenerFlagsChanged(int state) {
1991        synchronized (mNotificationList) {
1992            mListeners.notifyListenerFlagsChangedLocked(state);
1993        }
1994    }
1995
1996    private final class WorkerHandler extends Handler
1997    {
1998        @Override
1999        public void handleMessage(Message msg)
2000        {
2001            switch (msg.what)
2002            {
2003                case MESSAGE_TIMEOUT:
2004                    handleTimeout((ToastRecord)msg.obj);
2005                    break;
2006                case MESSAGE_SAVE_POLICY_FILE:
2007                    handleSavePolicyFile();
2008                    break;
2009                case MESSAGE_SEND_RANKING_UPDATE:
2010                    handleSendRankingUpdate();
2011                    break;
2012                case MESSAGE_LISTENER_FLAGS_CHANGED:
2013                    handleListenerFlagsChanged(msg.arg1);
2014                    break;
2015            }
2016        }
2017
2018    }
2019
2020    private final class RankingWorkerHandler extends Handler
2021    {
2022        public RankingWorkerHandler(Looper looper) {
2023            super(looper);
2024        }
2025
2026        @Override
2027        public void handleMessage(Message msg) {
2028            switch (msg.what) {
2029                case MESSAGE_RECONSIDER_RANKING:
2030                    handleRankingReconsideration(msg);
2031                    break;
2032                case MESSAGE_RANKING_CONFIG_CHANGE:
2033                    handleRankingConfigChange();
2034                    break;
2035            }
2036        }
2037    }
2038
2039    // Notifications
2040    // ============================================================================
2041    static int clamp(int x, int low, int high) {
2042        return (x < low) ? low : ((x > high) ? high : x);
2043    }
2044
2045    void sendAccessibilityEvent(Notification notification, CharSequence packageName) {
2046        AccessibilityManager manager = AccessibilityManager.getInstance(getContext());
2047        if (!manager.isEnabled()) {
2048            return;
2049        }
2050
2051        AccessibilityEvent event =
2052            AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
2053        event.setPackageName(packageName);
2054        event.setClassName(Notification.class.getName());
2055        event.setParcelableData(notification);
2056        CharSequence tickerText = notification.tickerText;
2057        if (!TextUtils.isEmpty(tickerText)) {
2058            event.getText().add(tickerText);
2059        }
2060
2061        manager.sendAccessibilityEvent(event);
2062    }
2063
2064    private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason) {
2065        // tell the app
2066        if (sendDelete) {
2067            if (r.getNotification().deleteIntent != null) {
2068                try {
2069                    r.getNotification().deleteIntent.send();
2070                } catch (PendingIntent.CanceledException ex) {
2071                    // do nothing - there's no relevant way to recover, and
2072                    //     no reason to let this propagate
2073                    Slog.w(TAG, "canceled PendingIntent for " + r.sbn.getPackageName(), ex);
2074                }
2075            }
2076        }
2077
2078        // status bar
2079        if (r.getNotification().icon != 0) {
2080            r.isCanceled = true;
2081            mListeners.notifyRemovedLocked(r.sbn);
2082        }
2083
2084        // sound
2085        if (mSoundNotification == r) {
2086            mSoundNotification = null;
2087            final long identity = Binder.clearCallingIdentity();
2088            try {
2089                final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
2090                if (player != null) {
2091                    player.stopAsync();
2092                }
2093            } catch (RemoteException e) {
2094            } finally {
2095                Binder.restoreCallingIdentity(identity);
2096            }
2097        }
2098
2099        // vibrate
2100        if (mVibrateNotification == r) {
2101            mVibrateNotification = null;
2102            long identity = Binder.clearCallingIdentity();
2103            try {
2104                mVibrator.cancel();
2105            }
2106            finally {
2107                Binder.restoreCallingIdentity(identity);
2108            }
2109        }
2110
2111        // light
2112        mLights.remove(r.getKey());
2113        if (mLedNotification == r) {
2114            mLedNotification = null;
2115        }
2116
2117        // Record usage stats
2118        switch (reason) {
2119            case REASON_DELEGATE_CANCEL:
2120            case REASON_DELEGATE_CANCEL_ALL:
2121            case REASON_LISTENER_CANCEL:
2122            case REASON_LISTENER_CANCEL_ALL:
2123                mUsageStats.registerDismissedByUser(r);
2124                break;
2125            case REASON_NOMAN_CANCEL:
2126            case REASON_NOMAN_CANCEL_ALL:
2127                mUsageStats.registerRemovedByApp(r);
2128                break;
2129            case REASON_DELEGATE_CLICK:
2130                mUsageStats.registerCancelDueToClick(r);
2131                break;
2132            default:
2133                mUsageStats.registerCancelUnknown(r);
2134                break;
2135        }
2136
2137        // Save it for users of getHistoricalNotifications()
2138        mArchive.record(r.sbn);
2139    }
2140
2141    /**
2142     * Cancels a notification ONLY if it has all of the {@code mustHaveFlags}
2143     * and none of the {@code mustNotHaveFlags}.
2144     */
2145    void cancelNotification(final int callingUid, final int callingPid,
2146            final String pkg, final String tag, final int id,
2147            final int mustHaveFlags, final int mustNotHaveFlags, final boolean sendDelete,
2148            final int userId, final int reason, final ManagedServiceInfo listener) {
2149        // In enqueueNotificationInternal notifications are added by scheduling the
2150        // work on the worker handler. Hence, we also schedule the cancel on this
2151        // handler to avoid a scenario where an add notification call followed by a
2152        // remove notification call ends up in not removing the notification.
2153        mHandler.post(new Runnable() {
2154            @Override
2155            public void run() {
2156                String listenerName = listener == null ? null : listener.component.toShortString();
2157                EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, id, tag, userId,
2158                        mustHaveFlags, mustNotHaveFlags, reason, listenerName);
2159
2160                synchronized (mNotificationList) {
2161                    int index = indexOfNotificationLocked(pkg, tag, id, userId);
2162                    if (index >= 0) {
2163                        NotificationRecord r = mNotificationList.get(index);
2164
2165                        // Ideally we'd do this in the caller of this method. However, that would
2166                        // require the caller to also find the notification.
2167                        if (reason == REASON_DELEGATE_CLICK) {
2168                            mUsageStats.registerClickedByUser(r);
2169                        }
2170
2171                        if ((r.getNotification().flags & mustHaveFlags) != mustHaveFlags) {
2172                            return;
2173                        }
2174                        if ((r.getNotification().flags & mustNotHaveFlags) != 0) {
2175                            return;
2176                        }
2177
2178                        mNotificationList.remove(index);
2179                        mNotificationsByKey.remove(r.sbn.getKey());
2180
2181                        cancelNotificationLocked(r, sendDelete, reason);
2182                        cancelGroupChildrenLocked(r, callingUid, callingPid, listenerName);
2183                        updateLightsLocked();
2184                    }
2185                }
2186            }
2187        });
2188    }
2189
2190    /**
2191     * Determine whether the userId applies to the notification in question, either because
2192     * they match exactly, or one of them is USER_ALL (which is treated as a wildcard).
2193     */
2194    private boolean notificationMatchesUserId(NotificationRecord r, int userId) {
2195        return
2196                // looking for USER_ALL notifications? match everything
2197                   userId == UserHandle.USER_ALL
2198                // a notification sent to USER_ALL matches any query
2199                || r.getUserId() == UserHandle.USER_ALL
2200                // an exact user match
2201                || r.getUserId() == userId;
2202    }
2203
2204    /**
2205     * Determine whether the userId applies to the notification in question, either because
2206     * they match exactly, or one of them is USER_ALL (which is treated as a wildcard) or
2207     * because it matches one of the users profiles.
2208     */
2209    private boolean notificationMatchesCurrentProfiles(NotificationRecord r, int userId) {
2210        return notificationMatchesUserId(r, userId)
2211                || mUserProfiles.isCurrentProfile(r.getUserId());
2212    }
2213
2214    /**
2215     * Cancels all notifications from a given package that have all of the
2216     * {@code mustHaveFlags}.
2217     */
2218    boolean cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, int mustHaveFlags,
2219            int mustNotHaveFlags, boolean doit, int userId, int reason,
2220            ManagedServiceInfo listener) {
2221        String listenerName = listener == null ? null : listener.component.toShortString();
2222        EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
2223                pkg, userId, mustHaveFlags, mustNotHaveFlags, reason,
2224                listenerName);
2225
2226        synchronized (mNotificationList) {
2227            final int N = mNotificationList.size();
2228            ArrayList<NotificationRecord> canceledNotifications = null;
2229            for (int i = N-1; i >= 0; --i) {
2230                NotificationRecord r = mNotificationList.get(i);
2231                if (!notificationMatchesUserId(r, userId)) {
2232                    continue;
2233                }
2234                // Don't remove notifications to all, if there's no package name specified
2235                if (r.getUserId() == UserHandle.USER_ALL && pkg == null) {
2236                    continue;
2237                }
2238                if ((r.getFlags() & mustHaveFlags) != mustHaveFlags) {
2239                    continue;
2240                }
2241                if ((r.getFlags() & mustNotHaveFlags) != 0) {
2242                    continue;
2243                }
2244                if (pkg != null && !r.sbn.getPackageName().equals(pkg)) {
2245                    continue;
2246                }
2247                if (canceledNotifications == null) {
2248                    canceledNotifications = new ArrayList<>();
2249                }
2250                canceledNotifications.add(r);
2251                if (!doit) {
2252                    return true;
2253                }
2254                mNotificationList.remove(i);
2255                mNotificationsByKey.remove(r.sbn.getKey());
2256                cancelNotificationLocked(r, false, reason);
2257            }
2258            if (doit && canceledNotifications != null) {
2259                final int M = canceledNotifications.size();
2260                for (int i = 0; i < M; i++) {
2261                    cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid,
2262                            listenerName);
2263                }
2264            }
2265            if (canceledNotifications != null) {
2266                updateLightsLocked();
2267            }
2268            return canceledNotifications != null;
2269        }
2270    }
2271
2272    void cancelAllLocked(int callingUid, int callingPid, int userId, int reason,
2273            ManagedServiceInfo listener, boolean includeCurrentProfiles) {
2274        String listenerName = listener == null ? null : listener.component.toShortString();
2275        EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
2276                null, userId, 0, 0, reason, listenerName);
2277
2278        ArrayList<NotificationRecord> canceledNotifications = null;
2279        final int N = mNotificationList.size();
2280        for (int i=N-1; i>=0; i--) {
2281            NotificationRecord r = mNotificationList.get(i);
2282            if (includeCurrentProfiles) {
2283                if (!notificationMatchesCurrentProfiles(r, userId)) {
2284                    continue;
2285                }
2286            } else {
2287                if (!notificationMatchesUserId(r, userId)) {
2288                    continue;
2289                }
2290            }
2291
2292            if ((r.getFlags() & (Notification.FLAG_ONGOING_EVENT
2293                            | Notification.FLAG_NO_CLEAR)) == 0) {
2294                mNotificationList.remove(i);
2295                mNotificationsByKey.remove(r.sbn.getKey());
2296                cancelNotificationLocked(r, true, reason);
2297                // Make a note so we can cancel children later.
2298                if (canceledNotifications == null) {
2299                    canceledNotifications = new ArrayList<>();
2300                }
2301                canceledNotifications.add(r);
2302            }
2303        }
2304        int M = canceledNotifications != null ? canceledNotifications.size() : 0;
2305        for (int i = 0; i < M; i++) {
2306            cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid,
2307                    listenerName);
2308        }
2309        updateLightsLocked();
2310    }
2311
2312    // Warning: The caller is responsible for invoking updateLightsLocked().
2313    private void cancelGroupChildrenLocked(NotificationRecord r, int callingUid, int callingPid,
2314            String listenerName) {
2315        Notification n = r.getNotification();
2316        if (n.getGroup() == null || (n.flags & Notification.FLAG_GROUP_SUMMARY) == 0) {
2317            return;
2318        }
2319
2320        String pkg = r.sbn.getPackageName();
2321        int userId = r.getUserId();
2322
2323        if (pkg == null) {
2324            if (DBG) Log.e(TAG, "No package for group summary: " + r.getKey());
2325            return;
2326        }
2327
2328        final int N = mNotificationList.size();
2329        for (int i = N - 1; i >= 0; i--) {
2330            NotificationRecord childR = mNotificationList.get(i);
2331            Notification childN = childR.getNotification();
2332            StatusBarNotification childSbn = childR.sbn;
2333            if (childR.getUserId() == userId && pkg.equals(childSbn.getPackageName()) &&
2334                    n.getGroup().equals(childN.getGroup())) {
2335                EventLogTags.writeNotificationCancel(callingUid, callingPid,
2336                        pkg, childSbn.getId(), childSbn.getTag(), userId, 0, 0,
2337                        REASON_GROUP_SUMMARY_CANCELED, listenerName);
2338                mNotificationList.remove(i);
2339                mNotificationsByKey.remove(childR.getKey());
2340                cancelNotificationLocked(childR, false, REASON_GROUP_SUMMARY_CANCELED);
2341            }
2342        }
2343    }
2344
2345    // lock on mNotificationList
2346    void updateLightsLocked()
2347    {
2348        // handle notification lights
2349        if (mLedNotification == null) {
2350            // get next notification, if any
2351            int n = mLights.size();
2352            if (n > 0) {
2353                mLedNotification = mNotificationsByKey.get(mLights.get(n-1));
2354            }
2355        }
2356
2357        // Don't flash while we are in a call or screen is on
2358        if (mLedNotification == null || mInCall || mScreenOn) {
2359            mNotificationLight.turnOff();
2360        } else {
2361            final Notification ledno = mLedNotification.sbn.getNotification();
2362            int ledARGB = ledno.ledARGB;
2363            int ledOnMS = ledno.ledOnMS;
2364            int ledOffMS = ledno.ledOffMS;
2365            if ((ledno.defaults & Notification.DEFAULT_LIGHTS) != 0) {
2366                ledARGB = mDefaultNotificationColor;
2367                ledOnMS = mDefaultNotificationLedOn;
2368                ledOffMS = mDefaultNotificationLedOff;
2369            }
2370            if (mNotificationPulseEnabled) {
2371                // pulse repeatedly
2372                mNotificationLight.setFlashing(ledARGB, Light.LIGHT_FLASH_TIMED,
2373                        ledOnMS, ledOffMS);
2374            }
2375        }
2376    }
2377
2378    // lock on mNotificationList
2379    int indexOfNotificationLocked(String pkg, String tag, int id, int userId)
2380    {
2381        ArrayList<NotificationRecord> list = mNotificationList;
2382        final int len = list.size();
2383        for (int i=0; i<len; i++) {
2384            NotificationRecord r = list.get(i);
2385            if (!notificationMatchesUserId(r, userId) || r.sbn.getId() != id) {
2386                continue;
2387            }
2388            if (tag == null) {
2389                if (r.sbn.getTag() != null) {
2390                    continue;
2391                }
2392            } else {
2393                if (!tag.equals(r.sbn.getTag())) {
2394                    continue;
2395                }
2396            }
2397            if (r.sbn.getPackageName().equals(pkg)) {
2398                return i;
2399            }
2400        }
2401        return -1;
2402    }
2403
2404    // lock on mNotificationList
2405    int indexOfNotificationLocked(String key) {
2406        final int N = mNotificationList.size();
2407        for (int i = 0; i < N; i++) {
2408            if (key.equals(mNotificationList.get(i).getKey())) {
2409                return i;
2410            }
2411        }
2412        return -1;
2413    }
2414
2415    private void updateNotificationPulse() {
2416        synchronized (mNotificationList) {
2417            updateLightsLocked();
2418        }
2419    }
2420
2421    private static boolean isUidSystem(int uid) {
2422        final int appid = UserHandle.getAppId(uid);
2423        return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0);
2424    }
2425
2426    private static boolean isCallerSystem() {
2427        return isUidSystem(Binder.getCallingUid());
2428    }
2429
2430    private static void checkCallerIsSystem() {
2431        if (isCallerSystem()) {
2432            return;
2433        }
2434        throw new SecurityException("Disallowed call for uid " + Binder.getCallingUid());
2435    }
2436
2437    private static void checkCallerIsSystemOrSameApp(String pkg) {
2438        if (isCallerSystem()) {
2439            return;
2440        }
2441        final int uid = Binder.getCallingUid();
2442        try {
2443            ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo(
2444                    pkg, 0, UserHandle.getCallingUserId());
2445            if (ai == null) {
2446                throw new SecurityException("Unknown package " + pkg);
2447            }
2448            if (!UserHandle.isSameApp(ai.uid, uid)) {
2449                throw new SecurityException("Calling uid " + uid + " gave package"
2450                        + pkg + " which is owned by uid " + ai.uid);
2451            }
2452        } catch (RemoteException re) {
2453            throw new SecurityException("Unknown package " + pkg + "\n" + re);
2454        }
2455    }
2456
2457    /**
2458     * Generates a NotificationRankingUpdate from 'sbns', considering only
2459     * notifications visible to the given listener.
2460     *
2461     * <p>Caller must hold a lock on mNotificationList.</p>
2462     */
2463    private NotificationRankingUpdate makeRankingUpdateLocked(ManagedServiceInfo info) {
2464        int speedBumpIndex = -1;
2465        final int N = mNotificationList.size();
2466        ArrayList<String> keys = new ArrayList<String>(N);
2467        ArrayList<String> interceptedKeys = new ArrayList<String>(N);
2468        for (int i = 0; i < N; i++) {
2469            NotificationRecord record = mNotificationList.get(i);
2470            if (!info.enabledAndUserMatches(record.sbn.getUserId())) {
2471                continue;
2472            }
2473            keys.add(record.sbn.getKey());
2474            if (record.isIntercepted()) {
2475                interceptedKeys.add(record.sbn.getKey());
2476            }
2477            if (speedBumpIndex == -1 &&
2478                    record.sbn.getNotification().priority == Notification.PRIORITY_MIN) {
2479                speedBumpIndex = keys.size() - 1;
2480            }
2481        }
2482        String[] keysAr = keys.toArray(new String[keys.size()]);
2483        String[] interceptedKeysAr = interceptedKeys.toArray(new String[interceptedKeys.size()]);
2484        return new NotificationRankingUpdate(keysAr, interceptedKeysAr, speedBumpIndex);
2485    }
2486
2487    public class NotificationListeners extends ManagedServices {
2488
2489        public NotificationListeners() {
2490            super(getContext(), mHandler, mNotificationList, mUserProfiles);
2491        }
2492
2493        @Override
2494        protected Config getConfig() {
2495            Config c = new Config();
2496            c.caption = "notification listener";
2497            c.serviceInterface = NotificationListenerService.SERVICE_INTERFACE;
2498            c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_LISTENERS;
2499            c.bindPermission = android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE;
2500            c.settingsAction = Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS;
2501            c.clientLabel = R.string.notification_listener_binding_label;
2502            return c;
2503        }
2504
2505        @Override
2506        protected IInterface asInterface(IBinder binder) {
2507            return INotificationListener.Stub.asInterface(binder);
2508        }
2509
2510        @Override
2511        public void onServiceAdded(ManagedServiceInfo info) {
2512            final INotificationListener listener = (INotificationListener) info.service;
2513            final NotificationRankingUpdate update;
2514            synchronized (mNotificationList) {
2515                update = makeRankingUpdateLocked(info);
2516            }
2517            try {
2518                listener.onListenerConnected(update);
2519            } catch (RemoteException e) {
2520                // we tried
2521            }
2522        }
2523
2524        @Override
2525        protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
2526            if (mListenersDisablingAlerts.remove(removed)) {
2527                updateListenerFlagsLocked();
2528            }
2529        }
2530
2531        /**
2532         * asynchronously notify all listeners about a new notification
2533         */
2534        public void notifyPostedLocked(StatusBarNotification sbn) {
2535            // make a copy in case changes are made to the underlying Notification object
2536            final StatusBarNotification sbnClone = sbn.clone();
2537            for (final ManagedServiceInfo info : mServices) {
2538                if (!info.isEnabledForCurrentProfiles()) {
2539                    continue;
2540                }
2541                final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
2542                if (update.getOrderedKeys().length == 0) {
2543                    continue;
2544                }
2545                mHandler.post(new Runnable() {
2546                    @Override
2547                    public void run() {
2548                        notifyPostedIfUserMatch(info, sbnClone, update);
2549                    }
2550                });
2551            }
2552        }
2553
2554        /**
2555         * asynchronously notify all listeners about a removed notification
2556         */
2557        public void notifyRemovedLocked(StatusBarNotification sbn) {
2558            // make a copy in case changes are made to the underlying Notification object
2559            // NOTE: this copy is lightweight: it doesn't include heavyweight parts of the
2560            // notification
2561            final StatusBarNotification sbnLight = sbn.cloneLight();
2562            for (final ManagedServiceInfo info : mServices) {
2563                if (!info.isEnabledForCurrentProfiles()) {
2564                    continue;
2565                }
2566                final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
2567                mHandler.post(new Runnable() {
2568                    @Override
2569                    public void run() {
2570                        notifyRemovedIfUserMatch(info, sbnLight, update);
2571                    }
2572                });
2573            }
2574        }
2575
2576        /**
2577         * asynchronously notify all listeners about a reordering of notifications
2578         */
2579        public void notifyRankingUpdateLocked() {
2580            for (final ManagedServiceInfo serviceInfo : mServices) {
2581                if (!serviceInfo.isEnabledForCurrentProfiles()) {
2582                    continue;
2583                }
2584                final NotificationRankingUpdate update =
2585                        makeRankingUpdateLocked(serviceInfo);
2586                mHandler.post(new Runnable() {
2587                    @Override
2588                    public void run() {
2589                        notifyRankingUpdate(serviceInfo, update);
2590                    }
2591                });
2592            }
2593        }
2594
2595        public void notifyListenerFlagsChangedLocked(final int flags) {
2596            for (final ManagedServiceInfo serviceInfo : mServices) {
2597                if (!serviceInfo.isEnabledForCurrentProfiles()) {
2598                    continue;
2599                }
2600                mHandler.post(new Runnable() {
2601                    @Override
2602                    public void run() {
2603                        notifyListenerFlagsChanged(serviceInfo, flags);
2604                    }
2605                });
2606            }
2607        }
2608
2609        private void notifyPostedIfUserMatch(final ManagedServiceInfo info,
2610                final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) {
2611            if (!info.enabledAndUserMatches(sbn.getUserId())) {
2612                return;
2613            }
2614            final INotificationListener listener = (INotificationListener)info.service;
2615            try {
2616                listener.onNotificationPosted(sbn, rankingUpdate);
2617            } catch (RemoteException ex) {
2618                Log.e(TAG, "unable to notify listener (posted): " + listener, ex);
2619            }
2620        }
2621
2622        private void notifyRemovedIfUserMatch(ManagedServiceInfo info, StatusBarNotification sbn,
2623                NotificationRankingUpdate rankingUpdate) {
2624            if (!info.enabledAndUserMatches(sbn.getUserId())) {
2625                return;
2626            }
2627            final INotificationListener listener = (INotificationListener) info.service;
2628            try {
2629                listener.onNotificationRemoved(sbn, rankingUpdate);
2630            } catch (RemoteException ex) {
2631                Log.e(TAG, "unable to notify listener (removed): " + listener, ex);
2632            }
2633        }
2634
2635        private void notifyRankingUpdate(ManagedServiceInfo info,
2636                                         NotificationRankingUpdate rankingUpdate) {
2637            final INotificationListener listener = (INotificationListener) info.service;
2638            try {
2639                listener.onNotificationRankingUpdate(rankingUpdate);
2640            } catch (RemoteException ex) {
2641                Log.e(TAG, "unable to notify listener (ranking update): " + listener, ex);
2642            }
2643        }
2644
2645        private void notifyListenerFlagsChanged(ManagedServiceInfo info, int state) {
2646            final INotificationListener listener = (INotificationListener) info.service;
2647            try {
2648                listener.onListenerFlagsChanged(state);
2649            } catch (RemoteException ex) {
2650                Log.e(TAG, "unable to notify listener (listener flags): " + listener, ex);
2651            }
2652        }
2653    }
2654
2655    public static final class DumpFilter {
2656        public String pkgFilter;
2657        public boolean zen;
2658
2659        public static DumpFilter parseFromArguments(String[] args) {
2660            if (args != null && args.length == 2 && "p".equals(args[0])
2661                    && args[1] != null && !args[1].trim().isEmpty()) {
2662                final DumpFilter filter = new DumpFilter();
2663                filter.pkgFilter = args[1].trim().toLowerCase();
2664                return filter;
2665            }
2666            if (args != null && args.length == 1 && "zen".equals(args[0])) {
2667                final DumpFilter filter = new DumpFilter();
2668                filter.zen = true;
2669                return filter;
2670            }
2671            return null;
2672        }
2673
2674        public boolean matches(StatusBarNotification sbn) {
2675            return zen ? true : sbn != null
2676                    && (matches(sbn.getPackageName()) || matches(sbn.getOpPkg()));
2677        }
2678
2679        public boolean matches(ComponentName component) {
2680            return zen ? true : component != null && matches(component.getPackageName());
2681        }
2682
2683        public boolean matches(String pkg) {
2684            return zen ? true : pkg != null && pkg.toLowerCase().contains(pkgFilter);
2685        }
2686
2687        @Override
2688        public String toString() {
2689            return zen ? "zen" : ('\'' + pkgFilter + '\'');
2690        }
2691    }
2692}
2693