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