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