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