• 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.IActivityManager;
27 import android.app.INotificationManager;
28 import android.app.ITransientNotification;
29 import android.app.Notification;
30 import android.app.PendingIntent;
31 import android.app.StatusBarManager;
32 import android.content.BroadcastReceiver;
33 import android.content.ContentResolver;
34 import android.content.Context;
35 import android.content.Intent;
36 import android.content.IntentFilter;
37 import android.content.pm.ApplicationInfo;
38 import android.content.pm.PackageManager;
39 import android.content.pm.PackageManager.NameNotFoundException;
40 import android.content.res.Resources;
41 import android.database.ContentObserver;
42 import android.media.AudioManager;
43 import android.media.IAudioService;
44 import android.media.IRingtonePlayer;
45 import android.net.Uri;
46 import android.os.Binder;
47 import android.os.Handler;
48 import android.os.IBinder;
49 import android.os.Message;
50 import android.os.Process;
51 import android.os.RemoteException;
52 import android.os.ServiceManager;
53 import android.os.UserHandle;
54 import android.os.Vibrator;
55 import android.provider.Settings;
56 import android.telephony.TelephonyManager;
57 import android.text.TextUtils;
58 import android.util.AtomicFile;
59 import android.util.EventLog;
60 import android.util.Log;
61 import android.util.Slog;
62 import android.util.Xml;
63 import android.view.accessibility.AccessibilityEvent;
64 import android.view.accessibility.AccessibilityManager;
65 import android.widget.RemoteViews;
66 import android.widget.Toast;
67 
68 import com.android.internal.statusbar.StatusBarNotification;
69 import com.android.internal.util.FastXmlSerializer;
70 
71 import org.xmlpull.v1.XmlPullParser;
72 import org.xmlpull.v1.XmlPullParserException;
73 import org.xmlpull.v1.XmlSerializer;
74 
75 import java.io.File;
76 import java.io.FileDescriptor;
77 import java.io.FileInputStream;
78 import java.io.FileNotFoundException;
79 import java.io.FileOutputStream;
80 import java.io.IOException;
81 import java.io.PrintWriter;
82 import java.util.ArrayList;
83 import java.util.Arrays;
84 import java.util.HashSet;
85 
86 import libcore.io.IoUtils;
87 
88 
89 /** {@hide} */
90 public class NotificationManagerService extends INotificationManager.Stub
91 {
92     private static final String TAG = "NotificationService";
93     private static final boolean DBG = false;
94 
95     private static final int MAX_PACKAGE_NOTIFICATIONS = 50;
96 
97     // message codes
98     private static final int MESSAGE_TIMEOUT = 2;
99 
100     private static final int LONG_DELAY = 3500; // 3.5 seconds
101     private static final int SHORT_DELAY = 2000; // 2 seconds
102 
103     private static final long[] DEFAULT_VIBRATE_PATTERN = {0, 250, 250, 250};
104     private static final int VIBRATE_PATTERN_MAXLEN = 8 * 2 + 1; // up to eight bumps
105 
106     private static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_NOTIFICATION;
107     private static final boolean SCORE_ONGOING_HIGHER = false;
108 
109     private static final int JUNK_SCORE = -1000;
110     private static final int NOTIFICATION_PRIORITY_MULTIPLIER = 10;
111     private static final int SCORE_DISPLAY_THRESHOLD = Notification.PRIORITY_MIN * NOTIFICATION_PRIORITY_MULTIPLIER;
112 
113     // Notifications with scores below this will not interrupt the user, either via LED or
114     // sound or vibration
115     private static final int SCORE_INTERRUPTION_THRESHOLD =
116             Notification.PRIORITY_LOW * NOTIFICATION_PRIORITY_MULTIPLIER;
117 
118     private static final boolean ENABLE_BLOCKED_NOTIFICATIONS = true;
119     private static final boolean ENABLE_BLOCKED_TOASTS = true;
120 
121     final Context mContext;
122     final IActivityManager mAm;
123     final IBinder mForegroundToken = new Binder();
124 
125     private WorkerHandler mHandler;
126     private StatusBarManagerService mStatusBar;
127     private LightsService.Light mNotificationLight;
128     private LightsService.Light mAttentionLight;
129 
130     private int mDefaultNotificationColor;
131     private int mDefaultNotificationLedOn;
132     private int mDefaultNotificationLedOff;
133 
134     private long[] mDefaultVibrationPattern;
135     private long[] mFallbackVibrationPattern;
136 
137     private boolean mSystemReady;
138     private int mDisabledNotifications;
139 
140     private NotificationRecord mSoundNotification;
141     private NotificationRecord mVibrateNotification;
142 
143     private IAudioService mAudioService;
144     private Vibrator mVibrator;
145 
146     // for enabling and disabling notification pulse behavior
147     private boolean mScreenOn = true;
148     private boolean mInCall = false;
149     private boolean mNotificationPulseEnabled;
150 
151     private final ArrayList<NotificationRecord> mNotificationList =
152             new ArrayList<NotificationRecord>();
153 
154     private ArrayList<ToastRecord> mToastQueue;
155 
156     private ArrayList<NotificationRecord> mLights = new ArrayList<NotificationRecord>();
157     private NotificationRecord mLedNotification;
158 
159     // Notification control database. For now just contains disabled packages.
160     private AtomicFile mPolicyFile;
161     private HashSet<String> mBlockedPackages = new HashSet<String>();
162 
163     private static final int DB_VERSION = 1;
164 
165     private static final String TAG_BODY = "notification-policy";
166     private static final String ATTR_VERSION = "version";
167 
168     private static final String TAG_BLOCKED_PKGS = "blocked-packages";
169     private static final String TAG_PACKAGE = "package";
170     private static final String ATTR_NAME = "name";
171 
loadBlockDb()172     private void loadBlockDb() {
173         synchronized(mBlockedPackages) {
174             if (mPolicyFile == null) {
175                 File dir = new File("/data/system");
176                 mPolicyFile = new AtomicFile(new File(dir, "notification_policy.xml"));
177 
178                 mBlockedPackages.clear();
179 
180                 FileInputStream infile = null;
181                 try {
182                     infile = mPolicyFile.openRead();
183                     final XmlPullParser parser = Xml.newPullParser();
184                     parser.setInput(infile, null);
185 
186                     int type;
187                     String tag;
188                     int version = DB_VERSION;
189                     while ((type = parser.next()) != END_DOCUMENT) {
190                         tag = parser.getName();
191                         if (type == START_TAG) {
192                             if (TAG_BODY.equals(tag)) {
193                                 version = Integer.parseInt(parser.getAttributeValue(null, ATTR_VERSION));
194                             } else if (TAG_BLOCKED_PKGS.equals(tag)) {
195                                 while ((type = parser.next()) != END_DOCUMENT) {
196                                     tag = parser.getName();
197                                     if (TAG_PACKAGE.equals(tag)) {
198                                         mBlockedPackages.add(parser.getAttributeValue(null, ATTR_NAME));
199                                     } else if (TAG_BLOCKED_PKGS.equals(tag) && type == END_TAG) {
200                                         break;
201                                     }
202                                 }
203                             }
204                         }
205                     }
206                 } catch (FileNotFoundException e) {
207                     // No data yet
208                 } catch (IOException e) {
209                     Log.wtf(TAG, "Unable to read blocked notifications database", e);
210                 } catch (NumberFormatException e) {
211                     Log.wtf(TAG, "Unable to parse blocked notifications database", e);
212                 } catch (XmlPullParserException e) {
213                     Log.wtf(TAG, "Unable to parse blocked notifications database", e);
214                 } finally {
215                     IoUtils.closeQuietly(infile);
216                 }
217             }
218         }
219     }
220 
writeBlockDb()221     private void writeBlockDb() {
222         synchronized(mBlockedPackages) {
223             FileOutputStream outfile = null;
224             try {
225                 outfile = mPolicyFile.startWrite();
226 
227                 XmlSerializer out = new FastXmlSerializer();
228                 out.setOutput(outfile, "utf-8");
229 
230                 out.startDocument(null, true);
231 
232                 out.startTag(null, TAG_BODY); {
233                     out.attribute(null, ATTR_VERSION, String.valueOf(DB_VERSION));
234                     out.startTag(null, TAG_BLOCKED_PKGS); {
235                         // write all known network policies
236                         for (String pkg : mBlockedPackages) {
237                             out.startTag(null, TAG_PACKAGE); {
238                                 out.attribute(null, ATTR_NAME, pkg);
239                             } out.endTag(null, TAG_PACKAGE);
240                         }
241                     } out.endTag(null, TAG_BLOCKED_PKGS);
242                 } out.endTag(null, TAG_BODY);
243 
244                 out.endDocument();
245 
246                 mPolicyFile.finishWrite(outfile);
247             } catch (IOException e) {
248                 if (outfile != null) {
249                     mPolicyFile.failWrite(outfile);
250                 }
251             }
252         }
253     }
254 
areNotificationsEnabledForPackage(String pkg)255     public boolean areNotificationsEnabledForPackage(String pkg) {
256         checkCallerIsSystem();
257         return areNotificationsEnabledForPackageInt(pkg);
258     }
259 
260     // Unchecked. Not exposed via Binder, but can be called in the course of enqueue*().
areNotificationsEnabledForPackageInt(String pkg)261     private boolean areNotificationsEnabledForPackageInt(String pkg) {
262         final boolean enabled = !mBlockedPackages.contains(pkg);
263         if (DBG) {
264             Slog.v(TAG, "notifications are " + (enabled?"en":"dis") + "abled for " + pkg);
265         }
266         return enabled;
267     }
268 
setNotificationsEnabledForPackage(String pkg, boolean enabled)269     public void setNotificationsEnabledForPackage(String pkg, boolean enabled) {
270         checkCallerIsSystem();
271         if (DBG) {
272             Slog.v(TAG, (enabled?"en":"dis") + "abling notifications for " + pkg);
273         }
274         if (enabled) {
275             mBlockedPackages.remove(pkg);
276         } else {
277             mBlockedPackages.add(pkg);
278 
279             // Now, cancel any outstanding notifications that are part of a just-disabled app
280             if (ENABLE_BLOCKED_NOTIFICATIONS) {
281                 synchronized (mNotificationList) {
282                     final int N = mNotificationList.size();
283                     for (int i=0; i<N; i++) {
284                         final NotificationRecord r = mNotificationList.get(i);
285                         if (r.pkg.equals(pkg)) {
286                             cancelNotificationLocked(r, false);
287                         }
288                     }
289                 }
290             }
291             // Don't bother canceling toasts, they'll go away soon enough.
292         }
293         writeBlockDb();
294     }
295 
296 
idDebugString(Context baseContext, String packageName, int id)297     private static String idDebugString(Context baseContext, String packageName, int id) {
298         Context c = null;
299 
300         if (packageName != null) {
301             try {
302                 c = baseContext.createPackageContext(packageName, 0);
303             } catch (NameNotFoundException e) {
304                 c = baseContext;
305             }
306         } else {
307             c = baseContext;
308         }
309 
310         String pkg;
311         String type;
312         String name;
313 
314         Resources r = c.getResources();
315         try {
316             return r.getResourceName(id);
317         } catch (Resources.NotFoundException e) {
318             return "<name unknown>";
319         }
320     }
321 
322     private static final class NotificationRecord
323     {
324         final String pkg;
325         final String tag;
326         final int id;
327         final int uid;
328         final int initialPid;
329         final int userId;
330         final Notification notification;
331         final int score;
332         IBinder statusBarKey;
333 
NotificationRecord(String pkg, String tag, int id, int uid, int initialPid, int userId, int score, Notification notification)334         NotificationRecord(String pkg, String tag, int id, int uid, int initialPid,
335                 int userId, int score, Notification notification)
336         {
337             this.pkg = pkg;
338             this.tag = tag;
339             this.id = id;
340             this.uid = uid;
341             this.initialPid = initialPid;
342             this.userId = userId;
343             this.score = score;
344             this.notification = notification;
345         }
346 
dump(PrintWriter pw, String prefix, Context baseContext)347         void dump(PrintWriter pw, String prefix, Context baseContext) {
348             pw.println(prefix + this);
349             pw.println(prefix + "  icon=0x" + Integer.toHexString(notification.icon)
350                     + " / " + idDebugString(baseContext, this.pkg, notification.icon));
351             pw.println(prefix + "  pri=" + notification.priority);
352             pw.println(prefix + "  score=" + this.score);
353             pw.println(prefix + "  contentIntent=" + notification.contentIntent);
354             pw.println(prefix + "  deleteIntent=" + notification.deleteIntent);
355             pw.println(prefix + "  tickerText=" + notification.tickerText);
356             pw.println(prefix + "  contentView=" + notification.contentView);
357             pw.println(prefix + "  uid=" + uid + " userId=" + userId);
358             pw.println(prefix + "  defaults=0x" + Integer.toHexString(notification.defaults));
359             pw.println(prefix + "  flags=0x" + Integer.toHexString(notification.flags));
360             pw.println(prefix + "  sound=" + notification.sound);
361             pw.println(prefix + "  vibrate=" + Arrays.toString(notification.vibrate));
362             pw.println(prefix + "  ledARGB=0x" + Integer.toHexString(notification.ledARGB)
363                     + " ledOnMS=" + notification.ledOnMS
364                     + " ledOffMS=" + notification.ledOffMS);
365         }
366 
367         @Override
toString()368         public final String toString()
369         {
370             return "NotificationRecord{"
371                 + Integer.toHexString(System.identityHashCode(this))
372                 + " pkg=" + pkg
373                 + " id=" + Integer.toHexString(id)
374                 + " tag=" + tag
375                 + " score=" + score
376                 + "}";
377         }
378     }
379 
380     private static final class ToastRecord
381     {
382         final int pid;
383         final String pkg;
384         final ITransientNotification callback;
385         int duration;
386 
ToastRecord(int pid, String pkg, ITransientNotification callback, int duration)387         ToastRecord(int pid, String pkg, ITransientNotification callback, int duration)
388         {
389             this.pid = pid;
390             this.pkg = pkg;
391             this.callback = callback;
392             this.duration = duration;
393         }
394 
update(int duration)395         void update(int duration) {
396             this.duration = duration;
397         }
398 
dump(PrintWriter pw, String prefix)399         void dump(PrintWriter pw, String prefix) {
400             pw.println(prefix + this);
401         }
402 
403         @Override
toString()404         public final String toString()
405         {
406             return "ToastRecord{"
407                 + Integer.toHexString(System.identityHashCode(this))
408                 + " pkg=" + pkg
409                 + " callback=" + callback
410                 + " duration=" + duration;
411         }
412     }
413 
414     private StatusBarManagerService.NotificationCallbacks mNotificationCallbacks
415             = new StatusBarManagerService.NotificationCallbacks() {
416 
417         public void onSetDisabled(int status) {
418             synchronized (mNotificationList) {
419                 mDisabledNotifications = status;
420                 if ((mDisabledNotifications & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) {
421                     // cancel whatever's going on
422                     long identity = Binder.clearCallingIdentity();
423                     try {
424                         final IRingtonePlayer player = mAudioService.getRingtonePlayer();
425                         if (player != null) {
426                             player.stopAsync();
427                         }
428                     } catch (RemoteException e) {
429                     } finally {
430                         Binder.restoreCallingIdentity(identity);
431                     }
432 
433                     identity = Binder.clearCallingIdentity();
434                     try {
435                         mVibrator.cancel();
436                     } finally {
437                         Binder.restoreCallingIdentity(identity);
438                     }
439                 }
440             }
441         }
442 
443         public void onClearAll() {
444             // XXX to be totally correct, the caller should tell us which user
445             // this is for.
446             cancelAll(ActivityManager.getCurrentUser());
447         }
448 
449         public void onNotificationClick(String pkg, String tag, int id) {
450             // XXX to be totally correct, the caller should tell us which user
451             // this is for.
452             cancelNotification(pkg, tag, id, Notification.FLAG_AUTO_CANCEL,
453                     Notification.FLAG_FOREGROUND_SERVICE, false,
454                     ActivityManager.getCurrentUser());
455         }
456 
457         public void onNotificationClear(String pkg, String tag, int id) {
458             // XXX to be totally correct, the caller should tell us which user
459             // this is for.
460             cancelNotification(pkg, tag, id, 0,
461                 Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
462                 true, ActivityManager.getCurrentUser());
463         }
464 
465         public void onPanelRevealed() {
466             synchronized (mNotificationList) {
467                 // sound
468                 mSoundNotification = null;
469 
470                 long identity = Binder.clearCallingIdentity();
471                 try {
472                     final IRingtonePlayer player = mAudioService.getRingtonePlayer();
473                     if (player != null) {
474                         player.stopAsync();
475                     }
476                 } catch (RemoteException e) {
477                 } finally {
478                     Binder.restoreCallingIdentity(identity);
479                 }
480 
481                 // vibrate
482                 mVibrateNotification = null;
483                 identity = Binder.clearCallingIdentity();
484                 try {
485                     mVibrator.cancel();
486                 } finally {
487                     Binder.restoreCallingIdentity(identity);
488                 }
489 
490                 // light
491                 mLights.clear();
492                 mLedNotification = null;
493                 updateLightsLocked();
494             }
495         }
496 
497         public void onNotificationError(String pkg, String tag, int id,
498                 int uid, int initialPid, String message) {
499             Slog.d(TAG, "onNotification error pkg=" + pkg + " tag=" + tag + " id=" + id
500                     + "; will crashApplication(uid=" + uid + ", pid=" + initialPid + ")");
501             // XXX to be totally correct, the caller should tell us which user
502             // this is for.
503             cancelNotification(pkg, tag, id, 0, 0, false, UserHandle.getUserId(uid));
504             long ident = Binder.clearCallingIdentity();
505             try {
506                 ActivityManagerNative.getDefault().crashApplication(uid, initialPid, pkg,
507                         "Bad notification posted from package " + pkg
508                         + ": " + message);
509             } catch (RemoteException e) {
510             }
511             Binder.restoreCallingIdentity(ident);
512         }
513     };
514 
515     private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
516         @Override
517         public void onReceive(Context context, Intent intent) {
518             String action = intent.getAction();
519 
520             boolean queryRestart = false;
521             boolean packageChanged = false;
522 
523             if (action.equals(Intent.ACTION_PACKAGE_REMOVED)
524                     || action.equals(Intent.ACTION_PACKAGE_RESTARTED)
525                     || (packageChanged=action.equals(Intent.ACTION_PACKAGE_CHANGED))
526                     || (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART))
527                     || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
528                 String pkgList[] = null;
529                 if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
530                     pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
531                 } else if (queryRestart) {
532                     pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
533                 } else {
534                     Uri uri = intent.getData();
535                     if (uri == null) {
536                         return;
537                     }
538                     String pkgName = uri.getSchemeSpecificPart();
539                     if (pkgName == null) {
540                         return;
541                     }
542                     if (packageChanged) {
543                         // We cancel notifications for packages which have just been disabled
544                         final int enabled = mContext.getPackageManager()
545                                 .getApplicationEnabledSetting(pkgName);
546                         if (enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED
547                                 || enabled == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
548                             return;
549                         }
550                     }
551                     pkgList = new String[]{pkgName};
552                 }
553                 if (pkgList != null && (pkgList.length > 0)) {
554                     for (String pkgName : pkgList) {
555                         cancelAllNotificationsInt(pkgName, 0, 0, !queryRestart,
556                                 UserHandle.USER_ALL);
557                     }
558                 }
559             } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
560                 // Keep track of screen on/off state, but do not turn off the notification light
561                 // until user passes through the lock screen or views the notification.
562                 mScreenOn = true;
563             } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
564                 mScreenOn = false;
565             } else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) {
566                 mInCall = (intent.getStringExtra(TelephonyManager.EXTRA_STATE).equals(
567                         TelephonyManager.EXTRA_STATE_OFFHOOK));
568                 updateNotificationPulse();
569             } else if (action.equals(Intent.ACTION_USER_STOPPED)) {
570                 int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
571                 if (userHandle >= 0) {
572                     cancelAllNotificationsInt(null, 0, 0, true, userHandle);
573                 }
574             } else if (action.equals(Intent.ACTION_USER_PRESENT)) {
575                 // turn off LED when user passes through lock screen
576                 mNotificationLight.turnOff();
577             }
578         }
579     };
580 
581     class SettingsObserver extends ContentObserver {
SettingsObserver(Handler handler)582         SettingsObserver(Handler handler) {
583             super(handler);
584         }
585 
observe()586         void observe() {
587             ContentResolver resolver = mContext.getContentResolver();
588             resolver.registerContentObserver(Settings.System.getUriFor(
589                     Settings.System.NOTIFICATION_LIGHT_PULSE), false, this);
590             update();
591         }
592 
onChange(boolean selfChange)593         @Override public void onChange(boolean selfChange) {
594             update();
595         }
596 
update()597         public void update() {
598             ContentResolver resolver = mContext.getContentResolver();
599             boolean pulseEnabled = Settings.System.getInt(resolver,
600                         Settings.System.NOTIFICATION_LIGHT_PULSE, 0) != 0;
601             if (mNotificationPulseEnabled != pulseEnabled) {
602                 mNotificationPulseEnabled = pulseEnabled;
603                 updateNotificationPulse();
604             }
605         }
606     }
607 
getLongArray(Resources r, int resid, int maxlen, long[] def)608     static long[] getLongArray(Resources r, int resid, int maxlen, long[] def) {
609         int[] ar = r.getIntArray(resid);
610         if (ar == null) {
611             return def;
612         }
613         final int len = ar.length > maxlen ? maxlen : ar.length;
614         long[] out = new long[len];
615         for (int i=0; i<len; i++) {
616             out[i] = ar[i];
617         }
618         return out;
619     }
620 
NotificationManagerService(Context context, StatusBarManagerService statusBar, LightsService lights)621     NotificationManagerService(Context context, StatusBarManagerService statusBar,
622             LightsService lights)
623     {
624         super();
625         mContext = context;
626         mVibrator = (Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE);
627         mAm = ActivityManagerNative.getDefault();
628         mToastQueue = new ArrayList<ToastRecord>();
629         mHandler = new WorkerHandler();
630 
631         loadBlockDb();
632 
633         mStatusBar = statusBar;
634         statusBar.setNotificationCallbacks(mNotificationCallbacks);
635 
636         mNotificationLight = lights.getLight(LightsService.LIGHT_ID_NOTIFICATIONS);
637         mAttentionLight = lights.getLight(LightsService.LIGHT_ID_ATTENTION);
638 
639         Resources resources = mContext.getResources();
640         mDefaultNotificationColor = resources.getColor(
641                 com.android.internal.R.color.config_defaultNotificationColor);
642         mDefaultNotificationLedOn = resources.getInteger(
643                 com.android.internal.R.integer.config_defaultNotificationLedOn);
644         mDefaultNotificationLedOff = resources.getInteger(
645                 com.android.internal.R.integer.config_defaultNotificationLedOff);
646 
647         mDefaultVibrationPattern = getLongArray(resources,
648                 com.android.internal.R.array.config_defaultNotificationVibePattern,
649                 VIBRATE_PATTERN_MAXLEN,
650                 DEFAULT_VIBRATE_PATTERN);
651 
652         mFallbackVibrationPattern = getLongArray(resources,
653                 com.android.internal.R.array.config_notificationFallbackVibePattern,
654                 VIBRATE_PATTERN_MAXLEN,
655                 DEFAULT_VIBRATE_PATTERN);
656 
657         // Don't start allowing notifications until the setup wizard has run once.
658         // After that, including subsequent boots, init with notifications turned on.
659         // This works on the first boot because the setup wizard will toggle this
660         // flag at least once and we'll go back to 0 after that.
661         if (0 == Settings.Global.getInt(mContext.getContentResolver(),
662                     Settings.Global.DEVICE_PROVISIONED, 0)) {
663             mDisabledNotifications = StatusBarManager.DISABLE_NOTIFICATION_ALERTS;
664         }
665 
666         // register for various Intents
667         IntentFilter filter = new IntentFilter();
668         filter.addAction(Intent.ACTION_SCREEN_ON);
669         filter.addAction(Intent.ACTION_SCREEN_OFF);
670         filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
671         filter.addAction(Intent.ACTION_USER_PRESENT);
672         filter.addAction(Intent.ACTION_USER_STOPPED);
673         mContext.registerReceiver(mIntentReceiver, filter);
674         IntentFilter pkgFilter = new IntentFilter();
675         pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
676         pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
677         pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
678         pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
679         pkgFilter.addDataScheme("package");
680         mContext.registerReceiver(mIntentReceiver, pkgFilter);
681         IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
682         mContext.registerReceiver(mIntentReceiver, sdFilter);
683 
684         SettingsObserver observer = new SettingsObserver(mHandler);
685         observer.observe();
686     }
687 
systemReady()688     void systemReady() {
689         mAudioService = IAudioService.Stub.asInterface(
690                 ServiceManager.getService(Context.AUDIO_SERVICE));
691 
692         // no beeping until we're basically done booting
693         mSystemReady = true;
694     }
695 
696     // Toasts
697     // ============================================================================
enqueueToast(String pkg, ITransientNotification callback, int duration)698     public void enqueueToast(String pkg, ITransientNotification callback, int duration)
699     {
700         if (DBG) Slog.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback + " duration=" + duration);
701 
702         if (pkg == null || callback == null) {
703             Slog.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback);
704             return ;
705         }
706 
707         final boolean isSystemToast = ("android".equals(pkg));
708 
709         if (ENABLE_BLOCKED_TOASTS && !isSystemToast && !areNotificationsEnabledForPackageInt(pkg)) {
710             Slog.e(TAG, "Suppressing toast from package " + pkg + " by user request.");
711             return;
712         }
713 
714         synchronized (mToastQueue) {
715             int callingPid = Binder.getCallingPid();
716             long callingId = Binder.clearCallingIdentity();
717             try {
718                 ToastRecord record;
719                 int index = indexOfToastLocked(pkg, callback);
720                 // If it's already in the queue, we update it in place, we don't
721                 // move it to the end of the queue.
722                 if (index >= 0) {
723                     record = mToastQueue.get(index);
724                     record.update(duration);
725                 } else {
726                     // Limit the number of toasts that any given package except the android
727                     // package can enqueue.  Prevents DOS attacks and deals with leaks.
728                     if (!isSystemToast) {
729                         int count = 0;
730                         final int N = mToastQueue.size();
731                         for (int i=0; i<N; i++) {
732                              final ToastRecord r = mToastQueue.get(i);
733                              if (r.pkg.equals(pkg)) {
734                                  count++;
735                                  if (count >= MAX_PACKAGE_NOTIFICATIONS) {
736                                      Slog.e(TAG, "Package has already posted " + count
737                                             + " toasts. Not showing more. Package=" + pkg);
738                                      return;
739                                  }
740                              }
741                         }
742                     }
743 
744                     record = new ToastRecord(callingPid, pkg, callback, duration);
745                     mToastQueue.add(record);
746                     index = mToastQueue.size() - 1;
747                     keepProcessAliveLocked(callingPid);
748                 }
749                 // If it's at index 0, it's the current toast.  It doesn't matter if it's
750                 // new or just been updated.  Call back and tell it to show itself.
751                 // If the callback fails, this will remove it from the list, so don't
752                 // assume that it's valid after this.
753                 if (index == 0) {
754                     showNextToastLocked();
755                 }
756             } finally {
757                 Binder.restoreCallingIdentity(callingId);
758             }
759         }
760     }
761 
cancelToast(String pkg, ITransientNotification callback)762     public void cancelToast(String pkg, ITransientNotification callback) {
763         Slog.i(TAG, "cancelToast pkg=" + pkg + " callback=" + callback);
764 
765         if (pkg == null || callback == null) {
766             Slog.e(TAG, "Not cancelling notification. pkg=" + pkg + " callback=" + callback);
767             return ;
768         }
769 
770         synchronized (mToastQueue) {
771             long callingId = Binder.clearCallingIdentity();
772             try {
773                 int index = indexOfToastLocked(pkg, callback);
774                 if (index >= 0) {
775                     cancelToastLocked(index);
776                 } else {
777                     Slog.w(TAG, "Toast already cancelled. pkg=" + pkg + " callback=" + callback);
778                 }
779             } finally {
780                 Binder.restoreCallingIdentity(callingId);
781             }
782         }
783     }
784 
showNextToastLocked()785     private void showNextToastLocked() {
786         ToastRecord record = mToastQueue.get(0);
787         while (record != null) {
788             if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback);
789             try {
790                 record.callback.show();
791                 scheduleTimeoutLocked(record, false);
792                 return;
793             } catch (RemoteException e) {
794                 Slog.w(TAG, "Object died trying to show notification " + record.callback
795                         + " in package " + record.pkg);
796                 // remove it from the list and let the process die
797                 int index = mToastQueue.indexOf(record);
798                 if (index >= 0) {
799                     mToastQueue.remove(index);
800                 }
801                 keepProcessAliveLocked(record.pid);
802                 if (mToastQueue.size() > 0) {
803                     record = mToastQueue.get(0);
804                 } else {
805                     record = null;
806                 }
807             }
808         }
809     }
810 
cancelToastLocked(int index)811     private void cancelToastLocked(int index) {
812         ToastRecord record = mToastQueue.get(index);
813         try {
814             record.callback.hide();
815         } catch (RemoteException e) {
816             Slog.w(TAG, "Object died trying to hide notification " + record.callback
817                     + " in package " + record.pkg);
818             // don't worry about this, we're about to remove it from
819             // the list anyway
820         }
821         mToastQueue.remove(index);
822         keepProcessAliveLocked(record.pid);
823         if (mToastQueue.size() > 0) {
824             // Show the next one. If the callback fails, this will remove
825             // it from the list, so don't assume that the list hasn't changed
826             // after this point.
827             showNextToastLocked();
828         }
829     }
830 
scheduleTimeoutLocked(ToastRecord r, boolean immediate)831     private void scheduleTimeoutLocked(ToastRecord r, boolean immediate)
832     {
833         Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r);
834         long delay = immediate ? 0 : (r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY);
835         mHandler.removeCallbacksAndMessages(r);
836         mHandler.sendMessageDelayed(m, delay);
837     }
838 
handleTimeout(ToastRecord record)839     private void handleTimeout(ToastRecord record)
840     {
841         if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback);
842         synchronized (mToastQueue) {
843             int index = indexOfToastLocked(record.pkg, record.callback);
844             if (index >= 0) {
845                 cancelToastLocked(index);
846             }
847         }
848     }
849 
850     // lock on mToastQueue
indexOfToastLocked(String pkg, ITransientNotification callback)851     private int indexOfToastLocked(String pkg, ITransientNotification callback)
852     {
853         IBinder cbak = callback.asBinder();
854         ArrayList<ToastRecord> list = mToastQueue;
855         int len = list.size();
856         for (int i=0; i<len; i++) {
857             ToastRecord r = list.get(i);
858             if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) {
859                 return i;
860             }
861         }
862         return -1;
863     }
864 
865     // lock on mToastQueue
keepProcessAliveLocked(int pid)866     private void keepProcessAliveLocked(int pid)
867     {
868         int toastCount = 0; // toasts from this pid
869         ArrayList<ToastRecord> list = mToastQueue;
870         int N = list.size();
871         for (int i=0; i<N; i++) {
872             ToastRecord r = list.get(i);
873             if (r.pid == pid) {
874                 toastCount++;
875             }
876         }
877         try {
878             mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0);
879         } catch (RemoteException e) {
880             // Shouldn't happen.
881         }
882     }
883 
884     private final class WorkerHandler extends Handler
885     {
886         @Override
handleMessage(Message msg)887         public void handleMessage(Message msg)
888         {
889             switch (msg.what)
890             {
891                 case MESSAGE_TIMEOUT:
892                     handleTimeout((ToastRecord)msg.obj);
893                     break;
894             }
895         }
896     }
897 
898 
899     // Notifications
900     // ============================================================================
enqueueNotificationWithTag(String pkg, String tag, int id, Notification notification, int[] idOut, int userId)901     public void enqueueNotificationWithTag(String pkg, String tag, int id, Notification notification,
902             int[] idOut, int userId)
903     {
904         enqueueNotificationInternal(pkg, Binder.getCallingUid(), Binder.getCallingPid(),
905                 tag, id, notification, idOut, userId);
906     }
907 
clamp(int x, int low, int high)908     private final static int clamp(int x, int low, int high) {
909         return (x < low) ? low : ((x > high) ? high : x);
910     }
911 
912     // Not exposed via Binder; for system use only (otherwise malicious apps could spoof the
913     // uid/pid of another application)
enqueueNotificationInternal(String pkg, int callingUid, int callingPid, String tag, int id, Notification notification, int[] idOut, int userId)914     public void enqueueNotificationInternal(String pkg, int callingUid, int callingPid,
915             String tag, int id, Notification notification, int[] idOut, int userId)
916     {
917         if (DBG) {
918             Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id + " notification=" + notification);
919         }
920         checkCallerIsSystemOrSameApp(pkg);
921         final boolean isSystemNotification = ("android".equals(pkg));
922 
923         userId = ActivityManager.handleIncomingUser(callingPid,
924                 callingUid, userId, true, false, "enqueueNotification", pkg);
925         final UserHandle user = new UserHandle(userId);
926 
927         // Limit the number of notifications that any given package except the android
928         // package can enqueue.  Prevents DOS attacks and deals with leaks.
929         if (!isSystemNotification) {
930             synchronized (mNotificationList) {
931                 int count = 0;
932                 final int N = mNotificationList.size();
933                 for (int i=0; i<N; i++) {
934                     final NotificationRecord r = mNotificationList.get(i);
935                     if (r.pkg.equals(pkg) && r.userId == userId) {
936                         count++;
937                         if (count >= MAX_PACKAGE_NOTIFICATIONS) {
938                             Slog.e(TAG, "Package has already posted " + count
939                                     + " notifications.  Not showing more.  package=" + pkg);
940                             return;
941                         }
942                     }
943                 }
944             }
945         }
946 
947         // This conditional is a dirty hack to limit the logging done on
948         //     behalf of the download manager without affecting other apps.
949         if (!pkg.equals("com.android.providers.downloads")
950                 || Log.isLoggable("DownloadManager", Log.VERBOSE)) {
951             EventLog.writeEvent(EventLogTags.NOTIFICATION_ENQUEUE, pkg, id, tag, userId,
952                     notification.toString());
953         }
954 
955         if (pkg == null || notification == null) {
956             throw new IllegalArgumentException("null not allowed: pkg=" + pkg
957                     + " id=" + id + " notification=" + notification);
958         }
959         if (notification.icon != 0) {
960             if (notification.contentView == null) {
961                 throw new IllegalArgumentException("contentView required: pkg=" + pkg
962                         + " id=" + id + " notification=" + notification);
963             }
964         }
965 
966         // === Scoring ===
967 
968         // 0. Sanitize inputs
969         notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN, Notification.PRIORITY_MAX);
970         // Migrate notification flags to scores
971         if (0 != (notification.flags & Notification.FLAG_HIGH_PRIORITY)) {
972             if (notification.priority < Notification.PRIORITY_MAX) notification.priority = Notification.PRIORITY_MAX;
973         } else if (SCORE_ONGOING_HIGHER && 0 != (notification.flags & Notification.FLAG_ONGOING_EVENT)) {
974             if (notification.priority < Notification.PRIORITY_HIGH) notification.priority = Notification.PRIORITY_HIGH;
975         }
976 
977         // 1. initial score: buckets of 10, around the app
978         int score = notification.priority * NOTIFICATION_PRIORITY_MULTIPLIER; //[-20..20]
979 
980         // 2. Consult external heuristics (TBD)
981 
982         // 3. Apply local rules
983 
984         // blocked apps
985         if (ENABLE_BLOCKED_NOTIFICATIONS && !isSystemNotification && !areNotificationsEnabledForPackageInt(pkg)) {
986             score = JUNK_SCORE;
987             Slog.e(TAG, "Suppressing notification from package " + pkg + " by user request.");
988         }
989 
990         if (DBG) {
991             Slog.v(TAG, "Assigned score=" + score + " to " + notification);
992         }
993 
994         if (score < SCORE_DISPLAY_THRESHOLD) {
995             // Notification will be blocked because the score is too low.
996             return;
997         }
998 
999         // Should this notification make noise, vibe, or use the LED?
1000         final boolean canInterrupt = (score >= SCORE_INTERRUPTION_THRESHOLD);
1001 
1002         synchronized (mNotificationList) {
1003             NotificationRecord r = new NotificationRecord(pkg, tag, id,
1004                     callingUid, callingPid, userId,
1005                     score,
1006                     notification);
1007             NotificationRecord old = null;
1008 
1009             int index = indexOfNotificationLocked(pkg, tag, id, userId);
1010             if (index < 0) {
1011                 mNotificationList.add(r);
1012             } else {
1013                 old = mNotificationList.remove(index);
1014                 mNotificationList.add(index, r);
1015                 // Make sure we don't lose the foreground service state.
1016                 if (old != null) {
1017                     notification.flags |=
1018                         old.notification.flags&Notification.FLAG_FOREGROUND_SERVICE;
1019                 }
1020             }
1021 
1022             // Ensure if this is a foreground service that the proper additional
1023             // flags are set.
1024             if ((notification.flags&Notification.FLAG_FOREGROUND_SERVICE) != 0) {
1025                 notification.flags |= Notification.FLAG_ONGOING_EVENT
1026                         | Notification.FLAG_NO_CLEAR;
1027             }
1028 
1029             final int currentUser;
1030             final long token = Binder.clearCallingIdentity();
1031             try {
1032                 currentUser = ActivityManager.getCurrentUser();
1033             } finally {
1034                 Binder.restoreCallingIdentity(token);
1035             }
1036 
1037             if (notification.icon != 0) {
1038                 final StatusBarNotification n = new StatusBarNotification(
1039                         pkg, id, tag, r.uid, r.initialPid, score, notification, user);
1040                 if (old != null && old.statusBarKey != null) {
1041                     r.statusBarKey = old.statusBarKey;
1042                     long identity = Binder.clearCallingIdentity();
1043                     try {
1044                         mStatusBar.updateNotification(r.statusBarKey, n);
1045                     }
1046                     finally {
1047                         Binder.restoreCallingIdentity(identity);
1048                     }
1049                 } else {
1050                     long identity = Binder.clearCallingIdentity();
1051                     try {
1052                         r.statusBarKey = mStatusBar.addNotification(n);
1053                         if ((n.notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0
1054                                 && canInterrupt) {
1055                             mAttentionLight.pulse();
1056                         }
1057                     }
1058                     finally {
1059                         Binder.restoreCallingIdentity(identity);
1060                     }
1061                 }
1062                 // Send accessibility events only for the current user.
1063                 if (currentUser == userId) {
1064                     sendAccessibilityEvent(notification, pkg);
1065                 }
1066             } else {
1067                 Slog.e(TAG, "Ignoring notification with icon==0: " + notification);
1068                 if (old != null && old.statusBarKey != null) {
1069                     long identity = Binder.clearCallingIdentity();
1070                     try {
1071                         mStatusBar.removeNotification(old.statusBarKey);
1072                     }
1073                     finally {
1074                         Binder.restoreCallingIdentity(identity);
1075                     }
1076                 }
1077             }
1078 
1079             // If we're not supposed to beep, vibrate, etc. then don't.
1080             if (((mDisabledNotifications & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) == 0)
1081                     && (!(old != null
1082                         && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 ))
1083                     && (r.userId == UserHandle.USER_ALL ||
1084                         (r.userId == userId && r.userId == currentUser))
1085                     && canInterrupt
1086                     && mSystemReady) {
1087 
1088                 final AudioManager audioManager = (AudioManager) mContext
1089                 .getSystemService(Context.AUDIO_SERVICE);
1090 
1091                 // sound
1092                 final boolean useDefaultSound =
1093                     (notification.defaults & Notification.DEFAULT_SOUND) != 0;
1094 
1095                 Uri soundUri = null;
1096                 boolean hasValidSound = false;
1097 
1098                 if (useDefaultSound) {
1099                     soundUri = Settings.System.DEFAULT_NOTIFICATION_URI;
1100 
1101                     // check to see if the default notification sound is silent
1102                     ContentResolver resolver = mContext.getContentResolver();
1103                     hasValidSound = Settings.System.getString(resolver,
1104                            Settings.System.NOTIFICATION_SOUND) != null;
1105                 } else if (notification.sound != null) {
1106                     soundUri = notification.sound;
1107                     hasValidSound = (soundUri != null);
1108                 }
1109 
1110                 if (hasValidSound) {
1111                     boolean looping = (notification.flags & Notification.FLAG_INSISTENT) != 0;
1112                     int audioStreamType;
1113                     if (notification.audioStreamType >= 0) {
1114                         audioStreamType = notification.audioStreamType;
1115                     } else {
1116                         audioStreamType = DEFAULT_STREAM_TYPE;
1117                     }
1118                     mSoundNotification = r;
1119                     // do not play notifications if stream volume is 0
1120                     // (typically because ringer mode is silent) or if speech recognition is active.
1121                     if ((audioManager.getStreamVolume(audioStreamType) != 0)
1122                             && !audioManager.isSpeechRecognitionActive()) {
1123                         final long identity = Binder.clearCallingIdentity();
1124                         try {
1125                             final IRingtonePlayer player = mAudioService.getRingtonePlayer();
1126                             if (player != null) {
1127                                 player.playAsync(soundUri, user, looping, audioStreamType);
1128                             }
1129                         } catch (RemoteException e) {
1130                         } finally {
1131                             Binder.restoreCallingIdentity(identity);
1132                         }
1133                     }
1134                 }
1135 
1136                 // vibrate
1137                 // Does the notification want to specify its own vibration?
1138                 final boolean hasCustomVibrate = notification.vibrate != null;
1139 
1140                 // new in 4.2: if there was supposed to be a sound and we're in vibrate mode,
1141                 // and no other vibration is specified, we fall back to vibration
1142                 final boolean convertSoundToVibration =
1143                            !hasCustomVibrate
1144                         && hasValidSound
1145                         && (audioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE);
1146 
1147                 // The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback.
1148                 final boolean useDefaultVibrate =
1149                         (notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
1150 
1151                 if ((useDefaultVibrate || convertSoundToVibration || hasCustomVibrate)
1152                         && !(audioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT)) {
1153                     mVibrateNotification = r;
1154 
1155                     if (useDefaultVibrate || convertSoundToVibration) {
1156                         // Escalate privileges so we can use the vibrator even if the notifying app
1157                         // does not have the VIBRATE permission.
1158                         long identity = Binder.clearCallingIdentity();
1159                         try {
1160                             mVibrator.vibrate(useDefaultVibrate ? mDefaultVibrationPattern
1161                                                                 : mFallbackVibrationPattern,
1162                                 ((notification.flags & Notification.FLAG_INSISTENT) != 0) ? 0: -1);
1163                         } finally {
1164                             Binder.restoreCallingIdentity(identity);
1165                         }
1166                     } else if (notification.vibrate.length > 1) {
1167                         // If you want your own vibration pattern, you need the VIBRATE permission
1168                         mVibrator.vibrate(notification.vibrate,
1169                             ((notification.flags & Notification.FLAG_INSISTENT) != 0) ? 0: -1);
1170                     }
1171                 }
1172             }
1173 
1174             // this option doesn't shut off the lights
1175 
1176             // light
1177             // the most recent thing gets the light
1178             mLights.remove(old);
1179             if (mLedNotification == old) {
1180                 mLedNotification = null;
1181             }
1182             //Slog.i(TAG, "notification.lights="
1183             //        + ((old.notification.lights.flags & Notification.FLAG_SHOW_LIGHTS) != 0));
1184             if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0
1185                     && canInterrupt) {
1186                 mLights.add(r);
1187                 updateLightsLocked();
1188             } else {
1189                 if (old != null
1190                         && ((old.notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0)) {
1191                     updateLightsLocked();
1192                 }
1193             }
1194         }
1195 
1196         idOut[0] = id;
1197     }
1198 
sendAccessibilityEvent(Notification notification, CharSequence packageName)1199     private void sendAccessibilityEvent(Notification notification, CharSequence packageName) {
1200         AccessibilityManager manager = AccessibilityManager.getInstance(mContext);
1201         if (!manager.isEnabled()) {
1202             return;
1203         }
1204 
1205         AccessibilityEvent event =
1206             AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
1207         event.setPackageName(packageName);
1208         event.setClassName(Notification.class.getName());
1209         event.setParcelableData(notification);
1210         CharSequence tickerText = notification.tickerText;
1211         if (!TextUtils.isEmpty(tickerText)) {
1212             event.getText().add(tickerText);
1213         }
1214 
1215         manager.sendAccessibilityEvent(event);
1216     }
1217 
cancelNotificationLocked(NotificationRecord r, boolean sendDelete)1218     private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete) {
1219         // tell the app
1220         if (sendDelete) {
1221             if (r.notification.deleteIntent != null) {
1222                 try {
1223                     r.notification.deleteIntent.send();
1224                 } catch (PendingIntent.CanceledException ex) {
1225                     // do nothing - there's no relevant way to recover, and
1226                     //     no reason to let this propagate
1227                     Slog.w(TAG, "canceled PendingIntent for " + r.pkg, ex);
1228                 }
1229             }
1230         }
1231 
1232         // status bar
1233         if (r.notification.icon != 0) {
1234             long identity = Binder.clearCallingIdentity();
1235             try {
1236                 mStatusBar.removeNotification(r.statusBarKey);
1237             }
1238             finally {
1239                 Binder.restoreCallingIdentity(identity);
1240             }
1241             r.statusBarKey = null;
1242         }
1243 
1244         // sound
1245         if (mSoundNotification == r) {
1246             mSoundNotification = null;
1247             final long identity = Binder.clearCallingIdentity();
1248             try {
1249                 final IRingtonePlayer player = mAudioService.getRingtonePlayer();
1250                 if (player != null) {
1251                     player.stopAsync();
1252                 }
1253             } catch (RemoteException e) {
1254             } finally {
1255                 Binder.restoreCallingIdentity(identity);
1256             }
1257         }
1258 
1259         // vibrate
1260         if (mVibrateNotification == r) {
1261             mVibrateNotification = null;
1262             long identity = Binder.clearCallingIdentity();
1263             try {
1264                 mVibrator.cancel();
1265             }
1266             finally {
1267                 Binder.restoreCallingIdentity(identity);
1268             }
1269         }
1270 
1271         // light
1272         mLights.remove(r);
1273         if (mLedNotification == r) {
1274             mLedNotification = null;
1275         }
1276     }
1277 
1278     /**
1279      * Cancels a notification ONLY if it has all of the {@code mustHaveFlags}
1280      * and none of the {@code mustNotHaveFlags}.
1281      */
cancelNotification(String pkg, String tag, int id, int mustHaveFlags, int mustNotHaveFlags, boolean sendDelete, int userId)1282     private void cancelNotification(String pkg, String tag, int id, int mustHaveFlags,
1283             int mustNotHaveFlags, boolean sendDelete, int userId) {
1284         EventLog.writeEvent(EventLogTags.NOTIFICATION_CANCEL, pkg, id, tag, userId,
1285                 mustHaveFlags, mustNotHaveFlags);
1286 
1287         synchronized (mNotificationList) {
1288             int index = indexOfNotificationLocked(pkg, tag, id, userId);
1289             if (index >= 0) {
1290                 NotificationRecord r = mNotificationList.get(index);
1291 
1292                 if ((r.notification.flags & mustHaveFlags) != mustHaveFlags) {
1293                     return;
1294                 }
1295                 if ((r.notification.flags & mustNotHaveFlags) != 0) {
1296                     return;
1297                 }
1298 
1299                 mNotificationList.remove(index);
1300 
1301                 cancelNotificationLocked(r, sendDelete);
1302                 updateLightsLocked();
1303             }
1304         }
1305     }
1306 
1307     /**
1308      * Determine whether the userId applies to the notification in question, either because
1309      * they match exactly, or one of them is USER_ALL (which is treated as a wildcard).
1310      */
notificationMatchesUserId(NotificationRecord r, int userId)1311     private boolean notificationMatchesUserId(NotificationRecord r, int userId) {
1312         return
1313                 // looking for USER_ALL notifications? match everything
1314                    userId == UserHandle.USER_ALL
1315                 // a notification sent to USER_ALL matches any query
1316                 || r.userId == UserHandle.USER_ALL
1317                 // an exact user match
1318                 || r.userId == userId;
1319     }
1320 
1321     /**
1322      * Cancels all notifications from a given package that have all of the
1323      * {@code mustHaveFlags}.
1324      */
cancelAllNotificationsInt(String pkg, int mustHaveFlags, int mustNotHaveFlags, boolean doit, int userId)1325     boolean cancelAllNotificationsInt(String pkg, int mustHaveFlags,
1326             int mustNotHaveFlags, boolean doit, int userId) {
1327         EventLog.writeEvent(EventLogTags.NOTIFICATION_CANCEL_ALL, pkg, userId,
1328                 mustHaveFlags, mustNotHaveFlags);
1329 
1330         synchronized (mNotificationList) {
1331             final int N = mNotificationList.size();
1332             boolean canceledSomething = false;
1333             for (int i = N-1; i >= 0; --i) {
1334                 NotificationRecord r = mNotificationList.get(i);
1335                 if (!notificationMatchesUserId(r, userId)) {
1336                     continue;
1337                 }
1338                 // Don't remove notifications to all, if there's no package name specified
1339                 if (r.userId == UserHandle.USER_ALL && pkg == null) {
1340                     continue;
1341                 }
1342                 if ((r.notification.flags & mustHaveFlags) != mustHaveFlags) {
1343                     continue;
1344                 }
1345                 if ((r.notification.flags & mustNotHaveFlags) != 0) {
1346                     continue;
1347                 }
1348                 if (pkg != null && !r.pkg.equals(pkg)) {
1349                     continue;
1350                 }
1351                 canceledSomething = true;
1352                 if (!doit) {
1353                     return true;
1354                 }
1355                 mNotificationList.remove(i);
1356                 cancelNotificationLocked(r, false);
1357             }
1358             if (canceledSomething) {
1359                 updateLightsLocked();
1360             }
1361             return canceledSomething;
1362         }
1363     }
1364 
cancelNotificationWithTag(String pkg, String tag, int id, int userId)1365     public void cancelNotificationWithTag(String pkg, String tag, int id, int userId) {
1366         checkCallerIsSystemOrSameApp(pkg);
1367         userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1368                 Binder.getCallingUid(), userId, true, false, "cancelNotificationWithTag", pkg);
1369         // Don't allow client applications to cancel foreground service notis.
1370         cancelNotification(pkg, tag, id, 0,
1371                 Binder.getCallingUid() == Process.SYSTEM_UID
1372                 ? 0 : Notification.FLAG_FOREGROUND_SERVICE, false, userId);
1373     }
1374 
cancelAllNotifications(String pkg, int userId)1375     public void cancelAllNotifications(String pkg, int userId) {
1376         checkCallerIsSystemOrSameApp(pkg);
1377 
1378         userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1379                 Binder.getCallingUid(), userId, true, false, "cancelAllNotifications", pkg);
1380 
1381         // Calling from user space, don't allow the canceling of actively
1382         // running foreground services.
1383         cancelAllNotificationsInt(pkg, 0, Notification.FLAG_FOREGROUND_SERVICE, true, userId);
1384     }
1385 
checkCallerIsSystem()1386     void checkCallerIsSystem() {
1387         int uid = Binder.getCallingUid();
1388         if (UserHandle.getAppId(uid) == Process.SYSTEM_UID || uid == 0) {
1389             return;
1390         }
1391         throw new SecurityException("Disallowed call for uid " + uid);
1392     }
1393 
checkCallerIsSystemOrSameApp(String pkg)1394     void checkCallerIsSystemOrSameApp(String pkg) {
1395         int uid = Binder.getCallingUid();
1396         if (UserHandle.getAppId(uid) == Process.SYSTEM_UID || uid == 0) {
1397             return;
1398         }
1399         try {
1400             ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo(
1401                     pkg, 0, UserHandle.getCallingUserId());
1402             if (!UserHandle.isSameApp(ai.uid, uid)) {
1403                 throw new SecurityException("Calling uid " + uid + " gave package"
1404                         + pkg + " which is owned by uid " + ai.uid);
1405             }
1406         } catch (RemoteException re) {
1407             throw new SecurityException("Unknown package " + pkg + "\n" + re);
1408         }
1409     }
1410 
cancelAll(int userId)1411     void cancelAll(int userId) {
1412         synchronized (mNotificationList) {
1413             final int N = mNotificationList.size();
1414             for (int i=N-1; i>=0; i--) {
1415                 NotificationRecord r = mNotificationList.get(i);
1416 
1417                 if (!notificationMatchesUserId(r, userId)) {
1418                     continue;
1419                 }
1420 
1421                 if ((r.notification.flags & (Notification.FLAG_ONGOING_EVENT
1422                                 | Notification.FLAG_NO_CLEAR)) == 0) {
1423                     mNotificationList.remove(i);
1424                     cancelNotificationLocked(r, true);
1425                 }
1426             }
1427 
1428             updateLightsLocked();
1429         }
1430     }
1431 
1432     // lock on mNotificationList
updateLightsLocked()1433     private void updateLightsLocked()
1434     {
1435         // handle notification lights
1436         if (mLedNotification == null) {
1437             // get next notification, if any
1438             int n = mLights.size();
1439             if (n > 0) {
1440                 mLedNotification = mLights.get(n-1);
1441             }
1442         }
1443 
1444         // Don't flash while we are in a call or screen is on
1445         if (mLedNotification == null || mInCall || mScreenOn) {
1446             mNotificationLight.turnOff();
1447         } else {
1448             int ledARGB = mLedNotification.notification.ledARGB;
1449             int ledOnMS = mLedNotification.notification.ledOnMS;
1450             int ledOffMS = mLedNotification.notification.ledOffMS;
1451             if ((mLedNotification.notification.defaults & Notification.DEFAULT_LIGHTS) != 0) {
1452                 ledARGB = mDefaultNotificationColor;
1453                 ledOnMS = mDefaultNotificationLedOn;
1454                 ledOffMS = mDefaultNotificationLedOff;
1455             }
1456             if (mNotificationPulseEnabled) {
1457                 // pulse repeatedly
1458                 mNotificationLight.setFlashing(ledARGB, LightsService.LIGHT_FLASH_TIMED,
1459                         ledOnMS, ledOffMS);
1460             }
1461         }
1462     }
1463 
1464     // lock on mNotificationList
indexOfNotificationLocked(String pkg, String tag, int id, int userId)1465     private int indexOfNotificationLocked(String pkg, String tag, int id, int userId)
1466     {
1467         ArrayList<NotificationRecord> list = mNotificationList;
1468         final int len = list.size();
1469         for (int i=0; i<len; i++) {
1470             NotificationRecord r = list.get(i);
1471             if (!notificationMatchesUserId(r, userId) || r.id != id) {
1472                 continue;
1473             }
1474             if (tag == null) {
1475                 if (r.tag != null) {
1476                     continue;
1477                 }
1478             } else {
1479                 if (!tag.equals(r.tag)) {
1480                     continue;
1481                 }
1482             }
1483             if (r.pkg.equals(pkg)) {
1484                 return i;
1485             }
1486         }
1487         return -1;
1488     }
1489 
updateNotificationPulse()1490     private void updateNotificationPulse() {
1491         synchronized (mNotificationList) {
1492             updateLightsLocked();
1493         }
1494     }
1495 
1496     // ======================================================================
1497     @Override
dump(FileDescriptor fd, PrintWriter pw, String[] args)1498     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1499         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
1500                 != PackageManager.PERMISSION_GRANTED) {
1501             pw.println("Permission Denial: can't dump NotificationManager from from pid="
1502                     + Binder.getCallingPid()
1503                     + ", uid=" + Binder.getCallingUid());
1504             return;
1505         }
1506 
1507         pw.println("Current Notification Manager state:");
1508 
1509         int N;
1510 
1511         synchronized (mToastQueue) {
1512             N = mToastQueue.size();
1513             if (N > 0) {
1514                 pw.println("  Toast Queue:");
1515                 for (int i=0; i<N; i++) {
1516                     mToastQueue.get(i).dump(pw, "    ");
1517                 }
1518                 pw.println("  ");
1519             }
1520 
1521         }
1522 
1523         synchronized (mNotificationList) {
1524             N = mNotificationList.size();
1525             if (N > 0) {
1526                 pw.println("  Notification List:");
1527                 for (int i=0; i<N; i++) {
1528                     mNotificationList.get(i).dump(pw, "    ", mContext);
1529                 }
1530                 pw.println("  ");
1531             }
1532 
1533             N = mLights.size();
1534             if (N > 0) {
1535                 pw.println("  Lights List:");
1536                 for (int i=0; i<N; i++) {
1537                     mLights.get(i).dump(pw, "    ", mContext);
1538                 }
1539                 pw.println("  ");
1540             }
1541 
1542             pw.println("  mSoundNotification=" + mSoundNotification);
1543             pw.println("  mVibrateNotification=" + mVibrateNotification);
1544             pw.println("  mDisabledNotifications=0x" + Integer.toHexString(mDisabledNotifications));
1545             pw.println("  mSystemReady=" + mSystemReady);
1546         }
1547     }
1548 }
1549