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