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