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