NotificationManagerService.java revision a344656a010dc3c88aef39109f1ac459792e7607
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<String> mLights = new ArrayList<String>();
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            enforceSystemOrSystemUI("INotificationManager.getZenModeConfig");
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 [-20..20]
1482                final int score = notification.priority * NOTIFICATION_PRIORITY_MULTIPLIER;
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, score);
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                        r.score = JUNK_SCORE;
1511                        Slog.e(TAG, "Suppressing notification from package " + pkg
1512                                + " by user request.");
1513                    }
1514                }
1515
1516                if (r.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                        r.isUpdate = true;
1535                    }
1536                    mNotificationsByKey.put(n.getKey(), r);
1537
1538                    applyZenModeLocked(r);
1539
1540                    Collections.sort(mNotificationList, mRankingComparator);
1541
1542                    final int currentUser;
1543                    final long token = Binder.clearCallingIdentity();
1544                    try {
1545                        currentUser = ActivityManager.getCurrentUser();
1546                    } finally {
1547                        Binder.restoreCallingIdentity(token);
1548                    }
1549
1550                    if (notification.icon != 0) {
1551                        if (old != null && !old.isCanceled) {
1552                            final long identity = Binder.clearCallingIdentity();
1553                            try {
1554                                mStatusBar.updateNotification(n);
1555                            } finally {
1556                                Binder.restoreCallingIdentity(identity);
1557                            }
1558                        } else {
1559                            final long identity = Binder.clearCallingIdentity();
1560                            try {
1561                                mStatusBar.addNotification(n);
1562                            } finally {
1563                                Binder.restoreCallingIdentity(identity);
1564                            }
1565                        }
1566                        mListeners.notifyPostedLocked(n);
1567                    } else {
1568                        Slog.e(TAG, "Not posting notification with icon==0: " + notification);
1569                        if (old != null && !old.isCanceled) {
1570                            final long identity = Binder.clearCallingIdentity();
1571                            try {
1572                                mStatusBar.removeNotification(r.getKey());
1573                            } finally {
1574                                Binder.restoreCallingIdentity(identity);
1575                            }
1576
1577                            mListeners.notifyRemovedLocked(n);
1578                        }
1579                        // ATTENTION: in a future release we will bail out here
1580                        // so that we do not play sounds, show lights, etc. for invalid
1581                        // notifications
1582                        Slog.e(TAG, "WARNING: In a future release this will crash the app: "
1583                                + n.getPackageName());
1584                    }
1585
1586                    // Ensure if this is a foreground service that the proper additional
1587                    // flags are set.
1588                    if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
1589                        notification.flags |= Notification.FLAG_ONGOING_EVENT
1590                                | Notification.FLAG_NO_CLEAR;
1591                    }
1592
1593                    buzzBeepBlinkLocked(r);
1594                }
1595            }
1596        });
1597
1598        idOut[0] = id;
1599    }
1600
1601    private void buzzBeepBlinkLocked(NotificationRecord record) {
1602        final Notification notification = record.sbn.getNotification();
1603
1604        // Should this notification make noise, vibe, or use the LED?
1605        final boolean canInterrupt = (record.score >= SCORE_INTERRUPTION_THRESHOLD) &&
1606                !record.isIntercepted();
1607        if (DBG || record.isIntercepted())
1608            Slog.v(TAG,
1609                    "pkg=" + record.sbn.getPackageName() + " canInterrupt=" + canInterrupt +
1610                            " intercept=" + record.isIntercepted()
1611            );
1612
1613        final int currentUser;
1614        final long token = Binder.clearCallingIdentity();
1615        try {
1616            currentUser = ActivityManager.getCurrentUser();
1617        } finally {
1618            Binder.restoreCallingIdentity(token);
1619        }
1620
1621        // If we're not supposed to beep, vibrate, etc. then don't.
1622        if (!mDisableNotificationAlerts
1623                && (!(record.isUpdate
1624                    && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 ))
1625                && (record.getUserId() == UserHandle.USER_ALL ||
1626                    record.getUserId() == currentUser ||
1627                    mUserProfiles.isCurrentProfile(record.getUserId()))
1628                && canInterrupt
1629                && mSystemReady
1630                && mAudioManager != null) {
1631            if (DBG) Slog.v(TAG, "Interrupting!");
1632
1633            sendAccessibilityEvent(notification, record.sbn.getPackageName());
1634
1635            // sound
1636
1637            // should we use the default notification sound? (indicated either by
1638            // DEFAULT_SOUND or because notification.sound is pointing at
1639            // Settings.System.NOTIFICATION_SOUND)
1640            final boolean useDefaultSound =
1641                   (notification.defaults & Notification.DEFAULT_SOUND) != 0 ||
1642                           Settings.System.DEFAULT_NOTIFICATION_URI
1643                                   .equals(notification.sound);
1644
1645            Uri soundUri = null;
1646            boolean hasValidSound = false;
1647
1648            if (useDefaultSound) {
1649                soundUri = Settings.System.DEFAULT_NOTIFICATION_URI;
1650
1651                // check to see if the default notification sound is silent
1652                ContentResolver resolver = getContext().getContentResolver();
1653                hasValidSound = Settings.System.getString(resolver,
1654                       Settings.System.NOTIFICATION_SOUND) != null;
1655            } else if (notification.sound != null) {
1656                soundUri = notification.sound;
1657                hasValidSound = (soundUri != null);
1658            }
1659
1660            if (hasValidSound) {
1661                boolean looping =
1662                        (notification.flags & Notification.FLAG_INSISTENT) != 0;
1663                int audioStreamType;
1664                if (notification.audioStreamType >= 0) {
1665                    audioStreamType = notification.audioStreamType;
1666                } else {
1667                    audioStreamType = DEFAULT_STREAM_TYPE;
1668                }
1669                mSoundNotification = record;
1670                // do not play notifications if stream volume is 0 (typically because
1671                // ringer mode is silent) or if there is a user of exclusive audio focus
1672                if ((mAudioManager.getStreamVolume(audioStreamType) != 0)
1673                        && !mAudioManager.isAudioFocusExclusive()) {
1674                    final long identity = Binder.clearCallingIdentity();
1675                    try {
1676                        final IRingtonePlayer player =
1677                                mAudioManager.getRingtonePlayer();
1678                        if (player != null) {
1679                            if (DBG) Slog.v(TAG, "Playing sound " + soundUri
1680                                    + " on stream " + audioStreamType);
1681                            player.playAsync(soundUri, record.sbn.getUser(), looping,
1682                                    audioStreamType);
1683                        }
1684                    } catch (RemoteException e) {
1685                    } finally {
1686                        Binder.restoreCallingIdentity(identity);
1687                    }
1688                }
1689            }
1690
1691            // vibrate
1692            // Does the notification want to specify its own vibration?
1693            final boolean hasCustomVibrate = notification.vibrate != null;
1694
1695            // new in 4.2: if there was supposed to be a sound and we're in vibrate
1696            // mode, and no other vibration is specified, we fall back to vibration
1697            final boolean convertSoundToVibration =
1698                       !hasCustomVibrate
1699                    && hasValidSound
1700                    && (mAudioManager.getRingerMode()
1701                               == AudioManager.RINGER_MODE_VIBRATE);
1702
1703            // The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback.
1704            final boolean useDefaultVibrate =
1705                    (notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
1706
1707            if ((useDefaultVibrate || convertSoundToVibration || hasCustomVibrate)
1708                    && !(mAudioManager.getRingerMode()
1709                            == AudioManager.RINGER_MODE_SILENT)) {
1710                mVibrateNotification = record;
1711
1712                if (useDefaultVibrate || convertSoundToVibration) {
1713                    // Escalate privileges so we can use the vibrator even if the
1714                    // notifying app does not have the VIBRATE permission.
1715                    long identity = Binder.clearCallingIdentity();
1716                    try {
1717                        mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
1718                            useDefaultVibrate ? mDefaultVibrationPattern
1719                                : mFallbackVibrationPattern,
1720                            ((notification.flags & Notification.FLAG_INSISTENT) != 0)
1721                                    ? 0: -1, notification.audioStreamType);
1722                    } finally {
1723                        Binder.restoreCallingIdentity(identity);
1724                    }
1725                } else if (notification.vibrate.length > 1) {
1726                    // If you want your own vibration pattern, you need the VIBRATE
1727                    // permission
1728                    mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
1729                            notification.vibrate,
1730                        ((notification.flags & Notification.FLAG_INSISTENT) != 0)
1731                                ? 0: -1, notification.audioStreamType);
1732                }
1733            }
1734        }
1735
1736        // light
1737        // release the light
1738        boolean wasShowLights = mLights.remove(record.getKey());
1739        if (mLedNotification != null && record.getKey().equals(mLedNotification.getKey())) {
1740            mLedNotification = null;
1741        }
1742        if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0 && canInterrupt) {
1743            mLights.add(record.getKey());
1744            updateLightsLocked();
1745            mAttentionLight.pulse();
1746        } else if (wasShowLights) {
1747            updateLightsLocked();
1748        }
1749    }
1750
1751    void showNextToastLocked() {
1752        ToastRecord record = mToastQueue.get(0);
1753        while (record != null) {
1754            if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback);
1755            try {
1756                record.callback.show();
1757                scheduleTimeoutLocked(record);
1758                return;
1759            } catch (RemoteException e) {
1760                Slog.w(TAG, "Object died trying to show notification " + record.callback
1761                        + " in package " + record.pkg);
1762                // remove it from the list and let the process die
1763                int index = mToastQueue.indexOf(record);
1764                if (index >= 0) {
1765                    mToastQueue.remove(index);
1766                }
1767                keepProcessAliveLocked(record.pid);
1768                if (mToastQueue.size() > 0) {
1769                    record = mToastQueue.get(0);
1770                } else {
1771                    record = null;
1772                }
1773            }
1774        }
1775    }
1776
1777    void cancelToastLocked(int index) {
1778        ToastRecord record = mToastQueue.get(index);
1779        try {
1780            record.callback.hide();
1781        } catch (RemoteException e) {
1782            Slog.w(TAG, "Object died trying to hide notification " + record.callback
1783                    + " in package " + record.pkg);
1784            // don't worry about this, we're about to remove it from
1785            // the list anyway
1786        }
1787        mToastQueue.remove(index);
1788        keepProcessAliveLocked(record.pid);
1789        if (mToastQueue.size() > 0) {
1790            // Show the next one. If the callback fails, this will remove
1791            // it from the list, so don't assume that the list hasn't changed
1792            // after this point.
1793            showNextToastLocked();
1794        }
1795    }
1796
1797    private void scheduleTimeoutLocked(ToastRecord r)
1798    {
1799        mHandler.removeCallbacksAndMessages(r);
1800        Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r);
1801        long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY;
1802        mHandler.sendMessageDelayed(m, delay);
1803    }
1804
1805    private void handleTimeout(ToastRecord record)
1806    {
1807        if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback);
1808        synchronized (mToastQueue) {
1809            int index = indexOfToastLocked(record.pkg, record.callback);
1810            if (index >= 0) {
1811                cancelToastLocked(index);
1812            }
1813        }
1814    }
1815
1816    // lock on mToastQueue
1817    int indexOfToastLocked(String pkg, ITransientNotification callback)
1818    {
1819        IBinder cbak = callback.asBinder();
1820        ArrayList<ToastRecord> list = mToastQueue;
1821        int len = list.size();
1822        for (int i=0; i<len; i++) {
1823            ToastRecord r = list.get(i);
1824            if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) {
1825                return i;
1826            }
1827        }
1828        return -1;
1829    }
1830
1831    // lock on mToastQueue
1832    void keepProcessAliveLocked(int pid)
1833    {
1834        int toastCount = 0; // toasts from this pid
1835        ArrayList<ToastRecord> list = mToastQueue;
1836        int N = list.size();
1837        for (int i=0; i<N; i++) {
1838            ToastRecord r = list.get(i);
1839            if (r.pid == pid) {
1840                toastCount++;
1841            }
1842        }
1843        try {
1844            mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0);
1845        } catch (RemoteException e) {
1846            // Shouldn't happen.
1847        }
1848    }
1849
1850    private void scheduleRankingReconsideration(RankingReconsideration recon) {
1851        if (recon != null) {
1852            Message m = Message.obtain(mRankingHandler, MESSAGE_RECONSIDER_RANKING, recon);
1853            long delay = recon.getDelay(TimeUnit.MILLISECONDS);
1854            mRankingHandler.sendMessageDelayed(m, delay);
1855        }
1856    }
1857
1858    private void handleRankingReconsideration(Message message) {
1859        if (!(message.obj instanceof RankingReconsideration)) return;
1860        RankingReconsideration recon = (RankingReconsideration) message.obj;
1861        recon.run();
1862        boolean changed;
1863        synchronized (mNotificationList) {
1864            final NotificationRecord record = mNotificationsByKey.get(recon.getKey());
1865            if (record == null) {
1866                return;
1867            }
1868            int indexBefore = findNotificationRecordIndexLocked(record);
1869            boolean interceptBefore = record.isIntercepted();
1870            recon.applyChangesLocked(record);
1871            applyZenModeLocked(record);
1872            Collections.sort(mNotificationList, mRankingComparator);
1873            int indexAfter = findNotificationRecordIndexLocked(record);
1874            boolean interceptAfter = record.isIntercepted();
1875            changed = indexBefore != indexAfter || interceptBefore != interceptAfter;
1876            if (interceptBefore && !interceptAfter) {
1877                buzzBeepBlinkLocked(record);
1878            }
1879        }
1880        if (changed) {
1881            scheduleSendRankingUpdate();
1882        }
1883    }
1884
1885    // let zen mode evaluate this record and then make note of that for the future
1886    private void applyZenModeLocked(NotificationRecord record) {
1887        record.setIntercepted(mZenModeHelper.shouldIntercept(record, record.wasTouchedByZen()));
1888        record.setTouchedByZen();
1889    }
1890
1891    // lock on mNotificationList
1892    private int findNotificationRecordIndexLocked(NotificationRecord target) {
1893        return Collections.binarySearch(mNotificationList, target, mRankingComparator);
1894    }
1895
1896    private void scheduleSendRankingUpdate() {
1897        mHandler.removeMessages(MESSAGE_SEND_RANKING_UPDATE);
1898        Message m = Message.obtain(mHandler, MESSAGE_SEND_RANKING_UPDATE);
1899        mHandler.sendMessage(m);
1900    }
1901
1902    private void handleSendRankingUpdate() {
1903        synchronized (mNotificationList) {
1904            mListeners.notifyRankingUpdateLocked();
1905        }
1906    }
1907
1908    private final class WorkerHandler extends Handler
1909    {
1910        @Override
1911        public void handleMessage(Message msg)
1912        {
1913            switch (msg.what)
1914            {
1915                case MESSAGE_TIMEOUT:
1916                    handleTimeout((ToastRecord)msg.obj);
1917                    break;
1918                case MESSAGE_SAVE_POLICY_FILE:
1919                    handleSavePolicyFile();
1920                    break;
1921                case MESSAGE_SEND_RANKING_UPDATE:
1922                    handleSendRankingUpdate();
1923                    break;
1924            }
1925        }
1926
1927    }
1928
1929    private final class RankingWorkerHandler extends Handler
1930    {
1931        public RankingWorkerHandler(Looper looper) {
1932            super(looper);
1933        }
1934
1935        @Override
1936        public void handleMessage(Message msg) {
1937            switch (msg.what) {
1938                case MESSAGE_RECONSIDER_RANKING:
1939                    handleRankingReconsideration(msg);
1940                    break;
1941            }
1942        }
1943    }
1944
1945    // Notifications
1946    // ============================================================================
1947    static int clamp(int x, int low, int high) {
1948        return (x < low) ? low : ((x > high) ? high : x);
1949    }
1950
1951    void sendAccessibilityEvent(Notification notification, CharSequence packageName) {
1952        AccessibilityManager manager = AccessibilityManager.getInstance(getContext());
1953        if (!manager.isEnabled()) {
1954            return;
1955        }
1956
1957        AccessibilityEvent event =
1958            AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
1959        event.setPackageName(packageName);
1960        event.setClassName(Notification.class.getName());
1961        event.setParcelableData(notification);
1962        CharSequence tickerText = notification.tickerText;
1963        if (!TextUtils.isEmpty(tickerText)) {
1964            event.getText().add(tickerText);
1965        }
1966
1967        manager.sendAccessibilityEvent(event);
1968    }
1969
1970    private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason) {
1971        // tell the app
1972        if (sendDelete) {
1973            if (r.getNotification().deleteIntent != null) {
1974                try {
1975                    r.getNotification().deleteIntent.send();
1976                } catch (PendingIntent.CanceledException ex) {
1977                    // do nothing - there's no relevant way to recover, and
1978                    //     no reason to let this propagate
1979                    Slog.w(TAG, "canceled PendingIntent for " + r.sbn.getPackageName(), ex);
1980                }
1981            }
1982        }
1983
1984        // status bar
1985        if (r.getNotification().icon != 0) {
1986            final long identity = Binder.clearCallingIdentity();
1987            try {
1988                mStatusBar.removeNotification(r.getKey());
1989            } finally {
1990                Binder.restoreCallingIdentity(identity);
1991            }
1992            r.isCanceled = true;
1993            mListeners.notifyRemovedLocked(r.sbn);
1994        }
1995
1996        // sound
1997        if (mSoundNotification == r) {
1998            mSoundNotification = null;
1999            final long identity = Binder.clearCallingIdentity();
2000            try {
2001                final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
2002                if (player != null) {
2003                    player.stopAsync();
2004                }
2005            } catch (RemoteException e) {
2006            } finally {
2007                Binder.restoreCallingIdentity(identity);
2008            }
2009        }
2010
2011        // vibrate
2012        if (mVibrateNotification == r) {
2013            mVibrateNotification = null;
2014            long identity = Binder.clearCallingIdentity();
2015            try {
2016                mVibrator.cancel();
2017            }
2018            finally {
2019                Binder.restoreCallingIdentity(identity);
2020            }
2021        }
2022
2023        // light
2024        mLights.remove(r.getKey());
2025        if (mLedNotification == r) {
2026            mLedNotification = null;
2027        }
2028
2029        // Record usage stats
2030        switch (reason) {
2031            case REASON_DELEGATE_CANCEL:
2032            case REASON_DELEGATE_CANCEL_ALL:
2033            case REASON_LISTENER_CANCEL:
2034            case REASON_LISTENER_CANCEL_ALL:
2035                mUsageStats.registerDismissedByUser(r);
2036                break;
2037            case REASON_NOMAN_CANCEL:
2038            case REASON_NOMAN_CANCEL_ALL:
2039                mUsageStats.registerRemovedByApp(r);
2040                break;
2041            case REASON_DELEGATE_CLICK:
2042                mUsageStats.registerCancelDueToClick(r);
2043                break;
2044            default:
2045                mUsageStats.registerCancelUnknown(r);
2046                break;
2047        }
2048
2049        // Save it for users of getHistoricalNotifications()
2050        mArchive.record(r.sbn);
2051    }
2052
2053    /**
2054     * Cancels a notification ONLY if it has all of the {@code mustHaveFlags}
2055     * and none of the {@code mustNotHaveFlags}.
2056     */
2057    void cancelNotification(final int callingUid, final int callingPid,
2058            final String pkg, final String tag, final int id,
2059            final int mustHaveFlags, final int mustNotHaveFlags, final boolean sendDelete,
2060            final int userId, final int reason, final ManagedServiceInfo listener) {
2061        // In enqueueNotificationInternal notifications are added by scheduling the
2062        // work on the worker handler. Hence, we also schedule the cancel on this
2063        // handler to avoid a scenario where an add notification call followed by a
2064        // remove notification call ends up in not removing the notification.
2065        mHandler.post(new Runnable() {
2066            @Override
2067            public void run() {
2068                EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, id, tag, userId,
2069                        mustHaveFlags, mustNotHaveFlags, reason,
2070                        listener == null ? null : listener.component.toShortString());
2071
2072                synchronized (mNotificationList) {
2073                    int index = indexOfNotificationLocked(pkg, tag, id, userId);
2074                    if (index >= 0) {
2075                        NotificationRecord r = mNotificationList.get(index);
2076
2077                        // Ideally we'd do this in the caller of this method. However, that would
2078                        // require the caller to also find the notification.
2079                        if (reason == REASON_DELEGATE_CLICK) {
2080                            mUsageStats.registerClickedByUser(r);
2081                        }
2082
2083                        if ((r.getNotification().flags & mustHaveFlags) != mustHaveFlags) {
2084                            return;
2085                        }
2086                        if ((r.getNotification().flags & mustNotHaveFlags) != 0) {
2087                            return;
2088                        }
2089
2090                        mNotificationList.remove(index);
2091                        mNotificationsByKey.remove(r.sbn.getKey());
2092
2093                        cancelNotificationLocked(r, sendDelete, reason);
2094                        updateLightsLocked();
2095                    }
2096                }
2097            }
2098        });
2099    }
2100
2101    /**
2102     * Determine whether the userId applies to the notification in question, either because
2103     * they match exactly, or one of them is USER_ALL (which is treated as a wildcard).
2104     */
2105    private boolean notificationMatchesUserId(NotificationRecord r, int userId) {
2106        return
2107                // looking for USER_ALL notifications? match everything
2108                   userId == UserHandle.USER_ALL
2109                // a notification sent to USER_ALL matches any query
2110                || r.getUserId() == UserHandle.USER_ALL
2111                // an exact user match
2112                || r.getUserId() == userId;
2113    }
2114
2115    /**
2116     * Determine whether the userId applies to the notification in question, either because
2117     * they match exactly, or one of them is USER_ALL (which is treated as a wildcard) or
2118     * because it matches one of the users profiles.
2119     */
2120    private boolean notificationMatchesCurrentProfiles(NotificationRecord r, int userId) {
2121        return notificationMatchesUserId(r, userId)
2122                || mUserProfiles.isCurrentProfile(r.getUserId());
2123    }
2124
2125    /**
2126     * Cancels all notifications from a given package that have all of the
2127     * {@code mustHaveFlags}.
2128     */
2129    boolean cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, int mustHaveFlags,
2130            int mustNotHaveFlags, boolean doit, int userId, int reason,
2131            ManagedServiceInfo listener) {
2132        EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
2133                pkg, userId, mustHaveFlags, mustNotHaveFlags, reason,
2134                listener == null ? null : listener.component.toShortString());
2135
2136        synchronized (mNotificationList) {
2137            final int N = mNotificationList.size();
2138            boolean canceledSomething = false;
2139            for (int i = N-1; i >= 0; --i) {
2140                NotificationRecord r = mNotificationList.get(i);
2141                if (!notificationMatchesUserId(r, userId)) {
2142                    continue;
2143                }
2144                // Don't remove notifications to all, if there's no package name specified
2145                if (r.getUserId() == UserHandle.USER_ALL && pkg == null) {
2146                    continue;
2147                }
2148                if ((r.getFlags() & mustHaveFlags) != mustHaveFlags) {
2149                    continue;
2150                }
2151                if ((r.getFlags() & mustNotHaveFlags) != 0) {
2152                    continue;
2153                }
2154                if (pkg != null && !r.sbn.getPackageName().equals(pkg)) {
2155                    continue;
2156                }
2157                canceledSomething = true;
2158                if (!doit) {
2159                    return true;
2160                }
2161                mNotificationList.remove(i);
2162                mNotificationsByKey.remove(r.sbn.getKey());
2163                cancelNotificationLocked(r, false, reason);
2164            }
2165            if (canceledSomething) {
2166                updateLightsLocked();
2167            }
2168            return canceledSomething;
2169        }
2170    }
2171
2172    void cancelAllLocked(int callingUid, int callingPid, int userId, int reason,
2173            ManagedServiceInfo listener, boolean includeCurrentProfiles) {
2174        EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
2175                null, userId, 0, 0, reason,
2176                listener == null ? null : listener.component.toShortString());
2177
2178        final int N = mNotificationList.size();
2179        for (int i=N-1; i>=0; i--) {
2180            NotificationRecord r = mNotificationList.get(i);
2181            if (includeCurrentProfiles) {
2182                if (!notificationMatchesCurrentProfiles(r, userId)) {
2183                    continue;
2184                }
2185            } else {
2186                if (!notificationMatchesUserId(r, userId)) {
2187                    continue;
2188                }
2189            }
2190
2191            if ((r.getFlags() & (Notification.FLAG_ONGOING_EVENT
2192                            | Notification.FLAG_NO_CLEAR)) == 0) {
2193                mNotificationList.remove(i);
2194                mNotificationsByKey.remove(r.sbn.getKey());
2195                cancelNotificationLocked(r, true, reason);
2196            }
2197        }
2198        updateLightsLocked();
2199    }
2200
2201    // lock on mNotificationList
2202    void updateLightsLocked()
2203    {
2204        // handle notification lights
2205        if (mLedNotification == null) {
2206            // get next notification, if any
2207            int n = mLights.size();
2208            if (n > 0) {
2209                mLedNotification = mNotificationsByKey.get(mLights.get(n-1));
2210            }
2211        }
2212
2213        // Don't flash while we are in a call or screen is on
2214        if (mLedNotification == null || mInCall || mScreenOn) {
2215            mNotificationLight.turnOff();
2216        } else {
2217            final Notification ledno = mLedNotification.sbn.getNotification();
2218            int ledARGB = ledno.ledARGB;
2219            int ledOnMS = ledno.ledOnMS;
2220            int ledOffMS = ledno.ledOffMS;
2221            if ((ledno.defaults & Notification.DEFAULT_LIGHTS) != 0) {
2222                ledARGB = mDefaultNotificationColor;
2223                ledOnMS = mDefaultNotificationLedOn;
2224                ledOffMS = mDefaultNotificationLedOff;
2225            }
2226            if (mNotificationPulseEnabled) {
2227                // pulse repeatedly
2228                mNotificationLight.setFlashing(ledARGB, Light.LIGHT_FLASH_TIMED,
2229                        ledOnMS, ledOffMS);
2230            }
2231        }
2232    }
2233
2234    // lock on mNotificationList
2235    int indexOfNotificationLocked(String pkg, String tag, int id, int userId)
2236    {
2237        ArrayList<NotificationRecord> list = mNotificationList;
2238        final int len = list.size();
2239        for (int i=0; i<len; i++) {
2240            NotificationRecord r = list.get(i);
2241            if (!notificationMatchesUserId(r, userId) || r.sbn.getId() != id) {
2242                continue;
2243            }
2244            if (tag == null) {
2245                if (r.sbn.getTag() != null) {
2246                    continue;
2247                }
2248            } else {
2249                if (!tag.equals(r.sbn.getTag())) {
2250                    continue;
2251                }
2252            }
2253            if (r.sbn.getPackageName().equals(pkg)) {
2254                return i;
2255            }
2256        }
2257        return -1;
2258    }
2259
2260    // lock on mNotificationList
2261    int indexOfNotificationLocked(String key) {
2262        NotificationRecord r = mNotificationsByKey.get(key);
2263        if (r == null) {
2264            return -1;
2265        }
2266        int index = Collections.binarySearch(mNotificationList, r, mRankingComparator);
2267        // Guarantee to return -1 when not found.
2268        return (index >= 0) ? index : -1;
2269    }
2270
2271
2272    private void updateNotificationPulse() {
2273        synchronized (mNotificationList) {
2274            updateLightsLocked();
2275        }
2276    }
2277
2278    private static boolean isUidSystem(int uid) {
2279        final int appid = UserHandle.getAppId(uid);
2280        return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0);
2281    }
2282
2283    private static boolean isCallerSystem() {
2284        return isUidSystem(Binder.getCallingUid());
2285    }
2286
2287    private static void checkCallerIsSystem() {
2288        if (isCallerSystem()) {
2289            return;
2290        }
2291        throw new SecurityException("Disallowed call for uid " + Binder.getCallingUid());
2292    }
2293
2294    private static void checkCallerIsSystemOrSameApp(String pkg) {
2295        if (isCallerSystem()) {
2296            return;
2297        }
2298        final int uid = Binder.getCallingUid();
2299        try {
2300            ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo(
2301                    pkg, 0, UserHandle.getCallingUserId());
2302            if (!UserHandle.isSameApp(ai.uid, uid)) {
2303                throw new SecurityException("Calling uid " + uid + " gave package"
2304                        + pkg + " which is owned by uid " + ai.uid);
2305            }
2306        } catch (RemoteException re) {
2307            throw new SecurityException("Unknown package " + pkg + "\n" + re);
2308        }
2309    }
2310
2311    /**
2312     * Generates a NotificationRankingUpdate from 'sbns', considering only
2313     * notifications visible to the given listener.
2314     *
2315     * <p>Caller must hold a lock on mNotificationList.</p>
2316     */
2317    private NotificationRankingUpdate makeRankingUpdateLocked(ManagedServiceInfo info) {
2318        int speedBumpIndex = -1;
2319        final int N = mNotificationList.size();
2320        ArrayList<String> keys = new ArrayList<String>(N);
2321        ArrayList<String> dndKeys = new ArrayList<String>(N);
2322        for (int i = 0; i < N; i++) {
2323            NotificationRecord record = mNotificationList.get(i);
2324            if (!info.enabledAndUserMatches(record.sbn.getUserId())) {
2325                continue;
2326            }
2327            keys.add(record.sbn.getKey());
2328            if (record.isIntercepted()) {
2329                dndKeys.add(record.sbn.getKey());
2330            }
2331            if (speedBumpIndex == -1 &&
2332                    record.sbn.getNotification().priority == Notification.PRIORITY_MIN) {
2333                speedBumpIndex = keys.size() - 1;
2334            }
2335        }
2336        String[] keysAr = keys.toArray(new String[keys.size()]);
2337        String[] dndKeysAr = dndKeys.toArray(new String[dndKeys.size()]);
2338        return new NotificationRankingUpdate(keysAr, dndKeysAr, speedBumpIndex);
2339    }
2340
2341    public class NotificationListeners extends ManagedServices {
2342
2343        public NotificationListeners() {
2344            super(getContext(), mHandler, mNotificationList, mUserProfiles);
2345        }
2346
2347        @Override
2348        protected Config getConfig() {
2349            Config c = new Config();
2350            c.caption = "notification listener";
2351            c.serviceInterface = NotificationListenerService.SERVICE_INTERFACE;
2352            c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_LISTENERS;
2353            c.bindPermission = android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE;
2354            c.settingsAction = Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS;
2355            c.clientLabel = R.string.notification_listener_binding_label;
2356            return c;
2357        }
2358
2359        @Override
2360        protected IInterface asInterface(IBinder binder) {
2361            return INotificationListener.Stub.asInterface(binder);
2362        }
2363
2364        @Override
2365        public void onServiceAdded(ManagedServiceInfo info) {
2366            final INotificationListener listener = (INotificationListener) info.service;
2367            final NotificationRankingUpdate update;
2368            synchronized (mNotificationList) {
2369                update = makeRankingUpdateLocked(info);
2370            }
2371            try {
2372                listener.onListenerConnected(update);
2373            } catch (RemoteException e) {
2374                // we tried
2375            }
2376        }
2377
2378        /**
2379         * asynchronously notify all listeners about a new notification
2380         */
2381        public void notifyPostedLocked(StatusBarNotification sbn) {
2382            // make a copy in case changes are made to the underlying Notification object
2383            final StatusBarNotification sbnClone = sbn.clone();
2384            for (final ManagedServiceInfo info : mServices) {
2385                if (!info.isEnabledForCurrentProfiles()) {
2386                    continue;
2387                }
2388                final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
2389                if (update.getOrderedKeys().length == 0) {
2390                    continue;
2391                }
2392                mHandler.post(new Runnable() {
2393                    @Override
2394                    public void run() {
2395                        notifyPostedIfUserMatch(info, sbnClone, update);
2396                    }
2397                });
2398            }
2399        }
2400
2401        /**
2402         * asynchronously notify all listeners about a removed notification
2403         */
2404        public void notifyRemovedLocked(StatusBarNotification sbn) {
2405            // make a copy in case changes are made to the underlying Notification object
2406            // NOTE: this copy is lightweight: it doesn't include heavyweight parts of the
2407            // notification
2408            final StatusBarNotification sbnLight = sbn.cloneLight();
2409            for (final ManagedServiceInfo info : mServices) {
2410                if (!info.isEnabledForCurrentProfiles()) {
2411                    continue;
2412                }
2413                final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
2414                mHandler.post(new Runnable() {
2415                    @Override
2416                    public void run() {
2417                        notifyRemovedIfUserMatch(info, sbnLight, update);
2418                    }
2419                });
2420            }
2421        }
2422
2423        /**
2424         * asynchronously notify all listeners about a reordering of notifications
2425         */
2426        public void notifyRankingUpdateLocked() {
2427            for (final ManagedServiceInfo serviceInfo : mServices) {
2428                if (!serviceInfo.isEnabledForCurrentProfiles()) {
2429                    continue;
2430                }
2431                final NotificationRankingUpdate update =
2432                        makeRankingUpdateLocked(serviceInfo);
2433                mHandler.post(new Runnable() {
2434                    @Override
2435                    public void run() {
2436                        notifyRankingUpdate(serviceInfo, update);
2437                    }
2438                });
2439            }
2440        }
2441
2442        private void notifyPostedIfUserMatch(final ManagedServiceInfo info,
2443                final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) {
2444            if (!info.enabledAndUserMatches(sbn.getUserId())) {
2445                return;
2446            }
2447            final INotificationListener listener = (INotificationListener)info.service;
2448            try {
2449                listener.onNotificationPosted(sbn, rankingUpdate);
2450            } catch (RemoteException ex) {
2451                Log.e(TAG, "unable to notify listener (posted): " + listener, ex);
2452            }
2453        }
2454
2455        private void notifyRemovedIfUserMatch(ManagedServiceInfo info, StatusBarNotification sbn,
2456                NotificationRankingUpdate rankingUpdate) {
2457            if (!info.enabledAndUserMatches(sbn.getUserId())) {
2458                return;
2459            }
2460            final INotificationListener listener = (INotificationListener) info.service;
2461            try {
2462                listener.onNotificationRemoved(sbn, rankingUpdate);
2463            } catch (RemoteException ex) {
2464                Log.e(TAG, "unable to notify listener (removed): " + listener, ex);
2465            }
2466        }
2467
2468        private void notifyRankingUpdate(ManagedServiceInfo info,
2469                                         NotificationRankingUpdate rankingUpdate) {
2470            final INotificationListener listener = (INotificationListener) info.service;
2471            try {
2472                listener.onNotificationRankingUpdate(rankingUpdate);
2473            } catch (RemoteException ex) {
2474                Log.e(TAG, "unable to notify listener (ranking update): " + listener, ex);
2475            }
2476        }
2477    }
2478}
2479