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