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