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