NotificationManagerService.java revision 5579ec33eb317a19ad80929169b2b329b147d98b
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.toString());
818                        if (val instanceof Bitmap) {
819                            pw.print(String.format(" (%dx%d)",
820                                    ((Bitmap) val).getWidth(),
821                                    ((Bitmap) val).getHeight()));
822                        } else if (val.getClass().isArray()) {
823                            pw.println(" {");
824                            final int N = Array.getLength(val);
825                            for (int i=0; i<N; i++) {
826                                if (i > 0) pw.println(",");
827                                pw.print(prefix + "      " + Array.get(val, i));
828                            }
829                            pw.print("\n" + prefix + "    }");
830                        }
831                        pw.println();
832                    }
833                }
834                pw.println(prefix + "  }");
835            }
836        }
837
838        @Override
839        public final String toString() {
840            return String.format(
841                    "NotificationRecord(0x%08x: pkg=%s user=%s id=%d tag=%s score=%d key=%s: %s)",
842                    System.identityHashCode(this),
843                    this.sbn.getPackageName(), this.sbn.getUser(), this.sbn.getId(),
844                    this.sbn.getTag(), this.sbn.getScore(), this.sbn.getKey(),
845                    this.sbn.getNotification());
846        }
847    }
848
849    private static final class ToastRecord
850    {
851        final int pid;
852        final String pkg;
853        final ITransientNotification callback;
854        int duration;
855
856        ToastRecord(int pid, String pkg, ITransientNotification callback, int duration)
857        {
858            this.pid = pid;
859            this.pkg = pkg;
860            this.callback = callback;
861            this.duration = duration;
862        }
863
864        void update(int duration) {
865            this.duration = duration;
866        }
867
868        void dump(PrintWriter pw, String prefix) {
869            pw.println(prefix + this);
870        }
871
872        @Override
873        public final String toString()
874        {
875            return "ToastRecord{"
876                + Integer.toHexString(System.identityHashCode(this))
877                + " pkg=" + pkg
878                + " callback=" + callback
879                + " duration=" + duration;
880        }
881    }
882
883    private final NotificationDelegate mNotificationDelegate = new NotificationDelegate() {
884
885        @Override
886        public void onSetDisabled(int status) {
887            synchronized (mNotificationList) {
888                mDisabledNotifications = status;
889                if ((mDisabledNotifications & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) {
890                    // cancel whatever's going on
891                    long identity = Binder.clearCallingIdentity();
892                    try {
893                        final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
894                        if (player != null) {
895                            player.stopAsync();
896                        }
897                    } catch (RemoteException e) {
898                    } finally {
899                        Binder.restoreCallingIdentity(identity);
900                    }
901
902                    identity = Binder.clearCallingIdentity();
903                    try {
904                        mVibrator.cancel();
905                    } finally {
906                        Binder.restoreCallingIdentity(identity);
907                    }
908                }
909            }
910        }
911
912        @Override
913        public void onClearAll() {
914            // XXX to be totally correct, the caller should tell us which user
915            // this is for.
916            int currentUser = ActivityManager.getCurrentUser();
917            synchronized (mNotificationList) {
918                cancelAllLocked(currentUser);
919            }
920        }
921
922        @Override
923        public void onNotificationClick(String pkg, String tag, int id) {
924            // XXX to be totally correct, the caller should tell us which user
925            // this is for.
926            cancelNotification(pkg, tag, id, Notification.FLAG_AUTO_CANCEL,
927                    Notification.FLAG_FOREGROUND_SERVICE, false,
928                    ActivityManager.getCurrentUser());
929        }
930
931        @Override
932        public void onNotificationClear(String pkg, String tag, int id) {
933            // XXX to be totally correct, the caller should tell us which user
934            // this is for.
935            cancelNotification(pkg, tag, id, 0,
936                Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
937                true, ActivityManager.getCurrentUser());
938        }
939
940        @Override
941        public void onPanelRevealed() {
942            synchronized (mNotificationList) {
943                // sound
944                mSoundNotification = null;
945
946                long identity = Binder.clearCallingIdentity();
947                try {
948                    final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
949                    if (player != null) {
950                        player.stopAsync();
951                    }
952                } catch (RemoteException e) {
953                } finally {
954                    Binder.restoreCallingIdentity(identity);
955                }
956
957                // vibrate
958                mVibrateNotification = null;
959                identity = Binder.clearCallingIdentity();
960                try {
961                    mVibrator.cancel();
962                } finally {
963                    Binder.restoreCallingIdentity(identity);
964                }
965
966                // light
967                mLights.clear();
968                mLedNotification = null;
969                updateLightsLocked();
970            }
971        }
972
973        @Override
974        public void onNotificationError(String pkg, String tag, int id,
975                int uid, int initialPid, String message) {
976            Slog.d(TAG, "onNotification error pkg=" + pkg + " tag=" + tag + " id=" + id
977                    + "; will crashApplication(uid=" + uid + ", pid=" + initialPid + ")");
978            // XXX to be totally correct, the caller should tell us which user
979            // this is for.
980            cancelNotification(pkg, tag, id, 0, 0, false, UserHandle.getUserId(uid));
981            long ident = Binder.clearCallingIdentity();
982            try {
983                ActivityManagerNative.getDefault().crashApplication(uid, initialPid, pkg,
984                        "Bad notification posted from package " + pkg
985                        + ": " + message);
986            } catch (RemoteException e) {
987            }
988            Binder.restoreCallingIdentity(ident);
989        }
990    };
991
992    private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
993        @Override
994        public void onReceive(Context context, Intent intent) {
995            String action = intent.getAction();
996
997            boolean queryRestart = false;
998            boolean queryRemove = false;
999            boolean packageChanged = false;
1000            boolean cancelNotifications = true;
1001
1002            if (action.equals(Intent.ACTION_PACKAGE_ADDED)
1003                    || (queryRemove=action.equals(Intent.ACTION_PACKAGE_REMOVED))
1004                    || action.equals(Intent.ACTION_PACKAGE_RESTARTED)
1005                    || (packageChanged=action.equals(Intent.ACTION_PACKAGE_CHANGED))
1006                    || (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART))
1007                    || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
1008                String pkgList[] = null;
1009                boolean queryReplace = queryRemove &&
1010                        intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
1011                if (DBG) Slog.i(TAG, "queryReplace=" + queryReplace);
1012                if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
1013                    pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
1014                } else if (queryRestart) {
1015                    pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
1016                } else {
1017                    Uri uri = intent.getData();
1018                    if (uri == null) {
1019                        return;
1020                    }
1021                    String pkgName = uri.getSchemeSpecificPart();
1022                    if (pkgName == null) {
1023                        return;
1024                    }
1025                    if (packageChanged) {
1026                        // We cancel notifications for packages which have just been disabled
1027                        try {
1028                            final int enabled = getContext().getPackageManager()
1029                                    .getApplicationEnabledSetting(pkgName);
1030                            if (enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED
1031                                    || enabled == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
1032                                cancelNotifications = false;
1033                            }
1034                        } catch (IllegalArgumentException e) {
1035                            // Package doesn't exist; probably racing with uninstall.
1036                            // cancelNotifications is already true, so nothing to do here.
1037                            if (DBG) {
1038                                Slog.i(TAG, "Exception trying to look up app enabled setting", e);
1039                            }
1040                        }
1041                    }
1042                    pkgList = new String[]{pkgName};
1043                }
1044
1045                boolean anyListenersInvolved = false;
1046                if (pkgList != null && (pkgList.length > 0)) {
1047                    for (String pkgName : pkgList) {
1048                        if (cancelNotifications) {
1049                            cancelAllNotificationsInt(pkgName, 0, 0, !queryRestart,
1050                                    UserHandle.USER_ALL);
1051                        }
1052                        if (mEnabledListenerPackageNames.contains(pkgName)) {
1053                            anyListenersInvolved = true;
1054                        }
1055                    }
1056                }
1057
1058                if (anyListenersInvolved) {
1059                    // if we're not replacing a package, clean up orphaned bits
1060                    if (!queryReplace) {
1061                        disableNonexistentListeners();
1062                    }
1063                    // make sure we're still bound to any of our
1064                    // listeners who may have just upgraded
1065                    rebindListenerServices();
1066                }
1067            } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
1068                // Keep track of screen on/off state, but do not turn off the notification light
1069                // until user passes through the lock screen or views the notification.
1070                mScreenOn = true;
1071            } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
1072                mScreenOn = false;
1073            } else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) {
1074                mInCall = (intent.getStringExtra(TelephonyManager.EXTRA_STATE).equals(
1075                        TelephonyManager.EXTRA_STATE_OFFHOOK));
1076                updateNotificationPulse();
1077            } else if (action.equals(Intent.ACTION_USER_STOPPED)) {
1078                int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
1079                if (userHandle >= 0) {
1080                    cancelAllNotificationsInt(null, 0, 0, true, userHandle);
1081                }
1082            } else if (action.equals(Intent.ACTION_USER_PRESENT)) {
1083                // turn off LED when user passes through lock screen
1084                mNotificationLight.turnOff();
1085            } else if (action.equals(Intent.ACTION_USER_SWITCHED)) {
1086                // reload per-user settings
1087                mSettingsObserver.update(null);
1088            }
1089        }
1090    };
1091
1092    class SettingsObserver extends ContentObserver {
1093        private final Uri NOTIFICATION_LIGHT_PULSE_URI
1094                = Settings.System.getUriFor(Settings.System.NOTIFICATION_LIGHT_PULSE);
1095
1096        private final Uri ENABLED_NOTIFICATION_LISTENERS_URI
1097                = Settings.Secure.getUriFor(Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
1098
1099        SettingsObserver(Handler handler) {
1100            super(handler);
1101        }
1102
1103        void observe() {
1104            ContentResolver resolver = getContext().getContentResolver();
1105            resolver.registerContentObserver(NOTIFICATION_LIGHT_PULSE_URI,
1106                    false, this, UserHandle.USER_ALL);
1107            resolver.registerContentObserver(ENABLED_NOTIFICATION_LISTENERS_URI,
1108                    false, this, UserHandle.USER_ALL);
1109            update(null);
1110        }
1111
1112        @Override public void onChange(boolean selfChange, Uri uri) {
1113            update(uri);
1114        }
1115
1116        public void update(Uri uri) {
1117            ContentResolver resolver = getContext().getContentResolver();
1118            if (uri == null || NOTIFICATION_LIGHT_PULSE_URI.equals(uri)) {
1119                boolean pulseEnabled = Settings.System.getInt(resolver,
1120                            Settings.System.NOTIFICATION_LIGHT_PULSE, 0) != 0;
1121                if (mNotificationPulseEnabled != pulseEnabled) {
1122                    mNotificationPulseEnabled = pulseEnabled;
1123                    updateNotificationPulse();
1124                }
1125            }
1126            if (uri == null || ENABLED_NOTIFICATION_LISTENERS_URI.equals(uri)) {
1127                rebindListenerServices();
1128            }
1129        }
1130    }
1131
1132    private SettingsObserver mSettingsObserver;
1133
1134    static long[] getLongArray(Resources r, int resid, int maxlen, long[] def) {
1135        int[] ar = r.getIntArray(resid);
1136        if (ar == null) {
1137            return def;
1138        }
1139        final int len = ar.length > maxlen ? maxlen : ar.length;
1140        long[] out = new long[len];
1141        for (int i=0; i<len; i++) {
1142            out[i] = ar[i];
1143        }
1144        return out;
1145    }
1146
1147    public NotificationManagerService(Context context) {
1148        super(context);
1149    }
1150
1151    @Override
1152    public void onStart() {
1153        mAm = ActivityManagerNative.getDefault();
1154        mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
1155        mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
1156
1157        mHandler = new WorkerHandler();
1158
1159        importOldBlockDb();
1160
1161        mStatusBar = getLocalService(StatusBarManagerInternal.class);
1162        mStatusBar.setNotificationDelegate(mNotificationDelegate);
1163
1164        final LightsManager lights = getLocalService(LightsManager.class);
1165        mNotificationLight = lights.getLight(LightsManager.LIGHT_ID_NOTIFICATIONS);
1166        mAttentionLight = lights.getLight(LightsManager.LIGHT_ID_ATTENTION);
1167
1168        Resources resources = getContext().getResources();
1169        mDefaultNotificationColor = resources.getColor(
1170                R.color.config_defaultNotificationColor);
1171        mDefaultNotificationLedOn = resources.getInteger(
1172                R.integer.config_defaultNotificationLedOn);
1173        mDefaultNotificationLedOff = resources.getInteger(
1174                R.integer.config_defaultNotificationLedOff);
1175
1176        mDefaultVibrationPattern = getLongArray(resources,
1177                R.array.config_defaultNotificationVibePattern,
1178                VIBRATE_PATTERN_MAXLEN,
1179                DEFAULT_VIBRATE_PATTERN);
1180
1181        mFallbackVibrationPattern = getLongArray(resources,
1182                R.array.config_notificationFallbackVibePattern,
1183                VIBRATE_PATTERN_MAXLEN,
1184                DEFAULT_VIBRATE_PATTERN);
1185
1186        // Don't start allowing notifications until the setup wizard has run once.
1187        // After that, including subsequent boots, init with notifications turned on.
1188        // This works on the first boot because the setup wizard will toggle this
1189        // flag at least once and we'll go back to 0 after that.
1190        if (0 == Settings.Global.getInt(getContext().getContentResolver(),
1191                    Settings.Global.DEVICE_PROVISIONED, 0)) {
1192            mDisabledNotifications = StatusBarManager.DISABLE_NOTIFICATION_ALERTS;
1193        }
1194
1195        // register for various Intents
1196        IntentFilter filter = new IntentFilter();
1197        filter.addAction(Intent.ACTION_SCREEN_ON);
1198        filter.addAction(Intent.ACTION_SCREEN_OFF);
1199        filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
1200        filter.addAction(Intent.ACTION_USER_PRESENT);
1201        filter.addAction(Intent.ACTION_USER_STOPPED);
1202        filter.addAction(Intent.ACTION_USER_SWITCHED);
1203        getContext().registerReceiver(mIntentReceiver, filter);
1204        IntentFilter pkgFilter = new IntentFilter();
1205        pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
1206        pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
1207        pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
1208        pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
1209        pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
1210        pkgFilter.addDataScheme("package");
1211        getContext().registerReceiver(mIntentReceiver, pkgFilter);
1212        IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
1213        getContext().registerReceiver(mIntentReceiver, sdFilter);
1214
1215        mSettingsObserver = new SettingsObserver(mHandler);
1216
1217        // spin up NotificationScorers
1218        String[] notificationScorerNames = resources.getStringArray(
1219                R.array.config_notificationScorers);
1220        for (String scorerName : notificationScorerNames) {
1221            try {
1222                Class<?> scorerClass = getContext().getClassLoader().loadClass(scorerName);
1223                NotificationScorer scorer = (NotificationScorer) scorerClass.newInstance();
1224                scorer.initialize(getContext());
1225                mScorers.add(scorer);
1226            } catch (ClassNotFoundException e) {
1227                Slog.w(TAG, "Couldn't find scorer " + scorerName + ".", e);
1228            } catch (InstantiationException e) {
1229                Slog.w(TAG, "Couldn't instantiate scorer " + scorerName + ".", e);
1230            } catch (IllegalAccessException e) {
1231                Slog.w(TAG, "Problem accessing scorer " + scorerName + ".", e);
1232            }
1233        }
1234
1235        publishBinderService(Context.NOTIFICATION_SERVICE, mService);
1236        publishLocalService(NotificationManagerInternal.class, mInternalService);
1237    }
1238
1239    /**
1240     * Read the old XML-based app block database and import those blockages into the AppOps system.
1241     */
1242    private void importOldBlockDb() {
1243        loadBlockDb();
1244
1245        PackageManager pm = getContext().getPackageManager();
1246        for (String pkg : mBlockedPackages) {
1247            PackageInfo info = null;
1248            try {
1249                info = pm.getPackageInfo(pkg, 0);
1250                setNotificationsEnabledForPackageImpl(pkg, info.applicationInfo.uid, false);
1251            } catch (NameNotFoundException e) {
1252                // forget you
1253            }
1254        }
1255        mBlockedPackages.clear();
1256        if (mPolicyFile != null) {
1257            mPolicyFile.delete();
1258        }
1259    }
1260
1261    @Override
1262    public void onBootPhase(int phase) {
1263        if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
1264            // no beeping until we're basically done booting
1265            mSystemReady = true;
1266
1267            // Grab our optional AudioService
1268            mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
1269
1270        } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
1271            // This observer will force an update when observe is called, causing us to
1272            // bind to listener services.
1273            mSettingsObserver.observe();
1274        }
1275    }
1276
1277    void setNotificationsEnabledForPackageImpl(String pkg, int uid, boolean enabled) {
1278        Slog.v(TAG, (enabled?"en":"dis") + "abling notifications for " + pkg);
1279
1280        mAppOps.setMode(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg,
1281                enabled ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED);
1282
1283        // Now, cancel any outstanding notifications that are part of a just-disabled app
1284        if (ENABLE_BLOCKED_NOTIFICATIONS && !enabled) {
1285            cancelAllNotificationsInt(pkg, 0, 0, true, UserHandle.getUserId(uid));
1286        }
1287    }
1288
1289    private final IBinder mService = new INotificationManager.Stub() {
1290        // Toasts
1291        // ============================================================================
1292
1293        @Override
1294        public void enqueueToast(String pkg, ITransientNotification callback, int duration)
1295        {
1296            if (DBG) {
1297                Slog.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback
1298                        + " duration=" + duration);
1299            }
1300
1301            if (pkg == null || callback == null) {
1302                Slog.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback);
1303                return ;
1304            }
1305
1306            final boolean isSystemToast = isCallerSystem() || ("android".equals(pkg));
1307
1308            if (ENABLE_BLOCKED_TOASTS && !noteNotificationOp(pkg, Binder.getCallingUid())) {
1309                if (!isSystemToast) {
1310                    Slog.e(TAG, "Suppressing toast from package " + pkg + " by user request.");
1311                    return;
1312                }
1313            }
1314
1315            synchronized (mToastQueue) {
1316                int callingPid = Binder.getCallingPid();
1317                long callingId = Binder.clearCallingIdentity();
1318                try {
1319                    ToastRecord record;
1320                    int index = indexOfToastLocked(pkg, callback);
1321                    // If it's already in the queue, we update it in place, we don't
1322                    // move it to the end of the queue.
1323                    if (index >= 0) {
1324                        record = mToastQueue.get(index);
1325                        record.update(duration);
1326                    } else {
1327                        // Limit the number of toasts that any given package except the android
1328                        // package can enqueue.  Prevents DOS attacks and deals with leaks.
1329                        if (!isSystemToast) {
1330                            int count = 0;
1331                            final int N = mToastQueue.size();
1332                            for (int i=0; i<N; i++) {
1333                                 final ToastRecord r = mToastQueue.get(i);
1334                                 if (r.pkg.equals(pkg)) {
1335                                     count++;
1336                                     if (count >= MAX_PACKAGE_NOTIFICATIONS) {
1337                                         Slog.e(TAG, "Package has already posted " + count
1338                                                + " toasts. Not showing more. Package=" + pkg);
1339                                         return;
1340                                     }
1341                                 }
1342                            }
1343                        }
1344
1345                        record = new ToastRecord(callingPid, pkg, callback, duration);
1346                        mToastQueue.add(record);
1347                        index = mToastQueue.size() - 1;
1348                        keepProcessAliveLocked(callingPid);
1349                    }
1350                    // If it's at index 0, it's the current toast.  It doesn't matter if it's
1351                    // new or just been updated.  Call back and tell it to show itself.
1352                    // If the callback fails, this will remove it from the list, so don't
1353                    // assume that it's valid after this.
1354                    if (index == 0) {
1355                        showNextToastLocked();
1356                    }
1357                } finally {
1358                    Binder.restoreCallingIdentity(callingId);
1359                }
1360            }
1361        }
1362
1363        @Override
1364        public void cancelToast(String pkg, ITransientNotification callback) {
1365            Slog.i(TAG, "cancelToast pkg=" + pkg + " callback=" + callback);
1366
1367            if (pkg == null || callback == null) {
1368                Slog.e(TAG, "Not cancelling notification. pkg=" + pkg + " callback=" + callback);
1369                return ;
1370            }
1371
1372            synchronized (mToastQueue) {
1373                long callingId = Binder.clearCallingIdentity();
1374                try {
1375                    int index = indexOfToastLocked(pkg, callback);
1376                    if (index >= 0) {
1377                        cancelToastLocked(index);
1378                    } else {
1379                        Slog.w(TAG, "Toast already cancelled. pkg=" + pkg
1380                                + " callback=" + callback);
1381                    }
1382                } finally {
1383                    Binder.restoreCallingIdentity(callingId);
1384                }
1385            }
1386        }
1387
1388        @Override
1389        public void enqueueNotificationWithTag(String pkg, String basePkg, String tag, int id,
1390                Notification notification, int[] idOut, int userId) throws RemoteException {
1391            enqueueNotificationInternal(pkg, basePkg, Binder.getCallingUid(),
1392                    Binder.getCallingPid(), tag, id, notification, idOut, userId);
1393        }
1394
1395        @Override
1396        public void cancelNotificationWithTag(String pkg, String tag, int id, int userId) {
1397            checkCallerIsSystemOrSameApp(pkg);
1398            userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1399                    Binder.getCallingUid(), userId, true, false, "cancelNotificationWithTag", pkg);
1400            // Don't allow client applications to cancel foreground service notis.
1401            cancelNotification(pkg, tag, id, 0,
1402                    Binder.getCallingUid() == Process.SYSTEM_UID
1403                    ? 0 : Notification.FLAG_FOREGROUND_SERVICE, false, userId);
1404        }
1405
1406        @Override
1407        public void cancelAllNotifications(String pkg, int userId) {
1408            checkCallerIsSystemOrSameApp(pkg);
1409
1410            userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1411                    Binder.getCallingUid(), userId, true, false, "cancelAllNotifications", pkg);
1412
1413            // Calling from user space, don't allow the canceling of actively
1414            // running foreground services.
1415            cancelAllNotificationsInt(pkg, 0, Notification.FLAG_FOREGROUND_SERVICE, true, userId);
1416        }
1417
1418        @Override
1419        public void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) {
1420            checkCallerIsSystem();
1421
1422            setNotificationsEnabledForPackageImpl(pkg, uid, enabled);
1423        }
1424
1425        /**
1426         * Use this when you just want to know if notifications are OK for this package.
1427         */
1428        @Override
1429        public boolean areNotificationsEnabledForPackage(String pkg, int uid) {
1430            checkCallerIsSystem();
1431            return (mAppOps.checkOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
1432                    == AppOpsManager.MODE_ALLOWED);
1433        }
1434
1435        /**
1436         * System-only API for getting a list of current (i.e. not cleared) notifications.
1437         *
1438         * Requires ACCESS_NOTIFICATIONS which is signature|system.
1439         */
1440        @Override
1441        public StatusBarNotification[] getActiveNotifications(String callingPkg) {
1442            // enforce() will ensure the calling uid has the correct permission
1443            getContext().enforceCallingOrSelfPermission(
1444                    android.Manifest.permission.ACCESS_NOTIFICATIONS,
1445                    "NotificationManagerService.getActiveNotifications");
1446
1447            StatusBarNotification[] tmp = null;
1448            int uid = Binder.getCallingUid();
1449
1450            // noteOp will check to make sure the callingPkg matches the uid
1451            if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
1452                    == AppOpsManager.MODE_ALLOWED) {
1453                synchronized (mNotificationList) {
1454                    tmp = new StatusBarNotification[mNotificationList.size()];
1455                    final int N = mNotificationList.size();
1456                    for (int i=0; i<N; i++) {
1457                        tmp[i] = mNotificationList.get(i).sbn;
1458                    }
1459                }
1460            }
1461            return tmp;
1462        }
1463
1464        /**
1465         * System-only API for getting a list of recent (cleared, no longer shown) notifications.
1466         *
1467         * Requires ACCESS_NOTIFICATIONS which is signature|system.
1468         */
1469        @Override
1470        public StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count) {
1471            // enforce() will ensure the calling uid has the correct permission
1472            getContext().enforceCallingOrSelfPermission(
1473                    android.Manifest.permission.ACCESS_NOTIFICATIONS,
1474                    "NotificationManagerService.getHistoricalNotifications");
1475
1476            StatusBarNotification[] tmp = null;
1477            int uid = Binder.getCallingUid();
1478
1479            // noteOp will check to make sure the callingPkg matches the uid
1480            if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
1481                    == AppOpsManager.MODE_ALLOWED) {
1482                synchronized (mArchive) {
1483                    tmp = mArchive.getArray(count);
1484                }
1485            }
1486            return tmp;
1487        }
1488
1489        /**
1490         * Register a listener binder directly with the notification manager.
1491         *
1492         * Only works with system callers. Apps should extend
1493         * {@link android.service.notification.NotificationListenerService}.
1494         */
1495        @Override
1496        public void registerListener(final INotificationListener listener,
1497                final ComponentName component, final int userid) {
1498            checkCallerIsSystem();
1499            checkNullListener(listener);
1500            registerListenerImpl(listener, component, userid);
1501        }
1502
1503        /**
1504         * Remove a listener binder directly
1505         */
1506        @Override
1507        public void unregisterListener(INotificationListener listener, int userid) {
1508            checkNullListener(listener);
1509            // no need to check permissions; if your listener binder is in the list,
1510            // that's proof that you had permission to add it in the first place
1511            unregisterListenerImpl(listener, userid);
1512        }
1513
1514        /**
1515         * Allow an INotificationListener to simulate a "clear all" operation.
1516         *
1517         * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onClearAllNotifications}
1518         *
1519         * @param token The binder for the listener, to check that the caller is allowed
1520         */
1521        @Override
1522        public void cancelNotificationsFromListener(INotificationListener token, String[] keys) {
1523            long identity = Binder.clearCallingIdentity();
1524            try {
1525                synchronized (mNotificationList) {
1526                    final NotificationListenerInfo info = checkListenerTokenLocked(token);
1527                    if (keys != null) {
1528                        final int N = keys.length;
1529                        for (int i = 0; i < N; i++) {
1530                            NotificationRecord r = mNotificationsByKey.get(keys[i]);
1531                            if (r != null) {
1532                                cancelNotificationFromListenerLocked(info,
1533                                        r.sbn.getPackageName(), r.sbn.getTag(), r.sbn.getId());
1534                            }
1535                        }
1536                    } else {
1537                        cancelAllLocked(info.userid);
1538                    }
1539                }
1540            } finally {
1541                Binder.restoreCallingIdentity(identity);
1542            }
1543        }
1544
1545        private void cancelNotificationFromListenerLocked(NotificationListenerInfo info,
1546                String pkg, String tag, int id) {
1547            cancelNotification(pkg, tag, id, 0,
1548                    Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
1549                    true,
1550                    info.userid);
1551        }
1552
1553        /**
1554         * Allow an INotificationListener to simulate clearing (dismissing) a single notification.
1555         *
1556         * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onNotificationClear}
1557         *
1558         * @param token The binder for the listener, to check that the caller is allowed
1559         */
1560        @Override
1561        public void cancelNotificationFromListener(INotificationListener token, String pkg,
1562                String tag, int id) {
1563            long identity = Binder.clearCallingIdentity();
1564            try {
1565                synchronized (mNotificationList) {
1566                    final NotificationListenerInfo info = checkListenerTokenLocked(token);
1567                    cancelNotificationFromListenerLocked(info,
1568                            pkg, tag, id);
1569                }
1570            } finally {
1571                Binder.restoreCallingIdentity(identity);
1572            }
1573        }
1574
1575        /**
1576         * Allow an INotificationListener to request the list of outstanding notifications seen by
1577         * the current user. Useful when starting up, after which point the listener callbacks
1578         * should be used.
1579         *
1580         * @param token The binder for the listener, to check that the caller is allowed
1581         */
1582        @Override
1583        public StatusBarNotification[] getActiveNotificationsFromListener(
1584                INotificationListener token, String[] keys) {
1585            synchronized (mNotificationList) {
1586                final NotificationListenerInfo info = checkListenerTokenLocked(token);
1587                final ArrayList<StatusBarNotification> list
1588                        = new ArrayList<StatusBarNotification>();
1589                if (keys == null) {
1590                    final int N = mNotificationList.size();
1591                    for (int i=0; i<N; i++) {
1592                        StatusBarNotification sbn = mNotificationList.get(i).sbn;
1593                        if (info.enabledAndUserMatches(sbn)) {
1594                            list.add(sbn);
1595                        }
1596                    }
1597                } else {
1598                    final int N = keys.length;
1599                    for (int i=0; i<N; i++) {
1600                        NotificationRecord r = mNotificationsByKey.get(keys[i]);
1601                        if (r != null && info.enabledAndUserMatches(r.sbn)) {
1602                            list.add(r.sbn);
1603                        }
1604                    }
1605                }
1606                return list.toArray(new StatusBarNotification[list.size()]);
1607            }
1608        }
1609
1610        @Override
1611        public String[] getActiveNotificationKeysFromListener(INotificationListener token) {
1612            return NotificationManagerService.this.getActiveNotificationKeysFromListener(token);
1613        }
1614
1615        @Override
1616        protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1617            if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
1618                    != PackageManager.PERMISSION_GRANTED) {
1619                pw.println("Permission Denial: can't dump NotificationManager from from pid="
1620                        + Binder.getCallingPid()
1621                        + ", uid=" + Binder.getCallingUid());
1622                return;
1623            }
1624
1625            dumpImpl(pw);
1626        }
1627    };
1628
1629    private String[] getActiveNotificationKeysFromListener(INotificationListener token) {
1630        synchronized (mNotificationList) {
1631            final NotificationListenerInfo info = checkListenerTokenLocked(token);
1632            final ArrayList<String> keys = new ArrayList<String>();
1633            final int N = mNotificationList.size();
1634            for (int i=0; i<N; i++) {
1635                final StatusBarNotification sbn = mNotificationList.get(i).sbn;
1636                if (info.enabledAndUserMatches(sbn)) {
1637                    keys.add(sbn.getKey());
1638                }
1639            }
1640            return keys.toArray(new String[keys.size()]);
1641        }
1642    }
1643
1644    void dumpImpl(PrintWriter pw) {
1645        pw.println("Current Notification Manager state:");
1646
1647        pw.println("  Listeners (" + mEnabledListenersForCurrentUser.size()
1648                + ") enabled for current user:");
1649        for (ComponentName cmpt : mEnabledListenersForCurrentUser) {
1650            pw.println("    " + cmpt);
1651        }
1652
1653        pw.println("  Live listeners (" + mListeners.size() + "):");
1654        for (NotificationListenerInfo info : mListeners) {
1655            pw.println("    " + info.component
1656                    + " (user " + info.userid + "): " + info.listener
1657                    + (info.isSystem?" SYSTEM":""));
1658        }
1659
1660        int N;
1661
1662        synchronized (mToastQueue) {
1663            N = mToastQueue.size();
1664            if (N > 0) {
1665                pw.println("  Toast Queue:");
1666                for (int i=0; i<N; i++) {
1667                    mToastQueue.get(i).dump(pw, "    ");
1668                }
1669                pw.println("  ");
1670            }
1671
1672        }
1673
1674        synchronized (mNotificationList) {
1675            N = mNotificationList.size();
1676            if (N > 0) {
1677                pw.println("  Notification List:");
1678                for (int i=0; i<N; i++) {
1679                    mNotificationList.get(i).dump(pw, "    ", getContext());
1680                }
1681                pw.println("  ");
1682            }
1683
1684            N = mLights.size();
1685            if (N > 0) {
1686                pw.println("  Lights List:");
1687                for (int i=0; i<N; i++) {
1688                    pw.println("    " + mLights.get(i));
1689                }
1690                pw.println("  ");
1691            }
1692
1693            pw.println("  mSoundNotification=" + mSoundNotification);
1694            pw.println("  mVibrateNotification=" + mVibrateNotification);
1695            pw.println("  mDisabledNotifications=0x"
1696                    + Integer.toHexString(mDisabledNotifications));
1697            pw.println("  mSystemReady=" + mSystemReady);
1698            pw.println("  mArchive=" + mArchive.toString());
1699            Iterator<StatusBarNotification> iter = mArchive.descendingIterator();
1700            int i=0;
1701            while (iter.hasNext()) {
1702                pw.println("    " + iter.next());
1703                if (++i >= 5) {
1704                    if (iter.hasNext()) pw.println("    ...");
1705                    break;
1706                }
1707            }
1708
1709        }
1710    }
1711
1712    /**
1713     * The private API only accessible to the system process.
1714     */
1715    private final NotificationManagerInternal mInternalService = new NotificationManagerInternal() {
1716        @Override
1717        public void enqueueNotification(String pkg, String basePkg, int callingUid, int callingPid,
1718                String tag, int id, Notification notification, int[] idReceived, int userId) {
1719            enqueueNotificationInternal(pkg, basePkg, callingUid, callingPid, tag, id, notification,
1720                    idReceived, userId);
1721        }
1722    };
1723
1724    void enqueueNotificationInternal(final String pkg, String basePkg, final int callingUid,
1725            final int callingPid, final String tag, final int id, final Notification notification,
1726            int[] idOut, int incomingUserId) {
1727        if (DBG) {
1728            Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id
1729                    + " notification=" + notification);
1730        }
1731        checkCallerIsSystemOrSameApp(pkg);
1732        final boolean isSystemNotification = isUidSystem(callingUid) || ("android".equals(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 can enqueue.  Prevents DOS attacks and deals with leaks.
1740        if (!isSystemNotification) {
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