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