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