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