NotificationManagerService.java revision 2a764949c943681a4d25a17a0b203a0127a4a486
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            synchronized (mNotificationList) {
970                // sound
971                mSoundNotification = null;
972
973                long identity = Binder.clearCallingIdentity();
974                try {
975                    final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
976                    if (player != null) {
977                        player.stopAsync();
978                    }
979                } catch (RemoteException e) {
980                } finally {
981                    Binder.restoreCallingIdentity(identity);
982                }
983
984                // vibrate
985                mVibrateNotification = null;
986                identity = Binder.clearCallingIdentity();
987                try {
988                    mVibrator.cancel();
989                } finally {
990                    Binder.restoreCallingIdentity(identity);
991                }
992
993                // light
994                mLights.clear();
995                mLedNotification = null;
996                updateLightsLocked();
997            }
998        }
999
1000        @Override
1001        public void onNotificationError(int callingUid, int callingPid, String pkg, String tag, int id,
1002                int uid, int initialPid, String message, int userId) {
1003            Slog.d(TAG, "onNotification error pkg=" + pkg + " tag=" + tag + " id=" + id
1004                    + "; will crashApplication(uid=" + uid + ", pid=" + initialPid + ")");
1005            cancelNotification(callingUid, callingPid, pkg, tag, id, 0, 0, false, userId,
1006                    REASON_DELEGATE_ERROR, null);
1007            long ident = Binder.clearCallingIdentity();
1008            try {
1009                ActivityManagerNative.getDefault().crashApplication(uid, initialPid, pkg,
1010                        "Bad notification posted from package " + pkg
1011                        + ": " + message);
1012            } catch (RemoteException e) {
1013            }
1014            Binder.restoreCallingIdentity(ident);
1015        }
1016
1017        @Override
1018        public boolean allowDisable(int what, IBinder token, String pkg) {
1019            if (isCall(pkg, null)) {
1020                return mZenMode == Settings.Global.ZEN_MODE_OFF;
1021            }
1022            return true;
1023        }
1024    };
1025
1026    private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
1027        @Override
1028        public void onReceive(Context context, Intent intent) {
1029            String action = intent.getAction();
1030
1031            boolean queryRestart = false;
1032            boolean queryRemove = false;
1033            boolean packageChanged = false;
1034            boolean cancelNotifications = true;
1035
1036            if (action.equals(Intent.ACTION_PACKAGE_ADDED)
1037                    || (queryRemove=action.equals(Intent.ACTION_PACKAGE_REMOVED))
1038                    || action.equals(Intent.ACTION_PACKAGE_RESTARTED)
1039                    || (packageChanged=action.equals(Intent.ACTION_PACKAGE_CHANGED))
1040                    || (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART))
1041                    || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
1042                String pkgList[] = null;
1043                boolean queryReplace = queryRemove &&
1044                        intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
1045                if (DBG) Slog.i(TAG, "queryReplace=" + queryReplace);
1046                if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
1047                    pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
1048                } else if (queryRestart) {
1049                    pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
1050                } else {
1051                    Uri uri = intent.getData();
1052                    if (uri == null) {
1053                        return;
1054                    }
1055                    String pkgName = uri.getSchemeSpecificPart();
1056                    if (pkgName == null) {
1057                        return;
1058                    }
1059                    if (packageChanged) {
1060                        // We cancel notifications for packages which have just been disabled
1061                        try {
1062                            final int enabled = getContext().getPackageManager()
1063                                    .getApplicationEnabledSetting(pkgName);
1064                            if (enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED
1065                                    || enabled == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
1066                                cancelNotifications = false;
1067                            }
1068                        } catch (IllegalArgumentException e) {
1069                            // Package doesn't exist; probably racing with uninstall.
1070                            // cancelNotifications is already true, so nothing to do here.
1071                            if (DBG) {
1072                                Slog.i(TAG, "Exception trying to look up app enabled setting", e);
1073                            }
1074                        }
1075                    }
1076                    pkgList = new String[]{pkgName};
1077                }
1078
1079                boolean anyListenersInvolved = false;
1080                if (pkgList != null && (pkgList.length > 0)) {
1081                    for (String pkgName : pkgList) {
1082                        if (cancelNotifications) {
1083                            cancelAllNotificationsInt(MY_UID, MY_PID, pkgName, 0, 0, !queryRestart,
1084                                    UserHandle.USER_ALL, REASON_PACKAGE_CHANGED, null);
1085                        }
1086                        if (mEnabledListenerPackageNames.contains(pkgName)) {
1087                            anyListenersInvolved = true;
1088                        }
1089                    }
1090                }
1091
1092                if (anyListenersInvolved) {
1093                    // if we're not replacing a package, clean up orphaned bits
1094                    if (!queryReplace) {
1095                        disableNonexistentListeners();
1096                    }
1097                    // make sure we're still bound to any of our
1098                    // listeners who may have just upgraded
1099                    rebindListenerServices();
1100                }
1101            } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
1102                // Keep track of screen on/off state, but do not turn off the notification light
1103                // until user passes through the lock screen or views the notification.
1104                mScreenOn = true;
1105            } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
1106                mScreenOn = false;
1107            } else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) {
1108                mInCall = TelephonyManager.EXTRA_STATE_OFFHOOK
1109                        .equals(intent.getStringExtra(TelephonyManager.EXTRA_STATE));
1110                updateNotificationPulse();
1111            } else if (action.equals(Intent.ACTION_USER_STOPPED)) {
1112                int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
1113                if (userHandle >= 0) {
1114                    cancelAllNotificationsInt(MY_UID, MY_PID, null, 0, 0, true, userHandle,
1115                            REASON_USER_STOPPED, null);
1116                }
1117            } else if (action.equals(Intent.ACTION_USER_PRESENT)) {
1118                // turn off LED when user passes through lock screen
1119                mNotificationLight.turnOff();
1120            } else if (action.equals(Intent.ACTION_USER_SWITCHED)) {
1121                // reload per-user settings
1122                mSettingsObserver.update(null);
1123                updateCurrentProfilesCache(context);
1124            } else if (action.equals(Intent.ACTION_USER_ADDED)) {
1125                updateCurrentProfilesCache(context);
1126            }
1127        }
1128    };
1129
1130    class SettingsObserver extends ContentObserver {
1131        private final Uri NOTIFICATION_LIGHT_PULSE_URI
1132                = Settings.System.getUriFor(Settings.System.NOTIFICATION_LIGHT_PULSE);
1133
1134        private final Uri ENABLED_NOTIFICATION_LISTENERS_URI
1135                = Settings.Secure.getUriFor(Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
1136
1137        private final Uri ZEN_MODE
1138                = Settings.Global.getUriFor(Settings.Global.ZEN_MODE);
1139
1140        SettingsObserver(Handler handler) {
1141            super(handler);
1142        }
1143
1144        void observe() {
1145            ContentResolver resolver = getContext().getContentResolver();
1146            resolver.registerContentObserver(NOTIFICATION_LIGHT_PULSE_URI,
1147                    false, this, UserHandle.USER_ALL);
1148            resolver.registerContentObserver(ENABLED_NOTIFICATION_LISTENERS_URI,
1149                    false, this, UserHandle.USER_ALL);
1150            resolver.registerContentObserver(ZEN_MODE,
1151                    false, this);
1152            update(null);
1153        }
1154
1155        @Override public void onChange(boolean selfChange, Uri uri) {
1156            update(uri);
1157        }
1158
1159        public void update(Uri uri) {
1160            ContentResolver resolver = getContext().getContentResolver();
1161            if (uri == null || NOTIFICATION_LIGHT_PULSE_URI.equals(uri)) {
1162                boolean pulseEnabled = Settings.System.getInt(resolver,
1163                            Settings.System.NOTIFICATION_LIGHT_PULSE, 0) != 0;
1164                if (mNotificationPulseEnabled != pulseEnabled) {
1165                    mNotificationPulseEnabled = pulseEnabled;
1166                    updateNotificationPulse();
1167                }
1168            }
1169            if (uri == null || ENABLED_NOTIFICATION_LISTENERS_URI.equals(uri)) {
1170                rebindListenerServices();
1171            }
1172            if (ZEN_MODE.equals(uri)) {
1173                updateZenMode();
1174            }
1175        }
1176    }
1177
1178    private SettingsObserver mSettingsObserver;
1179
1180    static long[] getLongArray(Resources r, int resid, int maxlen, long[] def) {
1181        int[] ar = r.getIntArray(resid);
1182        if (ar == null) {
1183            return def;
1184        }
1185        final int len = ar.length > maxlen ? maxlen : ar.length;
1186        long[] out = new long[len];
1187        for (int i=0; i<len; i++) {
1188            out[i] = ar[i];
1189        }
1190        return out;
1191    }
1192
1193    public NotificationManagerService(Context context) {
1194        super(context);
1195    }
1196
1197    @Override
1198    public void onStart() {
1199        mAm = ActivityManagerNative.getDefault();
1200        mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
1201        mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
1202
1203        mHandler = new WorkerHandler();
1204
1205        importOldBlockDb();
1206
1207        mStatusBar = getLocalService(StatusBarManagerInternal.class);
1208        mStatusBar.setNotificationDelegate(mNotificationDelegate);
1209
1210        final LightsManager lights = getLocalService(LightsManager.class);
1211        mNotificationLight = lights.getLight(LightsManager.LIGHT_ID_NOTIFICATIONS);
1212        mAttentionLight = lights.getLight(LightsManager.LIGHT_ID_ATTENTION);
1213
1214        Resources resources = getContext().getResources();
1215        mDefaultNotificationColor = resources.getColor(
1216                R.color.config_defaultNotificationColor);
1217        mDefaultNotificationLedOn = resources.getInteger(
1218                R.integer.config_defaultNotificationLedOn);
1219        mDefaultNotificationLedOff = resources.getInteger(
1220                R.integer.config_defaultNotificationLedOff);
1221
1222        mDefaultVibrationPattern = getLongArray(resources,
1223                R.array.config_defaultNotificationVibePattern,
1224                VIBRATE_PATTERN_MAXLEN,
1225                DEFAULT_VIBRATE_PATTERN);
1226
1227        mFallbackVibrationPattern = getLongArray(resources,
1228                R.array.config_notificationFallbackVibePattern,
1229                VIBRATE_PATTERN_MAXLEN,
1230                DEFAULT_VIBRATE_PATTERN);
1231
1232        // Don't start allowing notifications until the setup wizard has run once.
1233        // After that, including subsequent boots, init with notifications turned on.
1234        // This works on the first boot because the setup wizard will toggle this
1235        // flag at least once and we'll go back to 0 after that.
1236        if (0 == Settings.Global.getInt(getContext().getContentResolver(),
1237                    Settings.Global.DEVICE_PROVISIONED, 0)) {
1238            mDisableNotificationAlerts = true;
1239        }
1240        updateZenMode();
1241
1242        // register for various Intents
1243        IntentFilter filter = new IntentFilter();
1244        filter.addAction(Intent.ACTION_SCREEN_ON);
1245        filter.addAction(Intent.ACTION_SCREEN_OFF);
1246        filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
1247        filter.addAction(Intent.ACTION_USER_PRESENT);
1248        filter.addAction(Intent.ACTION_USER_STOPPED);
1249        filter.addAction(Intent.ACTION_USER_SWITCHED);
1250        filter.addAction(Intent.ACTION_USER_ADDED);
1251        getContext().registerReceiver(mIntentReceiver, filter);
1252        IntentFilter pkgFilter = new IntentFilter();
1253        pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
1254        pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
1255        pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
1256        pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
1257        pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
1258        pkgFilter.addDataScheme("package");
1259        getContext().registerReceiver(mIntentReceiver, pkgFilter);
1260        IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
1261        getContext().registerReceiver(mIntentReceiver, sdFilter);
1262
1263        mSettingsObserver = new SettingsObserver(mHandler);
1264
1265        // spin up NotificationScorers
1266        String[] notificationScorerNames = resources.getStringArray(
1267                R.array.config_notificationScorers);
1268        for (String scorerName : notificationScorerNames) {
1269            try {
1270                Class<?> scorerClass = getContext().getClassLoader().loadClass(scorerName);
1271                NotificationScorer scorer = (NotificationScorer) scorerClass.newInstance();
1272                scorer.initialize(getContext());
1273                mScorers.add(scorer);
1274            } catch (ClassNotFoundException e) {
1275                Slog.w(TAG, "Couldn't find scorer " + scorerName + ".", e);
1276            } catch (InstantiationException e) {
1277                Slog.w(TAG, "Couldn't instantiate scorer " + scorerName + ".", e);
1278            } catch (IllegalAccessException e) {
1279                Slog.w(TAG, "Problem accessing scorer " + scorerName + ".", e);
1280            }
1281        }
1282
1283        publishBinderService(Context.NOTIFICATION_SERVICE, mService);
1284        publishLocalService(NotificationManagerInternal.class, mInternalService);
1285    }
1286
1287    /**
1288     * Read the old XML-based app block database and import those blockages into the AppOps system.
1289     */
1290    private void importOldBlockDb() {
1291        loadBlockDb();
1292
1293        PackageManager pm = getContext().getPackageManager();
1294        for (String pkg : mBlockedPackages) {
1295            PackageInfo info = null;
1296            try {
1297                info = pm.getPackageInfo(pkg, 0);
1298                setNotificationsEnabledForPackageImpl(pkg, info.applicationInfo.uid, false);
1299            } catch (NameNotFoundException e) {
1300                // forget you
1301            }
1302        }
1303        mBlockedPackages.clear();
1304        if (mPolicyFile != null) {
1305            mPolicyFile.delete();
1306        }
1307    }
1308
1309    @Override
1310    public void onBootPhase(int phase) {
1311        if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
1312            // no beeping until we're basically done booting
1313            mSystemReady = true;
1314
1315            // Grab our optional AudioService
1316            mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
1317
1318        } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
1319            // This observer will force an update when observe is called, causing us to
1320            // bind to listener services.
1321            mSettingsObserver.observe();
1322        }
1323    }
1324
1325    void setNotificationsEnabledForPackageImpl(String pkg, int uid, boolean enabled) {
1326        Slog.v(TAG, (enabled?"en":"dis") + "abling notifications for " + pkg);
1327
1328        mAppOps.setMode(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg,
1329                enabled ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED);
1330
1331        // Now, cancel any outstanding notifications that are part of a just-disabled app
1332        if (ENABLE_BLOCKED_NOTIFICATIONS && !enabled) {
1333            cancelAllNotificationsInt(MY_UID, MY_PID, pkg, 0, 0, true, UserHandle.getUserId(uid),
1334                    REASON_PACKAGE_BANNED, null);
1335        }
1336    }
1337
1338    private final IBinder mService = new INotificationManager.Stub() {
1339        // Toasts
1340        // ============================================================================
1341
1342        @Override
1343        public void enqueueToast(String pkg, ITransientNotification callback, int duration)
1344        {
1345            if (DBG) {
1346                Slog.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback
1347                        + " duration=" + duration);
1348            }
1349
1350            if (pkg == null || callback == null) {
1351                Slog.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback);
1352                return ;
1353            }
1354
1355            final boolean isSystemToast = isCallerSystem() || ("android".equals(pkg));
1356
1357            if (ENABLE_BLOCKED_TOASTS && !noteNotificationOp(pkg, Binder.getCallingUid())) {
1358                if (!isSystemToast) {
1359                    Slog.e(TAG, "Suppressing toast from package " + pkg + " by user request.");
1360                    return;
1361                }
1362            }
1363
1364            synchronized (mToastQueue) {
1365                int callingPid = Binder.getCallingPid();
1366                long callingId = Binder.clearCallingIdentity();
1367                try {
1368                    ToastRecord record;
1369                    int index = indexOfToastLocked(pkg, callback);
1370                    // If it's already in the queue, we update it in place, we don't
1371                    // move it to the end of the queue.
1372                    if (index >= 0) {
1373                        record = mToastQueue.get(index);
1374                        record.update(duration);
1375                    } else {
1376                        // Limit the number of toasts that any given package except the android
1377                        // package can enqueue.  Prevents DOS attacks and deals with leaks.
1378                        if (!isSystemToast) {
1379                            int count = 0;
1380                            final int N = mToastQueue.size();
1381                            for (int i=0; i<N; i++) {
1382                                 final ToastRecord r = mToastQueue.get(i);
1383                                 if (r.pkg.equals(pkg)) {
1384                                     count++;
1385                                     if (count >= MAX_PACKAGE_NOTIFICATIONS) {
1386                                         Slog.e(TAG, "Package has already posted " + count
1387                                                + " toasts. Not showing more. Package=" + pkg);
1388                                         return;
1389                                     }
1390                                 }
1391                            }
1392                        }
1393
1394                        record = new ToastRecord(callingPid, pkg, callback, duration);
1395                        mToastQueue.add(record);
1396                        index = mToastQueue.size() - 1;
1397                        keepProcessAliveLocked(callingPid);
1398                    }
1399                    // If it's at index 0, it's the current toast.  It doesn't matter if it's
1400                    // new or just been updated.  Call back and tell it to show itself.
1401                    // If the callback fails, this will remove it from the list, so don't
1402                    // assume that it's valid after this.
1403                    if (index == 0) {
1404                        showNextToastLocked();
1405                    }
1406                } finally {
1407                    Binder.restoreCallingIdentity(callingId);
1408                }
1409            }
1410        }
1411
1412        @Override
1413        public void cancelToast(String pkg, ITransientNotification callback) {
1414            Slog.i(TAG, "cancelToast pkg=" + pkg + " callback=" + callback);
1415
1416            if (pkg == null || callback == null) {
1417                Slog.e(TAG, "Not cancelling notification. pkg=" + pkg + " callback=" + callback);
1418                return ;
1419            }
1420
1421            synchronized (mToastQueue) {
1422                long callingId = Binder.clearCallingIdentity();
1423                try {
1424                    int index = indexOfToastLocked(pkg, callback);
1425                    if (index >= 0) {
1426                        cancelToastLocked(index);
1427                    } else {
1428                        Slog.w(TAG, "Toast already cancelled. pkg=" + pkg
1429                                + " callback=" + callback);
1430                    }
1431                } finally {
1432                    Binder.restoreCallingIdentity(callingId);
1433                }
1434            }
1435        }
1436
1437        @Override
1438        public void enqueueNotificationWithTag(String pkg, String basePkg, String tag, int id,
1439                Notification notification, int[] idOut, int userId) throws RemoteException {
1440            enqueueNotificationInternal(pkg, basePkg, Binder.getCallingUid(),
1441                    Binder.getCallingPid(), tag, id, notification, idOut, userId);
1442        }
1443
1444        @Override
1445        public void cancelNotificationWithTag(String pkg, String tag, int id, int userId) {
1446            checkCallerIsSystemOrSameApp(pkg);
1447            userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1448                    Binder.getCallingUid(), userId, true, false, "cancelNotificationWithTag", pkg);
1449            // Don't allow client applications to cancel foreground service notis.
1450            cancelNotification(Binder.getCallingUid(), Binder.getCallingPid(), pkg, tag, id, 0,
1451                    Binder.getCallingUid() == Process.SYSTEM_UID
1452                    ? 0 : Notification.FLAG_FOREGROUND_SERVICE, false, userId, REASON_NOMAN_CANCEL,
1453                    null);
1454        }
1455
1456        @Override
1457        public void cancelAllNotifications(String pkg, int userId) {
1458            checkCallerIsSystemOrSameApp(pkg);
1459
1460            userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1461                    Binder.getCallingUid(), userId, true, false, "cancelAllNotifications", pkg);
1462
1463            // Calling from user space, don't allow the canceling of actively
1464            // running foreground services.
1465            cancelAllNotificationsInt(Binder.getCallingUid(), Binder.getCallingPid(),
1466                    pkg, 0, Notification.FLAG_FOREGROUND_SERVICE, true, userId,
1467                    REASON_NOMAN_CANCEL_ALL, null);
1468        }
1469
1470        @Override
1471        public void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) {
1472            checkCallerIsSystem();
1473
1474            setNotificationsEnabledForPackageImpl(pkg, uid, enabled);
1475        }
1476
1477        /**
1478         * Use this when you just want to know if notifications are OK for this package.
1479         */
1480        @Override
1481        public boolean areNotificationsEnabledForPackage(String pkg, int uid) {
1482            checkCallerIsSystem();
1483            return (mAppOps.checkOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
1484                    == AppOpsManager.MODE_ALLOWED);
1485        }
1486
1487        /**
1488         * System-only API for getting a list of current (i.e. not cleared) notifications.
1489         *
1490         * Requires ACCESS_NOTIFICATIONS which is signature|system.
1491         */
1492        @Override
1493        public StatusBarNotification[] getActiveNotifications(String callingPkg) {
1494            // enforce() will ensure the calling uid has the correct permission
1495            getContext().enforceCallingOrSelfPermission(
1496                    android.Manifest.permission.ACCESS_NOTIFICATIONS,
1497                    "NotificationManagerService.getActiveNotifications");
1498
1499            StatusBarNotification[] tmp = null;
1500            int uid = Binder.getCallingUid();
1501
1502            // noteOp will check to make sure the callingPkg matches the uid
1503            if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
1504                    == AppOpsManager.MODE_ALLOWED) {
1505                synchronized (mNotificationList) {
1506                    tmp = new StatusBarNotification[mNotificationList.size()];
1507                    final int N = mNotificationList.size();
1508                    for (int i=0; i<N; i++) {
1509                        tmp[i] = mNotificationList.get(i).sbn;
1510                    }
1511                }
1512            }
1513            return tmp;
1514        }
1515
1516        /**
1517         * System-only API for getting a list of recent (cleared, no longer shown) notifications.
1518         *
1519         * Requires ACCESS_NOTIFICATIONS which is signature|system.
1520         */
1521        @Override
1522        public StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count) {
1523            // enforce() will ensure the calling uid has the correct permission
1524            getContext().enforceCallingOrSelfPermission(
1525                    android.Manifest.permission.ACCESS_NOTIFICATIONS,
1526                    "NotificationManagerService.getHistoricalNotifications");
1527
1528            StatusBarNotification[] tmp = null;
1529            int uid = Binder.getCallingUid();
1530
1531            // noteOp will check to make sure the callingPkg matches the uid
1532            if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
1533                    == AppOpsManager.MODE_ALLOWED) {
1534                synchronized (mArchive) {
1535                    tmp = mArchive.getArray(count);
1536                }
1537            }
1538            return tmp;
1539        }
1540
1541        /**
1542         * Register a listener binder directly with the notification manager.
1543         *
1544         * Only works with system callers. Apps should extend
1545         * {@link android.service.notification.NotificationListenerService}.
1546         */
1547        @Override
1548        public void registerListener(final INotificationListener listener,
1549                final ComponentName component, final int userid) {
1550            checkCallerIsSystem();
1551            checkNullListener(listener);
1552            registerListenerImpl(listener, component, userid);
1553        }
1554
1555        /**
1556         * Remove a listener binder directly
1557         */
1558        @Override
1559        public void unregisterListener(INotificationListener listener, int userid) {
1560            checkNullListener(listener);
1561            // no need to check permissions; if your listener binder is in the list,
1562            // that's proof that you had permission to add it in the first place
1563            unregisterListenerImpl(listener, userid);
1564        }
1565
1566        /**
1567         * Allow an INotificationListener to simulate a "clear all" operation.
1568         *
1569         * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onClearAllNotifications}
1570         *
1571         * @param token The binder for the listener, to check that the caller is allowed
1572         */
1573        @Override
1574        public void cancelNotificationsFromListener(INotificationListener token, String[] keys) {
1575            final int callingUid = Binder.getCallingUid();
1576            final int callingPid = Binder.getCallingPid();
1577            long identity = Binder.clearCallingIdentity();
1578            try {
1579                synchronized (mNotificationList) {
1580                    final NotificationListenerInfo info = checkListenerTokenLocked(token);
1581                    if (keys != null) {
1582                        final int N = keys.length;
1583                        for (int i = 0; i < N; i++) {
1584                            NotificationRecord r = mNotificationsByKey.get(keys[i]);
1585                            if (r != null) {
1586                                cancelNotificationFromListenerLocked(info, callingUid, callingPid,
1587                                        r.sbn.getPackageName(), r.sbn.getTag(), r.sbn.getId());
1588                            }
1589                        }
1590                    } else {
1591                        cancelAllLocked(callingUid, callingPid, info.userid,
1592                                REASON_LISTENER_CANCEL_ALL, info);
1593                    }
1594                }
1595            } finally {
1596                Binder.restoreCallingIdentity(identity);
1597            }
1598        }
1599
1600        private void cancelNotificationFromListenerLocked(NotificationListenerInfo info,
1601                int callingUid, int callingPid, String pkg, String tag, int id) {
1602            cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
1603                    Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
1604                    true,
1605                    info.userid, REASON_LISTENER_CANCEL, info);
1606        }
1607
1608        /**
1609         * Allow an INotificationListener to simulate clearing (dismissing) a single notification.
1610         *
1611         * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onNotificationClear}
1612         *
1613         * @param token The binder for the listener, to check that the caller is allowed
1614         */
1615        @Override
1616        public void cancelNotificationFromListener(INotificationListener token, String pkg,
1617                String tag, int id) {
1618            final int callingUid = Binder.getCallingUid();
1619            final int callingPid = Binder.getCallingPid();
1620            long identity = Binder.clearCallingIdentity();
1621            try {
1622                synchronized (mNotificationList) {
1623                    final NotificationListenerInfo info = checkListenerTokenLocked(token);
1624                    cancelNotificationFromListenerLocked(info, callingUid, callingPid,
1625                            pkg, tag, id);
1626                }
1627            } finally {
1628                Binder.restoreCallingIdentity(identity);
1629            }
1630        }
1631
1632        /**
1633         * Allow an INotificationListener to request the list of outstanding notifications seen by
1634         * the current user. Useful when starting up, after which point the listener callbacks
1635         * should be used.
1636         *
1637         * @param token The binder for the listener, to check that the caller is allowed
1638         */
1639        @Override
1640        public StatusBarNotification[] getActiveNotificationsFromListener(
1641                INotificationListener token, String[] keys) {
1642            synchronized (mNotificationList) {
1643                final NotificationListenerInfo info = checkListenerTokenLocked(token);
1644                final ArrayList<StatusBarNotification> list
1645                        = new ArrayList<StatusBarNotification>();
1646                if (keys == null) {
1647                    final int N = mNotificationList.size();
1648                    for (int i=0; i<N; i++) {
1649                        StatusBarNotification sbn = mNotificationList.get(i).sbn;
1650                        if (info.enabledAndUserMatches(sbn)) {
1651                            list.add(sbn);
1652                        }
1653                    }
1654                } else {
1655                    final int N = keys.length;
1656                    for (int i=0; i<N; i++) {
1657                        NotificationRecord r = mNotificationsByKey.get(keys[i]);
1658                        if (r != null && info.enabledAndUserMatches(r.sbn)) {
1659                            list.add(r.sbn);
1660                        }
1661                    }
1662                }
1663                return list.toArray(new StatusBarNotification[list.size()]);
1664            }
1665        }
1666
1667        @Override
1668        public String[] getActiveNotificationKeysFromListener(INotificationListener token) {
1669            return NotificationManagerService.this.getActiveNotificationKeysFromListener(token);
1670        }
1671
1672        @Override
1673        protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1674            if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
1675                    != PackageManager.PERMISSION_GRANTED) {
1676                pw.println("Permission Denial: can't dump NotificationManager from from pid="
1677                        + Binder.getCallingPid()
1678                        + ", uid=" + Binder.getCallingUid());
1679                return;
1680            }
1681
1682            dumpImpl(pw);
1683        }
1684    };
1685
1686    private String[] getActiveNotificationKeysFromListener(INotificationListener token) {
1687        synchronized (mNotificationList) {
1688            final NotificationListenerInfo info = checkListenerTokenLocked(token);
1689            final ArrayList<String> keys = new ArrayList<String>();
1690            final int N = mNotificationList.size();
1691            for (int i=0; i<N; i++) {
1692                final StatusBarNotification sbn = mNotificationList.get(i).sbn;
1693                if (info.enabledAndUserMatches(sbn)) {
1694                    keys.add(sbn.getKey());
1695                }
1696            }
1697            return keys.toArray(new String[keys.size()]);
1698        }
1699    }
1700
1701    void dumpImpl(PrintWriter pw) {
1702        pw.println("Current Notification Manager state:");
1703
1704        pw.println("  Listeners (" + mEnabledListenersForCurrentUser.size()
1705                + ") enabled for current user:");
1706        for (ComponentName cmpt : mEnabledListenersForCurrentUser) {
1707            pw.println("    " + cmpt);
1708        }
1709
1710        pw.println("  Live listeners (" + mListeners.size() + "):");
1711        for (NotificationListenerInfo info : mListeners) {
1712            pw.println("    " + info.component
1713                    + " (user " + info.userid + "): " + info.listener
1714                    + (info.isSystem?" SYSTEM":""));
1715        }
1716
1717        int N;
1718
1719        synchronized (mToastQueue) {
1720            N = mToastQueue.size();
1721            if (N > 0) {
1722                pw.println("  Toast Queue:");
1723                for (int i=0; i<N; i++) {
1724                    mToastQueue.get(i).dump(pw, "    ");
1725                }
1726                pw.println("  ");
1727            }
1728
1729        }
1730
1731        synchronized (mNotificationList) {
1732            N = mNotificationList.size();
1733            if (N > 0) {
1734                pw.println("  Notification List:");
1735                for (int i=0; i<N; i++) {
1736                    mNotificationList.get(i).dump(pw, "    ", getContext());
1737                }
1738                pw.println("  ");
1739            }
1740
1741            N = mLights.size();
1742            if (N > 0) {
1743                pw.println("  Lights List:");
1744                for (int i=0; i<N; i++) {
1745                    pw.println("    " + mLights.get(i));
1746                }
1747                pw.println("  ");
1748            }
1749
1750            pw.println("  mSoundNotification=" + mSoundNotification);
1751            pw.println("  mVibrateNotification=" + mVibrateNotification);
1752            pw.println("  mDisableNotificationAlerts=" + mDisableNotificationAlerts);
1753            pw.println("  mZenMode=" + Settings.Global.zenModeToString(mZenMode));
1754            pw.println("  mSystemReady=" + mSystemReady);
1755            pw.println("  mArchive=" + mArchive.toString());
1756            Iterator<StatusBarNotification> iter = mArchive.descendingIterator();
1757            int i=0;
1758            while (iter.hasNext()) {
1759                pw.println("    " + iter.next());
1760                if (++i >= 5) {
1761                    if (iter.hasNext()) pw.println("    ...");
1762                    break;
1763                }
1764            }
1765
1766            pw.println("\n  Usage Stats:");
1767            mUsageStats.dump(pw, "    ");
1768
1769        }
1770    }
1771
1772    /**
1773     * The private API only accessible to the system process.
1774     */
1775    private final NotificationManagerInternal mInternalService = new NotificationManagerInternal() {
1776        @Override
1777        public void enqueueNotification(String pkg, String basePkg, int callingUid, int callingPid,
1778                String tag, int id, Notification notification, int[] idReceived, int userId) {
1779            enqueueNotificationInternal(pkg, basePkg, callingUid, callingPid, tag, id, notification,
1780                    idReceived, userId);
1781        }
1782    };
1783
1784    void enqueueNotificationInternal(final String pkg, String basePkg, final int callingUid,
1785            final int callingPid, final String tag, final int id, final Notification notification,
1786            int[] idOut, int incomingUserId) {
1787        if (DBG) {
1788            Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id
1789                    + " notification=" + notification);
1790        }
1791        checkCallerIsSystemOrSameApp(pkg);
1792        final boolean isSystemNotification = isUidSystem(callingUid) || ("android".equals(pkg));
1793
1794        final int userId = ActivityManager.handleIncomingUser(callingPid,
1795                callingUid, incomingUserId, true, false, "enqueueNotification", pkg);
1796        final UserHandle user = new UserHandle(userId);
1797
1798        // Limit the number of notifications that any given package except the android
1799        // package can enqueue.  Prevents DOS attacks and deals with leaks.
1800        if (!isSystemNotification) {
1801            synchronized (mNotificationList) {
1802                int count = 0;
1803                final int N = mNotificationList.size();
1804                for (int i=0; i<N; i++) {
1805                    final NotificationRecord r = mNotificationList.get(i);
1806                    if (r.sbn.getPackageName().equals(pkg) && r.sbn.getUserId() == userId) {
1807                        count++;
1808                        if (count >= MAX_PACKAGE_NOTIFICATIONS) {
1809                            Slog.e(TAG, "Package has already posted " + count
1810                                    + " notifications.  Not showing more.  package=" + pkg);
1811                            return;
1812                        }
1813                    }
1814                }
1815            }
1816        }
1817
1818        // This conditional is a dirty hack to limit the logging done on
1819        //     behalf of the download manager without affecting other apps.
1820        if (!pkg.equals("com.android.providers.downloads")
1821                || Log.isLoggable("DownloadManager", Log.VERBOSE)) {
1822            EventLogTags.writeNotificationEnqueue(callingUid, callingPid,
1823                    pkg, id, tag, userId, notification.toString());
1824        }
1825
1826        if (pkg == null || notification == null) {
1827            throw new IllegalArgumentException("null not allowed: pkg=" + pkg
1828                    + " id=" + id + " notification=" + notification);
1829        }
1830        if (notification.icon != 0) {
1831            if (notification.contentView == null) {
1832                throw new IllegalArgumentException("contentView required: pkg=" + pkg
1833                        + " id=" + id + " notification=" + notification);
1834            }
1835        }
1836
1837        mHandler.post(new Runnable() {
1838            @Override
1839            public void run() {
1840
1841                // === Scoring ===
1842
1843                // 0. Sanitize inputs
1844                notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN,
1845                        Notification.PRIORITY_MAX);
1846                // Migrate notification flags to scores
1847                if (0 != (notification.flags & Notification.FLAG_HIGH_PRIORITY)) {
1848                    if (notification.priority < Notification.PRIORITY_MAX) {
1849                        notification.priority = Notification.PRIORITY_MAX;
1850                    }
1851                } else if (SCORE_ONGOING_HIGHER &&
1852                        0 != (notification.flags & Notification.FLAG_ONGOING_EVENT)) {
1853                    if (notification.priority < Notification.PRIORITY_HIGH) {
1854                        notification.priority = Notification.PRIORITY_HIGH;
1855                    }
1856                }
1857
1858                // 1. initial score: buckets of 10, around the app
1859                int score = notification.priority * NOTIFICATION_PRIORITY_MULTIPLIER; //[-20..20]
1860
1861                // 2. Consult external heuristics (TBD)
1862
1863                // 3. Apply local rules
1864
1865                int initialScore = score;
1866                if (!mScorers.isEmpty()) {
1867                    if (DBG) Slog.v(TAG, "Initial score is " + score + ".");
1868                    for (NotificationScorer scorer : mScorers) {
1869                        try {
1870                            score = scorer.getScore(notification, score);
1871                        } catch (Throwable t) {
1872                            Slog.w(TAG, "Scorer threw on .getScore.", t);
1873                        }
1874                    }
1875                    if (DBG) Slog.v(TAG, "Final score is " + score + ".");
1876                }
1877
1878                // add extra to indicate score modified by NotificationScorer
1879                notification.extras.putBoolean(Notification.EXTRA_SCORE_MODIFIED,
1880                        score != initialScore);
1881
1882                // blocked apps
1883                if (ENABLE_BLOCKED_NOTIFICATIONS && !noteNotificationOp(pkg, callingUid)) {
1884                    if (!isSystemNotification) {
1885                        score = JUNK_SCORE;
1886                        Slog.e(TAG, "Suppressing notification from package " + pkg
1887                                + " by user request.");
1888                    }
1889                }
1890
1891                if (DBG) {
1892                    Slog.v(TAG, "Assigned score=" + score + " to " + notification);
1893                }
1894
1895                if (score < SCORE_DISPLAY_THRESHOLD) {
1896                    // Notification will be blocked because the score is too low.
1897                    return;
1898                }
1899
1900                // Is this notification intercepted by zen mode?
1901                final boolean intercept = shouldIntercept(pkg, notification);
1902                notification.extras.putBoolean(EXTRA_INTERCEPT, intercept);
1903
1904                // Should this notification make noise, vibe, or use the LED?
1905                final boolean canInterrupt = (score >= SCORE_INTERRUPTION_THRESHOLD) && !intercept;
1906                if (DBG) Slog.v(TAG, "canInterrupt=" + canInterrupt + " intercept=" + intercept);
1907                synchronized (mNotificationList) {
1908                    final StatusBarNotification n = new StatusBarNotification(
1909                            pkg, id, tag, callingUid, callingPid, score, notification, user);
1910                    NotificationRecord r = new NotificationRecord(n);
1911                    NotificationRecord old = null;
1912
1913                    int index = indexOfNotificationLocked(pkg, tag, id, userId);
1914                    if (index < 0) {
1915                        mNotificationList.add(r);
1916                        mUsageStats.registerPostedByApp(r);
1917                    } else {
1918                        old = mNotificationList.remove(index);
1919                        mNotificationList.add(index, r);
1920                        mUsageStats.registerUpdatedByApp(r);
1921                        // Make sure we don't lose the foreground service state.
1922                        if (old != null) {
1923                            notification.flags |=
1924                                old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;
1925                        }
1926                    }
1927                    if (old != null) {
1928                        mNotificationsByKey.remove(old.sbn.getKey());
1929                    }
1930                    mNotificationsByKey.put(n.getKey(), r);
1931
1932                    // Ensure if this is a foreground service that the proper additional
1933                    // flags are set.
1934                    if ((notification.flags&Notification.FLAG_FOREGROUND_SERVICE) != 0) {
1935                        notification.flags |= Notification.FLAG_ONGOING_EVENT
1936                                | Notification.FLAG_NO_CLEAR;
1937                    }
1938
1939                    final int currentUser;
1940                    final long token = Binder.clearCallingIdentity();
1941                    try {
1942                        currentUser = ActivityManager.getCurrentUser();
1943                    } finally {
1944                        Binder.restoreCallingIdentity(token);
1945                    }
1946
1947                    if (notification.icon != 0) {
1948                        if (old != null && old.statusBarKey != null) {
1949                            r.statusBarKey = old.statusBarKey;
1950                            final long identity = Binder.clearCallingIdentity();
1951                            try {
1952                                mStatusBar.updateNotification(r.statusBarKey, n);
1953                            } finally {
1954                                Binder.restoreCallingIdentity(identity);
1955                            }
1956                        } else {
1957                            final long identity = Binder.clearCallingIdentity();
1958                            try {
1959                                r.statusBarKey = mStatusBar.addNotification(n);
1960                                if ((n.getNotification().flags & Notification.FLAG_SHOW_LIGHTS) != 0
1961                                        && canInterrupt) {
1962                                    mAttentionLight.pulse();
1963                                }
1964                            } finally {
1965                                Binder.restoreCallingIdentity(identity);
1966                            }
1967                        }
1968                        // Send accessibility events only for the current user.
1969                        if (currentUser == userId) {
1970                            sendAccessibilityEvent(notification, pkg);
1971                        }
1972
1973                        notifyPostedLocked(r);
1974                    } else {
1975                        Slog.e(TAG, "Not posting notification with icon==0: " + notification);
1976                        if (old != null && old.statusBarKey != null) {
1977                            final long identity = Binder.clearCallingIdentity();
1978                            try {
1979                                mStatusBar.removeNotification(old.statusBarKey);
1980                            } finally {
1981                                Binder.restoreCallingIdentity(identity);
1982                            }
1983
1984                            notifyRemovedLocked(r);
1985                        }
1986                        // ATTENTION: in a future release we will bail out here
1987                        // so that we do not play sounds, show lights, etc. for invalid
1988                        // notifications
1989                        Slog.e(TAG, "WARNING: In a future release this will crash the app: "
1990                                + n.getPackageName());
1991                    }
1992
1993                    // If we're not supposed to beep, vibrate, etc. then don't.
1994                    if (!mDisableNotificationAlerts
1995                            && (!(old != null
1996                                && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 ))
1997                            && (r.getUserId() == UserHandle.USER_ALL ||
1998                                (r.getUserId() == userId && r.getUserId() == currentUser))
1999                            && canInterrupt
2000                            && mSystemReady
2001                            && mAudioManager != null) {
2002                        if (DBG) Slog.v(TAG, "Interrupting!");
2003                        // sound
2004
2005                        // should we use the default notification sound? (indicated either by
2006                        // DEFAULT_SOUND or because notification.sound is pointing at
2007                        // Settings.System.NOTIFICATION_SOUND)
2008                        final boolean useDefaultSound =
2009                               (notification.defaults & Notification.DEFAULT_SOUND) != 0 ||
2010                                       Settings.System.DEFAULT_NOTIFICATION_URI
2011                                               .equals(notification.sound);
2012
2013                        Uri soundUri = null;
2014                        boolean hasValidSound = false;
2015
2016                        if (useDefaultSound) {
2017                            soundUri = Settings.System.DEFAULT_NOTIFICATION_URI;
2018
2019                            // check to see if the default notification sound is silent
2020                            ContentResolver resolver = getContext().getContentResolver();
2021                            hasValidSound = Settings.System.getString(resolver,
2022                                   Settings.System.NOTIFICATION_SOUND) != null;
2023                        } else if (notification.sound != null) {
2024                            soundUri = notification.sound;
2025                            hasValidSound = (soundUri != null);
2026                        }
2027
2028                        if (hasValidSound) {
2029                            boolean looping =
2030                                    (notification.flags & Notification.FLAG_INSISTENT) != 0;
2031                            int audioStreamType;
2032                            if (notification.audioStreamType >= 0) {
2033                                audioStreamType = notification.audioStreamType;
2034                            } else {
2035                                audioStreamType = DEFAULT_STREAM_TYPE;
2036                            }
2037                            mSoundNotification = r;
2038                            // do not play notifications if stream volume is 0 (typically because
2039                            // ringer mode is silent) or if there is a user of exclusive audio focus
2040                            if ((mAudioManager.getStreamVolume(audioStreamType) != 0)
2041                                    && !mAudioManager.isAudioFocusExclusive()) {
2042                                final long identity = Binder.clearCallingIdentity();
2043                                try {
2044                                    final IRingtonePlayer player =
2045                                            mAudioManager.getRingtonePlayer();
2046                                    if (player != null) {
2047                                        if (DBG) Slog.v(TAG, "Playing sound " + soundUri
2048                                                + " on stream " + audioStreamType);
2049                                        player.playAsync(soundUri, user, looping, audioStreamType);
2050                                    }
2051                                } catch (RemoteException e) {
2052                                } finally {
2053                                    Binder.restoreCallingIdentity(identity);
2054                                }
2055                            }
2056                        }
2057
2058                        // vibrate
2059                        // Does the notification want to specify its own vibration?
2060                        final boolean hasCustomVibrate = notification.vibrate != null;
2061
2062                        // new in 4.2: if there was supposed to be a sound and we're in vibrate
2063                        // mode, and no other vibration is specified, we fall back to vibration
2064                        final boolean convertSoundToVibration =
2065                                   !hasCustomVibrate
2066                                && hasValidSound
2067                                && (mAudioManager.getRingerMode()
2068                                           == AudioManager.RINGER_MODE_VIBRATE);
2069
2070                        // The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback.
2071                        final boolean useDefaultVibrate =
2072                                (notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
2073
2074                        if ((useDefaultVibrate || convertSoundToVibration || hasCustomVibrate)
2075                                && !(mAudioManager.getRingerMode()
2076                                        == AudioManager.RINGER_MODE_SILENT)) {
2077                            mVibrateNotification = r;
2078
2079                            if (useDefaultVibrate || convertSoundToVibration) {
2080                                // Escalate privileges so we can use the vibrator even if the
2081                                // notifying app does not have the VIBRATE permission.
2082                                long identity = Binder.clearCallingIdentity();
2083                                try {
2084                                    mVibrator.vibrate(r.sbn.getUid(), r.sbn.getBasePkg(),
2085                                        useDefaultVibrate ? mDefaultVibrationPattern
2086                                            : mFallbackVibrationPattern,
2087                                        ((notification.flags & Notification.FLAG_INSISTENT) != 0)
2088                                                ? 0: -1, notification.audioStreamType);
2089                                } finally {
2090                                    Binder.restoreCallingIdentity(identity);
2091                                }
2092                            } else if (notification.vibrate.length > 1) {
2093                                // If you want your own vibration pattern, you need the VIBRATE
2094                                // permission
2095                                mVibrator.vibrate(r.sbn.getUid(), r.sbn.getBasePkg(),
2096                                        notification.vibrate,
2097                                    ((notification.flags & Notification.FLAG_INSISTENT) != 0)
2098                                            ? 0: -1, notification.audioStreamType);
2099                            }
2100                        }
2101                    }
2102
2103                    // light
2104                    // the most recent thing gets the light
2105                    mLights.remove(old);
2106                    if (mLedNotification == old) {
2107                        mLedNotification = null;
2108                    }
2109                    //Slog.i(TAG, "notification.lights="
2110                    //        + ((old.notification.lights.flags & Notification.FLAG_SHOW_LIGHTS)
2111                    //                  != 0));
2112                    if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0
2113                            && canInterrupt) {
2114                        mLights.add(r);
2115                        updateLightsLocked();
2116                    } else {
2117                        if (old != null
2118                                && ((old.getFlags() & Notification.FLAG_SHOW_LIGHTS) != 0)) {
2119                            updateLightsLocked();
2120                        }
2121                    }
2122                }
2123            }
2124        });
2125
2126        idOut[0] = id;
2127    }
2128
2129     void registerListenerImpl(final INotificationListener listener,
2130            final ComponentName component, final int userid) {
2131        synchronized (mNotificationList) {
2132            try {
2133                NotificationListenerInfo info
2134                        = new NotificationListenerInfo(listener, component, userid, true);
2135                listener.asBinder().linkToDeath(info, 0);
2136                mListeners.add(info);
2137            } catch (RemoteException e) {
2138                // already dead
2139            }
2140        }
2141    }
2142
2143    /**
2144     * Removes a listener from the list and unbinds from its service.
2145     */
2146    void unregisterListenerImpl(final INotificationListener listener, final int userid) {
2147        NotificationListenerInfo info = removeListenerImpl(listener, userid);
2148        if (info != null && info.connection != null) {
2149            getContext().unbindService(info.connection);
2150        }
2151    }
2152
2153    /**
2154     * Removes a listener from the list but does not unbind from the listener's service.
2155     *
2156     * @return the removed listener.
2157     */
2158    NotificationListenerInfo removeListenerImpl(
2159            final INotificationListener listener, final int userid) {
2160        NotificationListenerInfo listenerInfo = null;
2161        synchronized (mNotificationList) {
2162            final int N = mListeners.size();
2163            for (int i=N-1; i>=0; i--) {
2164                final NotificationListenerInfo info = mListeners.get(i);
2165                if (info.listener.asBinder() == listener.asBinder()
2166                        && info.userid == userid) {
2167                    listenerInfo = mListeners.remove(i);
2168                }
2169            }
2170        }
2171        return listenerInfo;
2172    }
2173
2174    void showNextToastLocked() {
2175        ToastRecord record = mToastQueue.get(0);
2176        while (record != null) {
2177            if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback);
2178            try {
2179                record.callback.show();
2180                scheduleTimeoutLocked(record);
2181                return;
2182            } catch (RemoteException e) {
2183                Slog.w(TAG, "Object died trying to show notification " + record.callback
2184                        + " in package " + record.pkg);
2185                // remove it from the list and let the process die
2186                int index = mToastQueue.indexOf(record);
2187                if (index >= 0) {
2188                    mToastQueue.remove(index);
2189                }
2190                keepProcessAliveLocked(record.pid);
2191                if (mToastQueue.size() > 0) {
2192                    record = mToastQueue.get(0);
2193                } else {
2194                    record = null;
2195                }
2196            }
2197        }
2198    }
2199
2200    void cancelToastLocked(int index) {
2201        ToastRecord record = mToastQueue.get(index);
2202        try {
2203            record.callback.hide();
2204        } catch (RemoteException e) {
2205            Slog.w(TAG, "Object died trying to hide notification " + record.callback
2206                    + " in package " + record.pkg);
2207            // don't worry about this, we're about to remove it from
2208            // the list anyway
2209        }
2210        mToastQueue.remove(index);
2211        keepProcessAliveLocked(record.pid);
2212        if (mToastQueue.size() > 0) {
2213            // Show the next one. If the callback fails, this will remove
2214            // it from the list, so don't assume that the list hasn't changed
2215            // after this point.
2216            showNextToastLocked();
2217        }
2218    }
2219
2220    private void scheduleTimeoutLocked(ToastRecord r)
2221    {
2222        mHandler.removeCallbacksAndMessages(r);
2223        Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r);
2224        long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY;
2225        mHandler.sendMessageDelayed(m, delay);
2226    }
2227
2228    private void handleTimeout(ToastRecord record)
2229    {
2230        if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback);
2231        synchronized (mToastQueue) {
2232            int index = indexOfToastLocked(record.pkg, record.callback);
2233            if (index >= 0) {
2234                cancelToastLocked(index);
2235            }
2236        }
2237    }
2238
2239    // lock on mToastQueue
2240    int indexOfToastLocked(String pkg, ITransientNotification callback)
2241    {
2242        IBinder cbak = callback.asBinder();
2243        ArrayList<ToastRecord> list = mToastQueue;
2244        int len = list.size();
2245        for (int i=0; i<len; i++) {
2246            ToastRecord r = list.get(i);
2247            if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) {
2248                return i;
2249            }
2250        }
2251        return -1;
2252    }
2253
2254    // lock on mToastQueue
2255    void keepProcessAliveLocked(int pid)
2256    {
2257        int toastCount = 0; // toasts from this pid
2258        ArrayList<ToastRecord> list = mToastQueue;
2259        int N = list.size();
2260        for (int i=0; i<N; i++) {
2261            ToastRecord r = list.get(i);
2262            if (r.pid == pid) {
2263                toastCount++;
2264            }
2265        }
2266        try {
2267            mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0);
2268        } catch (RemoteException e) {
2269            // Shouldn't happen.
2270        }
2271    }
2272
2273    private final class WorkerHandler extends Handler
2274    {
2275        @Override
2276        public void handleMessage(Message msg)
2277        {
2278            switch (msg.what)
2279            {
2280                case MESSAGE_TIMEOUT:
2281                    handleTimeout((ToastRecord)msg.obj);
2282                    break;
2283            }
2284        }
2285    }
2286
2287
2288    // Notifications
2289    // ============================================================================
2290    static int clamp(int x, int low, int high) {
2291        return (x < low) ? low : ((x > high) ? high : x);
2292    }
2293
2294    void sendAccessibilityEvent(Notification notification, CharSequence packageName) {
2295        AccessibilityManager manager = AccessibilityManager.getInstance(getContext());
2296        if (!manager.isEnabled()) {
2297            return;
2298        }
2299
2300        AccessibilityEvent event =
2301            AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
2302        event.setPackageName(packageName);
2303        event.setClassName(Notification.class.getName());
2304        event.setParcelableData(notification);
2305        CharSequence tickerText = notification.tickerText;
2306        if (!TextUtils.isEmpty(tickerText)) {
2307            event.getText().add(tickerText);
2308        }
2309
2310        manager.sendAccessibilityEvent(event);
2311    }
2312
2313    private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason) {
2314        // tell the app
2315        if (sendDelete) {
2316            if (r.getNotification().deleteIntent != null) {
2317                try {
2318                    r.getNotification().deleteIntent.send();
2319                } catch (PendingIntent.CanceledException ex) {
2320                    // do nothing - there's no relevant way to recover, and
2321                    //     no reason to let this propagate
2322                    Slog.w(TAG, "canceled PendingIntent for " + r.sbn.getPackageName(), ex);
2323                }
2324            }
2325        }
2326
2327        // status bar
2328        if (r.getNotification().icon != 0) {
2329            final long identity = Binder.clearCallingIdentity();
2330            try {
2331                mStatusBar.removeNotification(r.statusBarKey);
2332            } finally {
2333                Binder.restoreCallingIdentity(identity);
2334            }
2335            r.statusBarKey = null;
2336            notifyRemovedLocked(r);
2337        }
2338
2339        // sound
2340        if (mSoundNotification == r) {
2341            mSoundNotification = null;
2342            final long identity = Binder.clearCallingIdentity();
2343            try {
2344                final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
2345                if (player != null) {
2346                    player.stopAsync();
2347                }
2348            } catch (RemoteException e) {
2349            } finally {
2350                Binder.restoreCallingIdentity(identity);
2351            }
2352        }
2353
2354        // vibrate
2355        if (mVibrateNotification == r) {
2356            mVibrateNotification = null;
2357            long identity = Binder.clearCallingIdentity();
2358            try {
2359                mVibrator.cancel();
2360            }
2361            finally {
2362                Binder.restoreCallingIdentity(identity);
2363            }
2364        }
2365
2366        // light
2367        mLights.remove(r);
2368        if (mLedNotification == r) {
2369            mLedNotification = null;
2370        }
2371
2372        // Record usage stats
2373        switch (reason) {
2374            case REASON_DELEGATE_CANCEL:
2375            case REASON_DELEGATE_CANCEL_ALL:
2376            case REASON_LISTENER_CANCEL:
2377            case REASON_LISTENER_CANCEL_ALL:
2378                mUsageStats.registerDismissedByUser(r);
2379                break;
2380            case REASON_NOMAN_CANCEL:
2381            case REASON_NOMAN_CANCEL_ALL:
2382                mUsageStats.registerRemovedByApp(r);
2383                break;
2384            case REASON_DELEGATE_CLICK:
2385                mUsageStats.registerCancelDueToClick(r);
2386                break;
2387            default:
2388                mUsageStats.registerCancelUnknown(r);
2389                break;
2390        }
2391
2392        // Save it for users of getHistoricalNotifications()
2393        mArchive.record(r.sbn);
2394    }
2395
2396    /**
2397     * Cancels a notification ONLY if it has all of the {@code mustHaveFlags}
2398     * and none of the {@code mustNotHaveFlags}.
2399     */
2400    void cancelNotification(final int callingUid, final int callingPid,
2401            final String pkg, final String tag, final int id,
2402            final int mustHaveFlags, final int mustNotHaveFlags, final boolean sendDelete,
2403            final int userId, final int reason, final NotificationListenerInfo listener) {
2404        // In enqueueNotificationInternal notifications are added by scheduling the
2405        // work on the worker handler. Hence, we also schedule the cancel on this
2406        // handler to avoid a scenario where an add notification call followed by a
2407        // remove notification call ends up in not removing the notification.
2408        mHandler.post(new Runnable() {
2409            @Override
2410            public void run() {
2411                EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, id, tag, userId,
2412                        mustHaveFlags, mustNotHaveFlags, reason,
2413                        listener == null ? null : listener.component.toShortString());
2414
2415                synchronized (mNotificationList) {
2416                    int index = indexOfNotificationLocked(pkg, tag, id, userId);
2417                    if (index >= 0) {
2418                        NotificationRecord r = mNotificationList.get(index);
2419
2420                        // Ideally we'd do this in the caller of this method. However, that would
2421                        // require the caller to also find the notification.
2422                        if (reason == REASON_DELEGATE_CLICK) {
2423                            mUsageStats.registerClickedByUser(r);
2424                        }
2425
2426                        if ((r.getNotification().flags & mustHaveFlags) != mustHaveFlags) {
2427                            return;
2428                        }
2429                        if ((r.getNotification().flags & mustNotHaveFlags) != 0) {
2430                            return;
2431                        }
2432
2433                        mNotificationList.remove(index);
2434                        mNotificationsByKey.remove(r.sbn.getKey());
2435
2436                        cancelNotificationLocked(r, sendDelete, reason);
2437                        updateLightsLocked();
2438                    }
2439                }
2440            }
2441        });
2442    }
2443
2444    /**
2445     * Determine whether the userId applies to the notification in question, either because
2446     * they match exactly, or one of them is USER_ALL (which is treated as a wildcard).
2447     */
2448    private boolean notificationMatchesUserId(NotificationRecord r, int userId) {
2449        return
2450                // looking for USER_ALL notifications? match everything
2451                   userId == UserHandle.USER_ALL
2452                // a notification sent to USER_ALL matches any query
2453                || r.getUserId() == UserHandle.USER_ALL
2454                // an exact user match
2455                || r.getUserId() == userId;
2456    }
2457
2458    /**
2459     * Determine whether the userId applies to the notification in question, either because
2460     * they match exactly, or one of them is USER_ALL (which is treated as a wildcard) or
2461     * because it matches one of the users profiles.
2462     */
2463    private boolean notificationMatchesCurrentProfiles(NotificationRecord r, int userId) {
2464        synchronized (mCurrentProfiles) {
2465            return notificationMatchesUserId(r, userId)
2466                    || mCurrentProfiles.get(r.getUserId()) != null;
2467        }
2468    }
2469
2470    /**
2471     * Cancels all notifications from a given package that have all of the
2472     * {@code mustHaveFlags}.
2473     */
2474    boolean cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, int mustHaveFlags,
2475            int mustNotHaveFlags, boolean doit, int userId, int reason,
2476            NotificationListenerInfo listener) {
2477        EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
2478                pkg, userId, mustHaveFlags, mustNotHaveFlags, reason,
2479                listener == null ? null : listener.component.toShortString());
2480
2481        synchronized (mNotificationList) {
2482            final int N = mNotificationList.size();
2483            boolean canceledSomething = false;
2484            for (int i = N-1; i >= 0; --i) {
2485                NotificationRecord r = mNotificationList.get(i);
2486                if (!notificationMatchesUserId(r, userId)) {
2487                    continue;
2488                }
2489                // Don't remove notifications to all, if there's no package name specified
2490                if (r.getUserId() == UserHandle.USER_ALL && pkg == null) {
2491                    continue;
2492                }
2493                if ((r.getFlags() & mustHaveFlags) != mustHaveFlags) {
2494                    continue;
2495                }
2496                if ((r.getFlags() & mustNotHaveFlags) != 0) {
2497                    continue;
2498                }
2499                if (pkg != null && !r.sbn.getPackageName().equals(pkg)) {
2500                    continue;
2501                }
2502                canceledSomething = true;
2503                if (!doit) {
2504                    return true;
2505                }
2506                mNotificationList.remove(i);
2507                mNotificationsByKey.remove(r.sbn.getKey());
2508                cancelNotificationLocked(r, false, reason);
2509            }
2510            if (canceledSomething) {
2511                updateLightsLocked();
2512            }
2513            return canceledSomething;
2514        }
2515    }
2516
2517
2518
2519    // Return true if the UID is a system or phone UID and therefore should not have
2520    // any notifications or toasts blocked.
2521    boolean isUidSystem(int uid) {
2522        final int appid = UserHandle.getAppId(uid);
2523        return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0);
2524    }
2525
2526    // same as isUidSystem(int, int) for the Binder caller's UID.
2527    boolean isCallerSystem() {
2528        return isUidSystem(Binder.getCallingUid());
2529    }
2530
2531    void checkCallerIsSystem() {
2532        if (isCallerSystem()) {
2533            return;
2534        }
2535        throw new SecurityException("Disallowed call for uid " + Binder.getCallingUid());
2536    }
2537
2538    void checkCallerIsSystemOrSameApp(String pkg) {
2539        if (isCallerSystem()) {
2540            return;
2541        }
2542        final int uid = Binder.getCallingUid();
2543        try {
2544            ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo(
2545                    pkg, 0, UserHandle.getCallingUserId());
2546            if (!UserHandle.isSameApp(ai.uid, uid)) {
2547                throw new SecurityException("Calling uid " + uid + " gave package"
2548                        + pkg + " which is owned by uid " + ai.uid);
2549            }
2550        } catch (RemoteException re) {
2551            throw new SecurityException("Unknown package " + pkg + "\n" + re);
2552        }
2553    }
2554
2555    void cancelAllLocked(int callingUid, int callingPid, int userId, int reason,
2556            NotificationListenerInfo listener) {
2557        EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
2558                null, userId, 0, 0, reason,
2559                listener == null ? null : listener.component.toShortString());
2560
2561        final int N = mNotificationList.size();
2562        for (int i=N-1; i>=0; i--) {
2563            NotificationRecord r = mNotificationList.get(i);
2564            if (!notificationMatchesCurrentProfiles(r, userId)) {
2565                continue;
2566            }
2567
2568            if ((r.getFlags() & (Notification.FLAG_ONGOING_EVENT
2569                            | Notification.FLAG_NO_CLEAR)) == 0) {
2570                mNotificationList.remove(i);
2571                mNotificationsByKey.remove(r.sbn.getKey());
2572                cancelNotificationLocked(r, true, reason);
2573            }
2574        }
2575        updateLightsLocked();
2576    }
2577
2578    // lock on mNotificationList
2579    void updateLightsLocked()
2580    {
2581        // handle notification lights
2582        if (mLedNotification == null) {
2583            // get next notification, if any
2584            int n = mLights.size();
2585            if (n > 0) {
2586                mLedNotification = mLights.get(n-1);
2587            }
2588        }
2589
2590        // Don't flash while we are in a call or screen is on
2591        if (mLedNotification == null || mInCall || mScreenOn) {
2592            mNotificationLight.turnOff();
2593        } else {
2594            final Notification ledno = mLedNotification.sbn.getNotification();
2595            int ledARGB = ledno.ledARGB;
2596            int ledOnMS = ledno.ledOnMS;
2597            int ledOffMS = ledno.ledOffMS;
2598            if ((ledno.defaults & Notification.DEFAULT_LIGHTS) != 0) {
2599                ledARGB = mDefaultNotificationColor;
2600                ledOnMS = mDefaultNotificationLedOn;
2601                ledOffMS = mDefaultNotificationLedOff;
2602            }
2603            if (mNotificationPulseEnabled) {
2604                // pulse repeatedly
2605                mNotificationLight.setFlashing(ledARGB, Light.LIGHT_FLASH_TIMED,
2606                        ledOnMS, ledOffMS);
2607            }
2608        }
2609    }
2610
2611    // lock on mNotificationList
2612    int indexOfNotificationLocked(String pkg, String tag, int id, int userId)
2613    {
2614        ArrayList<NotificationRecord> list = mNotificationList;
2615        final int len = list.size();
2616        for (int i=0; i<len; i++) {
2617            NotificationRecord r = list.get(i);
2618            if (!notificationMatchesUserId(r, userId) || r.sbn.getId() != id) {
2619                continue;
2620            }
2621            if (tag == null) {
2622                if (r.sbn.getTag() != null) {
2623                    continue;
2624                }
2625            } else {
2626                if (!tag.equals(r.sbn.getTag())) {
2627                    continue;
2628                }
2629            }
2630            if (r.sbn.getPackageName().equals(pkg)) {
2631                return i;
2632            }
2633        }
2634        return -1;
2635    }
2636
2637    private void updateNotificationPulse() {
2638        synchronized (mNotificationList) {
2639            updateLightsLocked();
2640        }
2641    }
2642
2643    private void updateZenMode() {
2644        final int mode = Settings.Global.getInt(getContext().getContentResolver(),
2645                Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF);
2646        if (mode != mZenMode) {
2647            Slog.d(TAG, String.format("updateZenMode: %s -> %s",
2648                    Settings.Global.zenModeToString(mZenMode),
2649                    Settings.Global.zenModeToString(mode)));
2650        }
2651        mZenMode = mode;
2652
2653        final String[] exceptionPackages = null; // none (for now)
2654
2655        // call restrictions
2656        final boolean muteCalls = mZenMode != Settings.Global.ZEN_MODE_OFF;
2657        mAppOps.setRestriction(AppOpsManager.OP_VIBRATE, AudioManager.STREAM_RING,
2658                muteCalls ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
2659                exceptionPackages);
2660        mAppOps.setRestriction(AppOpsManager.OP_PLAY_AUDIO, AudioManager.STREAM_RING,
2661                muteCalls ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
2662                exceptionPackages);
2663
2664        // alarm restrictions
2665        final boolean muteAlarms = false; // TODO until we save user config
2666        mAppOps.setRestriction(AppOpsManager.OP_VIBRATE, AudioManager.STREAM_ALARM,
2667                muteAlarms ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
2668                exceptionPackages);
2669        mAppOps.setRestriction(AppOpsManager.OP_PLAY_AUDIO, AudioManager.STREAM_ALARM,
2670                muteAlarms ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
2671                exceptionPackages);
2672
2673        // restrict vibrations with no hints
2674        mAppOps.setRestriction(AppOpsManager.OP_VIBRATE, AudioManager.USE_DEFAULT_STREAM_TYPE,
2675                (muteAlarms || muteCalls) ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
2676                exceptionPackages);
2677    }
2678
2679    private void updateCurrentProfilesCache(Context context) {
2680        UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
2681        int currentUserId = ActivityManager.getCurrentUser();
2682        if (userManager != null) {
2683            List<UserInfo> profiles = userManager.getProfiles(currentUserId);
2684            synchronized (mCurrentProfiles) {
2685                mCurrentProfiles.clear();
2686                for (UserInfo user : profiles) {
2687                    mCurrentProfiles.put(user.id, user);
2688                }
2689            }
2690        }
2691    }
2692
2693    private boolean isCall(String pkg, Notification n) {
2694        return CALL_PACKAGES.contains(pkg);
2695    }
2696
2697    private boolean isAlarm(String pkg, Notification n) {
2698        return ALARM_PACKAGES.contains(pkg);
2699    }
2700
2701    private boolean shouldIntercept(String pkg, Notification n) {
2702        if (mZenMode != Settings.Global.ZEN_MODE_OFF) {
2703            return !isAlarm(pkg, n);
2704        }
2705        return false;
2706    }
2707}
2708