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