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