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