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