blob: cfb892f489312bcf61a15a7befbb3d28ea92a528 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server;
18
Jeff Sharkey098d5802012-04-26 17:30:34 -070019import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
20import static org.xmlpull.v1.XmlPullParser.END_TAG;
21import static org.xmlpull.v1.XmlPullParser.START_TAG;
svetoslavganov75986cf2009-05-14 22:28:01 -070022
Dianne Hackborn41203752012-08-31 14:05:51 -070023import android.app.ActivityManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080024import android.app.ActivityManagerNative;
Amith Yamasanif203aee2012-08-29 18:41:53 -070025import android.app.AppGlobals;
Daniel Sandler4a900acd2013-01-30 14:04:10 -050026import android.app.AppOpsManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080027import android.app.IActivityManager;
28import android.app.INotificationManager;
29import android.app.ITransientNotification;
30import android.app.Notification;
31import android.app.PendingIntent;
32import android.app.StatusBarManager;
33import android.content.BroadcastReceiver;
Daniel Sandler5feceeb2013-03-22 18:29:23 -070034import android.content.ComponentName;
Dianne Hackborn1dac2772009-06-26 18:16:48 -070035import android.content.ContentResolver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080036import android.content.Context;
37import android.content.Intent;
38import android.content.IntentFilter;
Daniel Sandler5feceeb2013-03-22 18:29:23 -070039import android.content.ServiceConnection;
Dianne Hackbornd8a43f62009-08-17 23:33:56 -070040import android.content.pm.ApplicationInfo;
Daniel Sandler4a900acd2013-01-30 14:04:10 -050041import android.content.pm.PackageInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080042import android.content.pm.PackageManager;
43import android.content.pm.PackageManager.NameNotFoundException;
44import android.content.res.Resources;
Dianne Hackborn1dac2772009-06-26 18:16:48 -070045import android.database.ContentObserver;
svetoslavganov75986cf2009-05-14 22:28:01 -070046import android.media.AudioManager;
Jeff Sharkey098d5802012-04-26 17:30:34 -070047import android.media.IAudioService;
48import android.media.IRingtonePlayer;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080049import android.net.Uri;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080050import android.os.Binder;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080051import android.os.Handler;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080052import android.os.IBinder;
53import android.os.Message;
Dianne Hackbornd8a43f62009-08-17 23:33:56 -070054import android.os.Process;
svetoslavganov75986cf2009-05-14 22:28:01 -070055import android.os.RemoteException;
Jeff Sharkey098d5802012-04-26 17:30:34 -070056import android.os.ServiceManager;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -070057import android.os.UserHandle;
Daniel Sandler4b749ef2013-03-18 21:53:04 -040058import android.os.UserManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080059import android.os.Vibrator;
60import android.provider.Settings;
Daniel Sandler5feceeb2013-03-22 18:29:23 -070061import android.service.notification.INotificationListener;
62import android.service.notification.NotificationListenerService;
63import android.service.notification.StatusBarNotification;
Daniel Sandlere96ffb12010-03-11 13:38:06 -050064import android.telephony.TelephonyManager;
svetoslavganov75986cf2009-05-14 22:28:01 -070065import android.text.TextUtils;
Dianne Hackborn39606a02012-07-31 17:54:35 -070066import android.util.AtomicFile;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080067import android.util.EventLog;
68import android.util.Log;
Andy Stadler110988c2010-12-03 14:29:16 -080069import android.util.Slog;
Daniel Sandler0da673f2012-04-11 12:33:16 -040070import android.util.Xml;
svetoslavganov75986cf2009-05-14 22:28:01 -070071import android.view.accessibility.AccessibilityEvent;
72import android.view.accessibility.AccessibilityManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080073import android.widget.Toast;
74
Jeff Sharkey098d5802012-04-26 17:30:34 -070075import org.xmlpull.v1.XmlPullParser;
76import org.xmlpull.v1.XmlPullParserException;
Jeff Sharkey098d5802012-04-26 17:30:34 -070077
Daniel Sandler0da673f2012-04-11 12:33:16 -040078import java.io.File;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080079import java.io.FileDescriptor;
Daniel Sandler0da673f2012-04-11 12:33:16 -040080import java.io.FileInputStream;
81import java.io.FileNotFoundException;
Daniel Sandler0da673f2012-04-11 12:33:16 -040082import java.io.IOException;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080083import java.io.PrintWriter;
Daniel Sandlerfde19b12013-01-17 00:21:05 -050084import java.util.ArrayDeque;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080085import java.util.ArrayList;
86import java.util.Arrays;
Daniel Sandler0da673f2012-04-11 12:33:16 -040087import java.util.HashSet;
Daniel Sandlerfde19b12013-01-17 00:21:05 -050088import java.util.Iterator;
89import java.util.NoSuchElementException;
Daniel Sandler0da673f2012-04-11 12:33:16 -040090
91import libcore.io.IoUtils;
92
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080093
Daniel Sandlerd0a2f862010-08-03 15:29:31 -040094/** {@hide} */
95public class NotificationManagerService extends INotificationManager.Stub
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080096{
97 private static final String TAG = "NotificationService";
98 private static final boolean DBG = false;
99
Joe Onoratobd73d012010-06-04 11:44:54 -0700100 private static final int MAX_PACKAGE_NOTIFICATIONS = 50;
101
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800102 // message codes
103 private static final int MESSAGE_TIMEOUT = 2;
104
105 private static final int LONG_DELAY = 3500; // 3.5 seconds
106 private static final int SHORT_DELAY = 2000; // 2 seconds
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800107
108 private static final long[] DEFAULT_VIBRATE_PATTERN = {0, 250, 250, 250};
Daniel Sandleredbb3802012-11-13 20:49:47 -0800109 private static final int VIBRATE_PATTERN_MAXLEN = 8 * 2 + 1; // up to eight bumps
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800110
111 private static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_NOTIFICATION;
Daniel Sandler49a2ad12012-03-28 15:46:39 -0400112 private static final boolean SCORE_ONGOING_HIGHER = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800113
Daniel Sandler0da673f2012-04-11 12:33:16 -0400114 private static final int JUNK_SCORE = -1000;
115 private static final int NOTIFICATION_PRIORITY_MULTIPLIER = 10;
116 private static final int SCORE_DISPLAY_THRESHOLD = Notification.PRIORITY_MIN * NOTIFICATION_PRIORITY_MULTIPLIER;
117
Daniel Sandler526fa0e2012-12-04 14:51:50 -0500118 // Notifications with scores below this will not interrupt the user, either via LED or
119 // sound or vibration
120 private static final int SCORE_INTERRUPTION_THRESHOLD =
121 Notification.PRIORITY_LOW * NOTIFICATION_PRIORITY_MULTIPLIER;
122
Daniel Sandler0da673f2012-04-11 12:33:16 -0400123 private static final boolean ENABLE_BLOCKED_NOTIFICATIONS = true;
124 private static final boolean ENABLE_BLOCKED_TOASTS = true;
125
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700126 private static final String ENABLED_NOTIFICATION_LISTENERS_SEPARATOR = ":";
127
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800128 final Context mContext;
129 final IActivityManager mAm;
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400130 final UserManager mUserManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800131 final IBinder mForegroundToken = new Binder();
132
133 private WorkerHandler mHandler;
Joe Onorato089de882010-04-12 08:18:45 -0700134 private StatusBarManagerService mStatusBar;
Mike Lockwood3cb67a32009-11-27 14:25:58 -0500135 private LightsService.Light mNotificationLight;
136 private LightsService.Light mAttentionLight;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800137
Mike Lockwood670f9322010-01-20 12:13:36 -0500138 private int mDefaultNotificationColor;
139 private int mDefaultNotificationLedOn;
140 private int mDefaultNotificationLedOff;
141
Daniel Sandleredbb3802012-11-13 20:49:47 -0800142 private long[] mDefaultVibrationPattern;
143 private long[] mFallbackVibrationPattern;
144
Joe Onorato30275482009-07-08 17:09:14 -0700145 private boolean mSystemReady;
Joe Onorato39f5b6a2009-07-23 12:29:19 -0400146 private int mDisabledNotifications;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800147
Jeff Sharkey098d5802012-04-26 17:30:34 -0700148 private NotificationRecord mSoundNotification;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800149 private NotificationRecord mVibrateNotification;
Jeff Sharkey098d5802012-04-26 17:30:34 -0700150
151 private IAudioService mAudioService;
Jeff Brownc2346132012-04-13 01:55:38 -0700152 private Vibrator mVibrator;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800153
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500154 // for enabling and disabling notification pulse behavior
Mike Lockwood63b5ad92011-08-30 09:55:30 -0400155 private boolean mScreenOn = true;
Daniel Sandlere96ffb12010-03-11 13:38:06 -0500156 private boolean mInCall = false;
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500157 private boolean mNotificationPulseEnabled;
158
Daniel Sandler09a247e2013-02-14 10:24:17 -0500159 // used as a mutex for access to all active notifications & listeners
Fred Quintana6ecaff12009-09-25 14:23:13 -0700160 private final ArrayList<NotificationRecord> mNotificationList =
161 new ArrayList<NotificationRecord>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800162
163 private ArrayList<ToastRecord> mToastQueue;
164
165 private ArrayList<NotificationRecord> mLights = new ArrayList<NotificationRecord>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800166 private NotificationRecord mLedNotification;
svetoslavganov75986cf2009-05-14 22:28:01 -0700167
Daniel Sandler4a900acd2013-01-30 14:04:10 -0500168 private final AppOpsManager mAppOps;
169
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700170 // contains connections to all connected listeners, including app services
171 // and system listeners
172 private ArrayList<NotificationListenerInfo> mListeners
173 = new ArrayList<NotificationListenerInfo>();
174 // things that will be put into mListeners as soon as they're ready
175 private ArrayList<String> mServicesBinding = new ArrayList<String>();
176 // lists the component names of all enabled (and therefore connected) listener
177 // app services for the current user only
178 private HashSet<ComponentName> mEnabledListenersForCurrentUser
179 = new HashSet<ComponentName>();
180 // Just the packages from mEnabledListenersForCurrentUser
181 private HashSet<String> mEnabledListenerPackageNames = new HashSet<String>();
Daniel Sandler09a247e2013-02-14 10:24:17 -0500182
Daniel Sandler0da673f2012-04-11 12:33:16 -0400183 // Notification control database. For now just contains disabled packages.
184 private AtomicFile mPolicyFile;
185 private HashSet<String> mBlockedPackages = new HashSet<String>();
186
187 private static final int DB_VERSION = 1;
188
189 private static final String TAG_BODY = "notification-policy";
190 private static final String ATTR_VERSION = "version";
191
192 private static final String TAG_BLOCKED_PKGS = "blocked-packages";
193 private static final String TAG_PACKAGE = "package";
194 private static final String ATTR_NAME = "name";
195
Daniel Sandler09a247e2013-02-14 10:24:17 -0500196 private class NotificationListenerInfo implements DeathRecipient {
197 INotificationListener listener;
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700198 ComponentName component;
Daniel Sandler09a247e2013-02-14 10:24:17 -0500199 int userid;
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400200 boolean isSystem;
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700201 ServiceConnection connection;
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400202
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700203 public NotificationListenerInfo(INotificationListener listener, ComponentName component,
204 int userid, boolean isSystem) {
Daniel Sandler09a247e2013-02-14 10:24:17 -0500205 this.listener = listener;
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700206 this.component = component;
Daniel Sandler09a247e2013-02-14 10:24:17 -0500207 this.userid = userid;
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400208 this.isSystem = isSystem;
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700209 this.connection = null;
210 }
211
212 public NotificationListenerInfo(INotificationListener listener, ComponentName component,
213 int userid, ServiceConnection connection) {
214 this.listener = listener;
215 this.component = component;
216 this.userid = userid;
217 this.isSystem = false;
218 this.connection = connection;
Daniel Sandler09a247e2013-02-14 10:24:17 -0500219 }
220
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400221 boolean enabledAndUserMatches(StatusBarNotification sbn) {
222 final int nid = sbn.getUserId();
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700223 if (!isEnabledForCurrentUser()) {
224 return false;
225 }
Daniel Sandler21ca44d2013-03-07 13:58:00 -0500226 if (this.userid == UserHandle.USER_ALL) return true;
Daniel Sandler21ca44d2013-03-07 13:58:00 -0500227 return (nid == UserHandle.USER_ALL || nid == this.userid);
228 }
229
Daniel Sandler09a247e2013-02-14 10:24:17 -0500230 public void notifyPostedIfUserMatch(StatusBarNotification sbn) {
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700231 if (!enabledAndUserMatches(sbn)) {
232 return;
233 }
Daniel Sandler09a247e2013-02-14 10:24:17 -0500234 try {
235 listener.onNotificationPosted(sbn);
236 } catch (RemoteException ex) {
237 // not there?
238 }
239 }
240
241 public void notifyRemovedIfUserMatch(StatusBarNotification sbn) {
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400242 if (!enabledAndUserMatches(sbn)) return;
Daniel Sandler09a247e2013-02-14 10:24:17 -0500243 try {
244 listener.onNotificationRemoved(sbn);
245 } catch (RemoteException ex) {
246 // not there?
247 }
248 }
249
250 @Override
251 public void binderDied() {
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700252 if (connection == null) {
253 // This is not a service; it won't be recreated. We can give up this connection.
254 unregisterListener(this.listener, this.userid);
255 }
Daniel Sandler09a247e2013-02-14 10:24:17 -0500256 }
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400257
258 /** convenience method for looking in mEnabledListenersForCurrentUser */
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700259 public boolean isEnabledForCurrentUser() {
260 if (this.isSystem) return true;
261 if (this.connection == null) return false;
262 return mEnabledListenersForCurrentUser.contains(this.component);
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400263 }
Daniel Sandler09a247e2013-02-14 10:24:17 -0500264 }
265
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500266 private static class Archive {
267 static final int BUFFER_SIZE = 1000;
268 ArrayDeque<StatusBarNotification> mBuffer = new ArrayDeque<StatusBarNotification>(BUFFER_SIZE);
269
270 public Archive() {
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500271 }
Jeff Sharkey0c1baf92013-04-03 13:08:52 -0700272
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500273 public void record(StatusBarNotification nr) {
Jeff Sharkey0c1baf92013-04-03 13:08:52 -0700274 // Nuke heavy parts of notification before storing in archive
275 nr.notification.tickerView = null;
276 nr.notification.contentView = null;
277 nr.notification.bigContentView = null;
278 nr.notification.largeIcon = null;
279
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500280 if (mBuffer.size() == BUFFER_SIZE) {
281 mBuffer.removeFirst();
282 }
283 mBuffer.addLast(nr);
284 }
285
286 public void clear() {
287 mBuffer.clear();
288 }
289
290 public Iterator<StatusBarNotification> descendingIterator() {
291 return mBuffer.descendingIterator();
292 }
293 public Iterator<StatusBarNotification> ascendingIterator() {
294 return mBuffer.iterator();
295 }
296 public Iterator<StatusBarNotification> filter(
297 final Iterator<StatusBarNotification> iter, final String pkg, final int userId) {
298 return new Iterator<StatusBarNotification>() {
299 StatusBarNotification mNext = findNext();
300
301 private StatusBarNotification findNext() {
302 while (iter.hasNext()) {
303 StatusBarNotification nr = iter.next();
304 if ((pkg == null || nr.pkg == pkg)
305 && (userId == UserHandle.USER_ALL || nr.getUserId() == userId)) {
306 return nr;
307 }
308 }
309 return null;
310 }
311
312 @Override
313 public boolean hasNext() {
314 return mNext == null;
315 }
316
317 @Override
318 public StatusBarNotification next() {
319 StatusBarNotification next = mNext;
320 if (next == null) {
321 throw new NoSuchElementException();
322 }
323 mNext = findNext();
324 return next;
325 }
326
327 @Override
328 public void remove() {
329 iter.remove();
330 }
331 };
332 }
Daniel Sandler78d0d252013-02-12 08:14:52 -0500333
334 public StatusBarNotification[] getArray(int count) {
335 if (count == 0) count = Archive.BUFFER_SIZE;
336 final StatusBarNotification[] a
337 = new StatusBarNotification[Math.min(count, mBuffer.size())];
338 Iterator<StatusBarNotification> iter = descendingIterator();
339 int i=0;
340 while (iter.hasNext() && i < count) {
341 a[i++] = iter.next();
342 }
343 return a;
344 }
345
346 public StatusBarNotification[] getArray(int count, String pkg, int userId) {
347 if (count == 0) count = Archive.BUFFER_SIZE;
348 final StatusBarNotification[] a
349 = new StatusBarNotification[Math.min(count, mBuffer.size())];
350 Iterator<StatusBarNotification> iter = filter(descendingIterator(), pkg, userId);
351 int i=0;
352 while (iter.hasNext() && i < count) {
353 a[i++] = iter.next();
354 }
355 return a;
356 }
357
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500358 }
359
360 Archive mArchive = new Archive();
361
Daniel Sandler0da673f2012-04-11 12:33:16 -0400362 private void loadBlockDb() {
363 synchronized(mBlockedPackages) {
364 if (mPolicyFile == null) {
365 File dir = new File("/data/system");
366 mPolicyFile = new AtomicFile(new File(dir, "notification_policy.xml"));
367
368 mBlockedPackages.clear();
369
370 FileInputStream infile = null;
371 try {
372 infile = mPolicyFile.openRead();
373 final XmlPullParser parser = Xml.newPullParser();
374 parser.setInput(infile, null);
375
376 int type;
377 String tag;
378 int version = DB_VERSION;
379 while ((type = parser.next()) != END_DOCUMENT) {
380 tag = parser.getName();
381 if (type == START_TAG) {
382 if (TAG_BODY.equals(tag)) {
383 version = Integer.parseInt(parser.getAttributeValue(null, ATTR_VERSION));
384 } else if (TAG_BLOCKED_PKGS.equals(tag)) {
385 while ((type = parser.next()) != END_DOCUMENT) {
386 tag = parser.getName();
387 if (TAG_PACKAGE.equals(tag)) {
388 mBlockedPackages.add(parser.getAttributeValue(null, ATTR_NAME));
389 } else if (TAG_BLOCKED_PKGS.equals(tag) && type == END_TAG) {
390 break;
391 }
392 }
393 }
394 }
395 }
396 } catch (FileNotFoundException e) {
397 // No data yet
398 } catch (IOException e) {
399 Log.wtf(TAG, "Unable to read blocked notifications database", e);
400 } catch (NumberFormatException e) {
401 Log.wtf(TAG, "Unable to parse blocked notifications database", e);
402 } catch (XmlPullParserException e) {
403 Log.wtf(TAG, "Unable to parse blocked notifications database", e);
404 } finally {
405 IoUtils.closeQuietly(infile);
406 }
407 }
408 }
409 }
410
Daniel Sandler4a900acd2013-01-30 14:04:10 -0500411 /**
412 * Use this when you just want to know if notifications are OK for this package.
413 */
414 public boolean areNotificationsEnabledForPackage(String pkg, int uid) {
Daniel Sandler0da673f2012-04-11 12:33:16 -0400415 checkCallerIsSystem();
Daniel Sandler4a900acd2013-01-30 14:04:10 -0500416 return (mAppOps.checkOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
417 == AppOpsManager.MODE_ALLOWED);
Daniel Sandler0da673f2012-04-11 12:33:16 -0400418 }
419
Daniel Sandler4a900acd2013-01-30 14:04:10 -0500420 /** Use this when you actually want to post a notification or toast.
421 *
422 * Unchecked. Not exposed via Binder, but can be called in the course of enqueue*().
423 */
424 private boolean noteNotificationOp(String pkg, int uid) {
425 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
426 != AppOpsManager.MODE_ALLOWED) {
427 Slog.v(TAG, "notifications are disabled by AppOps for " + pkg);
428 return false;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400429 }
Daniel Sandler4a900acd2013-01-30 14:04:10 -0500430 return true;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400431 }
432
Daniel Sandler4a900acd2013-01-30 14:04:10 -0500433 public void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) {
Daniel Sandler0da673f2012-04-11 12:33:16 -0400434 checkCallerIsSystem();
Daniel Sandler4a900acd2013-01-30 14:04:10 -0500435 if (true||DBG) {
Daniel Sandler0da673f2012-04-11 12:33:16 -0400436 Slog.v(TAG, (enabled?"en":"dis") + "abling notifications for " + pkg);
437 }
Daniel Sandler4a900acd2013-01-30 14:04:10 -0500438 mAppOps.setMode(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg,
439 enabled ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED);
Daniel Sandler0da673f2012-04-11 12:33:16 -0400440 }
441
442
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800443 private static String idDebugString(Context baseContext, String packageName, int id) {
444 Context c = null;
445
446 if (packageName != null) {
447 try {
448 c = baseContext.createPackageContext(packageName, 0);
449 } catch (NameNotFoundException e) {
450 c = baseContext;
451 }
452 } else {
453 c = baseContext;
454 }
455
456 String pkg;
457 String type;
458 String name;
459
460 Resources r = c.getResources();
461 try {
462 return r.getResourceName(id);
463 } catch (Resources.NotFoundException e) {
464 return "<name unknown>";
465 }
466 }
467
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700468 /**
469 * System-only API for getting a list of current (i.e. not cleared) notifications.
470 *
471 * Requires ACCESS_NOTIFICATIONS which is signature|system.
472 */
473 @Override
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500474 public StatusBarNotification[] getActiveNotifications(String callingPkg) {
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400475 // enforce() will ensure the calling uid has the correct permission
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500476 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_NOTIFICATIONS,
Daniel Sandler78d0d252013-02-12 08:14:52 -0500477 "NotificationManagerService.getActiveNotifications");
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500478
479 StatusBarNotification[] tmp = null;
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500480 int uid = Binder.getCallingUid();
481
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400482 // noteOp will check to make sure the callingPkg matches the uid
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500483 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
484 == AppOpsManager.MODE_ALLOWED) {
485 synchronized (mNotificationList) {
486 tmp = new StatusBarNotification[mNotificationList.size()];
487 final int N = mNotificationList.size();
488 for (int i=0; i<N; i++) {
489 tmp[i] = mNotificationList.get(i).sbn;
490 }
491 }
492 }
493 return tmp;
494 }
495
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700496 /**
497 * System-only API for getting a list of recent (cleared, no longer shown) notifications.
498 *
499 * Requires ACCESS_NOTIFICATIONS which is signature|system.
500 */
501 @Override
Daniel Sandler78d0d252013-02-12 08:14:52 -0500502 public StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count) {
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400503 // enforce() will ensure the calling uid has the correct permission
Daniel Sandler78d0d252013-02-12 08:14:52 -0500504 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_NOTIFICATIONS,
505 "NotificationManagerService.getHistoricalNotifications");
506
507 StatusBarNotification[] tmp = null;
508 int uid = Binder.getCallingUid();
509
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400510 // noteOp will check to make sure the callingPkg matches the uid
Daniel Sandler78d0d252013-02-12 08:14:52 -0500511 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
512 == AppOpsManager.MODE_ALLOWED) {
513 synchronized (mArchive) {
514 tmp = mArchive.getArray(count);
515 }
516 }
517 return tmp;
518 }
519
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700520 /**
521 * Called whenever packages change, the user switches, or ENABLED_NOTIFICATION_LISTENERS
522 * is altered. (For example in response to USER_SWITCHED in our broadcast receiver)
523 */
524 void rebindListenerServices() {
525 String flat = Settings.Secure.getString(
526 mContext.getContentResolver(),
527 Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
528
529 NotificationListenerInfo[] toRemove = new NotificationListenerInfo[mListeners.size()];
530 final ArrayList<ComponentName> toAdd;
531 final int currentUser = ActivityManager.getCurrentUser();
532
533 synchronized (mNotificationList) {
534 // unbind and remove all existing listeners
535 toRemove = mListeners.toArray(toRemove);
536
537 toAdd = new ArrayList<ComponentName>();
538 final HashSet<ComponentName> newEnabled = new HashSet<ComponentName>();
539 final HashSet<String> newPackages = new HashSet<String>();
540
541 // decode the list of components
542 if (flat != null) {
543 String[] components = flat.split(ENABLED_NOTIFICATION_LISTENERS_SEPARATOR);
544 for (int i=0; i<components.length; i++) {
545 final ComponentName component
546 = ComponentName.unflattenFromString(components[i]);
547 if (component != null) {
548 newEnabled.add(component);
549 toAdd.add(component);
550 newPackages.add(component.getPackageName());
551 }
552 }
553
554 mEnabledListenersForCurrentUser = newEnabled;
555 mEnabledListenerPackageNames = newPackages;
556 }
557 }
558
559 for (NotificationListenerInfo info : toRemove) {
560 final ComponentName component = info.component;
561 final int oldUser = info.userid;
562 Slog.v(TAG, "disabling notification listener for user " + oldUser + ": " + component);
563 unregisterListenerService(component, info.userid);
564 }
565
566 final int N = toAdd.size();
567 for (int i=0; i<N; i++) {
568 final ComponentName component = toAdd.get(i);
569 Slog.v(TAG, "enabling notification listener for user " + currentUser + ": "
570 + component);
571 registerListenerService(component, currentUser);
572 }
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400573 }
574
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700575 /**
576 * Register a listener binder directly with the notification manager.
577 *
578 * Only works with system callers. Apps should extend
579 * {@link android.service.notification.NotificationListenerService}.
580 */
Daniel Sandler09a247e2013-02-14 10:24:17 -0500581 @Override
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400582 public void registerListener(final INotificationListener listener,
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700583 final ComponentName component, final int userid) {
584 checkCallerIsSystem();
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400585
Daniel Sandler09a247e2013-02-14 10:24:17 -0500586 synchronized (mNotificationList) {
587 try {
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400588 NotificationListenerInfo info
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700589 = new NotificationListenerInfo(listener, component, userid, true);
Daniel Sandler09a247e2013-02-14 10:24:17 -0500590 listener.asBinder().linkToDeath(info, 0);
591 mListeners.add(info);
592 } catch (RemoteException e) {
593 // already dead
594 }
595 }
596 }
597
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700598 /**
599 * Version of registerListener that takes the name of a
600 * {@link android.service.notification.NotificationListenerService} to bind to.
601 *
602 * This is the mechanism by which third parties may subscribe to notifications.
603 */
604 private void registerListenerService(final ComponentName name, final int userid) {
605 checkCallerIsSystem();
606
607 if (DBG) Slog.v(TAG, "registerListenerService: " + name + " u=" + userid);
608
609 synchronized (mNotificationList) {
610 final String servicesBindingTag = name.toString() + "/" + userid;
611 if (mServicesBinding.contains(servicesBindingTag)) {
612 // stop registering this thing already! we're working on it
613 return;
614 }
615 mServicesBinding.add(servicesBindingTag);
616
617 final int N = mListeners.size();
618 for (int i=N-1; i>=0; i--) {
619 final NotificationListenerInfo info = mListeners.get(i);
620 if (name.equals(info.component)
621 && info.userid == userid) {
622 // cut old connections
623 if (DBG) Slog.v(TAG, " disconnecting old listener: " + info.listener);
624 mListeners.remove(i);
625 if (info.connection != null) {
626 mContext.unbindService(info.connection);
627 }
628 }
629 }
630
631 Intent intent = new Intent(NotificationListenerService.SERVICE_INTERFACE);
632 intent.setComponent(name);
633
634 intent.putExtra(Intent.EXTRA_CLIENT_LABEL,
635 com.android.internal.R.string.notification_listener_binding_label);
636 intent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity(
637 mContext, 0, new Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS), 0));
638
639 try {
640 if (DBG) Slog.v(TAG, "binding: " + intent);
641 if (!mContext.bindServiceAsUser(intent,
642 new ServiceConnection() {
643 INotificationListener mListener;
644 @Override
645 public void onServiceConnected(ComponentName name, IBinder service) {
646 synchronized (mNotificationList) {
647 mServicesBinding.remove(servicesBindingTag);
648 try {
649 mListener = INotificationListener.Stub.asInterface(service);
650 NotificationListenerInfo info = new NotificationListenerInfo(
651 mListener, name, userid, this);
652 service.linkToDeath(info, 0);
653 mListeners.add(info);
654 } catch (RemoteException e) {
655 // already dead
656 }
657 }
658 }
659
660 @Override
661 public void onServiceDisconnected(ComponentName name) {
662 Slog.v(TAG, "notification listener connection lost: " + name);
663 }
664 },
665 Context.BIND_AUTO_CREATE,
666 new UserHandle(userid)))
667 {
668 mServicesBinding.remove(servicesBindingTag);
669 Slog.w(TAG, "Unable to bind listener service: " + intent);
670 return;
671 }
672 } catch (SecurityException ex) {
673 Slog.e(TAG, "Unable to bind listener service: " + intent, ex);
674 return;
675 }
676 }
677 }
678
679 /**
680 * Remove a listener binder directly
681 */
Daniel Sandler09a247e2013-02-14 10:24:17 -0500682 @Override
683 public void unregisterListener(INotificationListener listener, int userid) {
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400684 // no need to check permissions; if your listener binder is in the list,
685 // that's proof that you had permission to add it in the first place
686
Daniel Sandler09a247e2013-02-14 10:24:17 -0500687 synchronized (mNotificationList) {
688 final int N = mListeners.size();
689 for (int i=N-1; i>=0; i--) {
690 final NotificationListenerInfo info = mListeners.get(i);
691 if (info.listener == listener && info.userid == userid) {
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700692 mListeners.remove(i);
693 if (info.connection != null) {
694 mContext.unbindService(info.connection);
695 }
Daniel Sandler09a247e2013-02-14 10:24:17 -0500696 }
697 }
698 }
699 }
700
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700701 /**
702 * Remove a listener service for the given user by ComponentName
703 */
704 private void unregisterListenerService(ComponentName name, int userid) {
705 checkCallerIsSystem();
706
707 synchronized (mNotificationList) {
708 final int N = mListeners.size();
709 for (int i=N-1; i>=0; i--) {
710 final NotificationListenerInfo info = mListeners.get(i);
711 if (name.equals(info.component)
712 && info.userid == userid) {
713 mListeners.remove(i);
714 if (info.connection != null) {
715 mContext.unbindService(info.connection);
716 }
717 }
718 }
719 }
720 }
721
722 /**
723 * asynchronously notify all listeners about a new notification
724 */
Daniel Sandler09a247e2013-02-14 10:24:17 -0500725 private void notifyPostedLocked(NotificationRecord n) {
726 final StatusBarNotification sbn = n.sbn;
727 for (final NotificationListenerInfo info : mListeners) {
728 mHandler.post(new Runnable() {
729 @Override
730 public void run() {
731 info.notifyPostedIfUserMatch(sbn);
732 }});
733 }
734 }
735
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700736 /**
737 * asynchronously notify all listeners about a removed notification
738 */
Daniel Sandler09a247e2013-02-14 10:24:17 -0500739 private void notifyRemovedLocked(NotificationRecord n) {
740 final StatusBarNotification sbn = n.sbn;
741 for (final NotificationListenerInfo info : mListeners) {
742 mHandler.post(new Runnable() {
743 @Override
744 public void run() {
745 info.notifyRemovedIfUserMatch(sbn);
746 }});
747 }
748 }
749
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700750 // -- APIs to support listeners clicking/clearing notifications --
751
752 private NotificationListenerInfo checkListenerToken(INotificationListener listener) {
753 final IBinder token = listener.asBinder();
754 final int N = mListeners.size();
755 for (int i=0; i<N; i++) {
756 final NotificationListenerInfo info = mListeners.get(i);
757 if (info.listener.asBinder() == token) return info;
758 }
759 throw new SecurityException("Disallowed call from unknown listener: " + listener);
760 }
761
762 /**
763 * Allow an INotificationListener to simulate a "clear all" operation.
764 *
765 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onClearAllNotifications}
766 *
767 * @param token The binder for the listener, to check that the caller is allowed
768 */
769 public void clearAllNotificationsFromListener(INotificationListener token) {
770 NotificationListenerInfo info = checkListenerToken(token);
771 long identity = Binder.clearCallingIdentity();
772 try {
773 cancelAll(info.userid);
774 } finally {
775 Binder.restoreCallingIdentity(identity);
776 }
777 }
778
779 /**
780 * Allow an INotificationListener to simulate clearing (dismissing) a single notification.
781 *
782 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onNotificationClear}
783 *
784 * @param token The binder for the listener, to check that the caller is allowed
785 */
786 public void clearNotificationFromListener(INotificationListener token, String pkg, String tag, int id) {
787 NotificationListenerInfo info = checkListenerToken(token);
788 long identity = Binder.clearCallingIdentity();
789 try {
790 cancelNotification(pkg, tag, id, 0,
791 Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
792 true,
793 info.userid);
794 } finally {
795 Binder.restoreCallingIdentity(identity);
796 }
797 }
798
799 // -- end of listener APIs --
800
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500801 public static final class NotificationRecord
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800802 {
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500803 final StatusBarNotification sbn;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800804 IBinder statusBarKey;
805
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500806 NotificationRecord(StatusBarNotification sbn)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800807 {
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500808 this.sbn = sbn;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800809 }
Fred Quintana6ecaff12009-09-25 14:23:13 -0700810
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500811 public Notification getNotification() { return sbn.notification; }
812 public int getFlags() { return sbn.notification.flags; }
813 public int getUserId() { return sbn.getUserId(); }
814
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800815 void dump(PrintWriter pw, String prefix, Context baseContext) {
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500816 final Notification notification = sbn.notification;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800817 pw.println(prefix + this);
818 pw.println(prefix + " icon=0x" + Integer.toHexString(notification.icon)
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500819 + " / " + idDebugString(baseContext, this.sbn.pkg, notification.icon));
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500820 pw.println(prefix + " pri=" + notification.priority);
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500821 pw.println(prefix + " score=" + this.sbn.score);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800822 pw.println(prefix + " contentIntent=" + notification.contentIntent);
823 pw.println(prefix + " deleteIntent=" + notification.deleteIntent);
824 pw.println(prefix + " tickerText=" + notification.tickerText);
825 pw.println(prefix + " contentView=" + notification.contentView);
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500826 pw.println(prefix + " uid=" + this.sbn.uid + " userId=" + this.sbn.getUserId());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800827 pw.println(prefix + " defaults=0x" + Integer.toHexString(notification.defaults));
828 pw.println(prefix + " flags=0x" + Integer.toHexString(notification.flags));
829 pw.println(prefix + " sound=" + notification.sound);
830 pw.println(prefix + " vibrate=" + Arrays.toString(notification.vibrate));
831 pw.println(prefix + " ledARGB=0x" + Integer.toHexString(notification.ledARGB)
832 + " ledOnMS=" + notification.ledOnMS
833 + " ledOffMS=" + notification.ledOffMS);
834 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800835
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800836 @Override
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500837 public final String toString() {
838 return String.format(
839 "NotificationRecord(0x%08x: pkg=%s user=%s id=%d tag=%s score=%d: %s)",
840 System.identityHashCode(this),
841 this.sbn.pkg, this.sbn.user, this.sbn.id, this.sbn.tag,
842 this.sbn.score, this.sbn.notification);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800843 }
844 }
845
846 private static final class ToastRecord
847 {
848 final int pid;
849 final String pkg;
850 final ITransientNotification callback;
851 int duration;
852
853 ToastRecord(int pid, String pkg, ITransientNotification callback, int duration)
854 {
855 this.pid = pid;
856 this.pkg = pkg;
857 this.callback = callback;
858 this.duration = duration;
859 }
860
861 void update(int duration) {
862 this.duration = duration;
863 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800864
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800865 void dump(PrintWriter pw, String prefix) {
866 pw.println(prefix + this);
867 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800868
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800869 @Override
870 public final String toString()
871 {
872 return "ToastRecord{"
873 + Integer.toHexString(System.identityHashCode(this))
874 + " pkg=" + pkg
875 + " callback=" + callback
876 + " duration=" + duration;
877 }
878 }
879
Joe Onorato089de882010-04-12 08:18:45 -0700880 private StatusBarManagerService.NotificationCallbacks mNotificationCallbacks
881 = new StatusBarManagerService.NotificationCallbacks() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800882
883 public void onSetDisabled(int status) {
884 synchronized (mNotificationList) {
885 mDisabledNotifications = status;
886 if ((mDisabledNotifications & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) {
887 // cancel whatever's going on
888 long identity = Binder.clearCallingIdentity();
889 try {
Jeff Sharkey098d5802012-04-26 17:30:34 -0700890 final IRingtonePlayer player = mAudioService.getRingtonePlayer();
891 if (player != null) {
892 player.stopAsync();
893 }
894 } catch (RemoteException e) {
895 } finally {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800896 Binder.restoreCallingIdentity(identity);
897 }
898
899 identity = Binder.clearCallingIdentity();
900 try {
901 mVibrator.cancel();
Jeff Sharkey098d5802012-04-26 17:30:34 -0700902 } finally {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800903 Binder.restoreCallingIdentity(identity);
904 }
905 }
906 }
907 }
908
909 public void onClearAll() {
Dianne Hackborn41203752012-08-31 14:05:51 -0700910 // XXX to be totally correct, the caller should tell us which user
911 // this is for.
912 cancelAll(ActivityManager.getCurrentUser());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800913 }
914
Fred Quintana6ecaff12009-09-25 14:23:13 -0700915 public void onNotificationClick(String pkg, String tag, int id) {
Dianne Hackborn41203752012-08-31 14:05:51 -0700916 // XXX to be totally correct, the caller should tell us which user
917 // this is for.
Fred Quintana6ecaff12009-09-25 14:23:13 -0700918 cancelNotification(pkg, tag, id, Notification.FLAG_AUTO_CANCEL,
Dianne Hackborn41203752012-08-31 14:05:51 -0700919 Notification.FLAG_FOREGROUND_SERVICE, false,
920 ActivityManager.getCurrentUser());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800921 }
922
Daniel Sandler0f0b11c2010-08-04 15:54:58 -0400923 public void onNotificationClear(String pkg, String tag, int id) {
Dianne Hackborn41203752012-08-31 14:05:51 -0700924 // XXX to be totally correct, the caller should tell us which user
925 // this is for.
Joe Onorato46439ce2010-11-19 13:56:21 -0800926 cancelNotification(pkg, tag, id, 0,
927 Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
Dianne Hackborn41203752012-08-31 14:05:51 -0700928 true, ActivityManager.getCurrentUser());
Daniel Sandler0f0b11c2010-08-04 15:54:58 -0400929 }
930
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800931 public void onPanelRevealed() {
932 synchronized (mNotificationList) {
933 // sound
934 mSoundNotification = null;
Jeff Sharkey098d5802012-04-26 17:30:34 -0700935
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800936 long identity = Binder.clearCallingIdentity();
937 try {
Jeff Sharkey098d5802012-04-26 17:30:34 -0700938 final IRingtonePlayer player = mAudioService.getRingtonePlayer();
939 if (player != null) {
940 player.stopAsync();
941 }
942 } catch (RemoteException e) {
943 } finally {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800944 Binder.restoreCallingIdentity(identity);
945 }
946
947 // vibrate
948 mVibrateNotification = null;
949 identity = Binder.clearCallingIdentity();
950 try {
951 mVibrator.cancel();
Jeff Sharkey098d5802012-04-26 17:30:34 -0700952 } finally {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800953 Binder.restoreCallingIdentity(identity);
954 }
955
956 // light
957 mLights.clear();
958 mLedNotification = null;
959 updateLightsLocked();
960 }
961 }
Joe Onorato005847b2010-06-04 16:08:02 -0400962
Dianne Hackborn9d39d0c2010-06-24 15:57:42 -0700963 public void onNotificationError(String pkg, String tag, int id,
964 int uid, int initialPid, String message) {
Daniel Sandlerd0a2f862010-08-03 15:29:31 -0400965 Slog.d(TAG, "onNotification error pkg=" + pkg + " tag=" + tag + " id=" + id
966 + "; will crashApplication(uid=" + uid + ", pid=" + initialPid + ")");
Dianne Hackborn41203752012-08-31 14:05:51 -0700967 // XXX to be totally correct, the caller should tell us which user
968 // this is for.
969 cancelNotification(pkg, tag, id, 0, 0, false, UserHandle.getUserId(uid));
Dianne Hackborn9d39d0c2010-06-24 15:57:42 -0700970 long ident = Binder.clearCallingIdentity();
971 try {
972 ActivityManagerNative.getDefault().crashApplication(uid, initialPid, pkg,
973 "Bad notification posted from package " + pkg
974 + ": " + message);
975 } catch (RemoteException e) {
976 }
977 Binder.restoreCallingIdentity(ident);
Joe Onorato005847b2010-06-04 16:08:02 -0400978 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800979 };
980
981 private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
982 @Override
983 public void onReceive(Context context, Intent intent) {
984 String action = intent.getAction();
985
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800986 boolean queryRestart = false;
Daniel Sandler26ece572012-06-01 15:38:46 -0400987 boolean packageChanged = false;
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800988
Mike Lockwood541c9942011-06-12 19:35:45 -0400989 if (action.equals(Intent.ACTION_PACKAGE_REMOVED)
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800990 || action.equals(Intent.ACTION_PACKAGE_RESTARTED)
Daniel Sandler26ece572012-06-01 15:38:46 -0400991 || (packageChanged=action.equals(Intent.ACTION_PACKAGE_CHANGED))
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800992 || (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART))
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800993 || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800994 String pkgList[] = null;
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800995 if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800996 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800997 } else if (queryRestart) {
998 pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800999 } else {
1000 Uri uri = intent.getData();
1001 if (uri == null) {
1002 return;
1003 }
1004 String pkgName = uri.getSchemeSpecificPart();
1005 if (pkgName == null) {
1006 return;
1007 }
Daniel Sandler26ece572012-06-01 15:38:46 -04001008 if (packageChanged) {
1009 // We cancel notifications for packages which have just been disabled
1010 final int enabled = mContext.getPackageManager()
1011 .getApplicationEnabledSetting(pkgName);
1012 if (enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED
1013 || enabled == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
1014 return;
1015 }
1016 }
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001017 pkgList = new String[]{pkgName};
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001018 }
Daniel Sandler5feceeb2013-03-22 18:29:23 -07001019
1020 boolean anyListenersInvolved = false;
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001021 if (pkgList != null && (pkgList.length > 0)) {
1022 for (String pkgName : pkgList) {
Dianne Hackborn41203752012-08-31 14:05:51 -07001023 cancelAllNotificationsInt(pkgName, 0, 0, !queryRestart,
1024 UserHandle.USER_ALL);
Daniel Sandler5feceeb2013-03-22 18:29:23 -07001025 if (mEnabledListenerPackageNames.contains(pkgName)) {
1026 anyListenersInvolved = true;
1027 }
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001028 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001029 }
Daniel Sandler5feceeb2013-03-22 18:29:23 -07001030
1031 if (anyListenersInvolved) {
1032 // make sure we're still bound to any of our
1033 // listeners who may have just upgraded
1034 rebindListenerServices();
1035 }
Mike Lockwood63b5ad92011-08-30 09:55:30 -04001036 } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
1037 // Keep track of screen on/off state, but do not turn off the notification light
1038 // until user passes through the lock screen or views the notification.
1039 mScreenOn = true;
1040 } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
1041 mScreenOn = false;
Daniel Sandlere96ffb12010-03-11 13:38:06 -05001042 } else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) {
Mike Lockwood63b5ad92011-08-30 09:55:30 -04001043 mInCall = (intent.getStringExtra(TelephonyManager.EXTRA_STATE).equals(
1044 TelephonyManager.EXTRA_STATE_OFFHOOK));
Daniel Sandlere96ffb12010-03-11 13:38:06 -05001045 updateNotificationPulse();
Dianne Hackborn80a4af22012-08-27 19:18:31 -07001046 } else if (action.equals(Intent.ACTION_USER_STOPPED)) {
1047 int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
1048 if (userHandle >= 0) {
Dianne Hackborn41203752012-08-31 14:05:51 -07001049 cancelAllNotificationsInt(null, 0, 0, true, userHandle);
Dianne Hackborn80a4af22012-08-27 19:18:31 -07001050 }
Mike Lockwood63b5ad92011-08-30 09:55:30 -04001051 } else if (action.equals(Intent.ACTION_USER_PRESENT)) {
1052 // turn off LED when user passes through lock screen
1053 mNotificationLight.turnOff();
Daniel Sandler4b749ef2013-03-18 21:53:04 -04001054 } else if (action.equals(Intent.ACTION_USER_SWITCHED)) {
1055 // reload per-user settings
1056 mSettingsObserver.update(null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001057 }
1058 }
1059 };
1060
Dianne Hackborn1dac2772009-06-26 18:16:48 -07001061 class SettingsObserver extends ContentObserver {
Daniel Sandler4b749ef2013-03-18 21:53:04 -04001062 private final Uri NOTIFICATION_LIGHT_PULSE_URI
1063 = Settings.System.getUriFor(Settings.System.NOTIFICATION_LIGHT_PULSE);
1064
1065 private final Uri ENABLED_NOTIFICATION_LISTENERS_URI
Daniel Sandler5feceeb2013-03-22 18:29:23 -07001066 = Settings.Secure.getUriFor(Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
Daniel Sandler4b749ef2013-03-18 21:53:04 -04001067
Dianne Hackborn1dac2772009-06-26 18:16:48 -07001068 SettingsObserver(Handler handler) {
1069 super(handler);
1070 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001071
Dianne Hackborn1dac2772009-06-26 18:16:48 -07001072 void observe() {
1073 ContentResolver resolver = mContext.getContentResolver();
Daniel Sandler4b749ef2013-03-18 21:53:04 -04001074 resolver.registerContentObserver(NOTIFICATION_LIGHT_PULSE_URI,
Daniel Sandler5feceeb2013-03-22 18:29:23 -07001075 false, this, UserHandle.USER_ALL);
Daniel Sandler4b749ef2013-03-18 21:53:04 -04001076 resolver.registerContentObserver(ENABLED_NOTIFICATION_LISTENERS_URI,
Daniel Sandler5feceeb2013-03-22 18:29:23 -07001077 false, this, UserHandle.USER_ALL);
Daniel Sandler4b749ef2013-03-18 21:53:04 -04001078 update(null);
Dianne Hackborn1dac2772009-06-26 18:16:48 -07001079 }
1080
Daniel Sandler4b749ef2013-03-18 21:53:04 -04001081 @Override public void onChange(boolean selfChange, Uri uri) {
1082 update(uri);
Dianne Hackborn1dac2772009-06-26 18:16:48 -07001083 }
1084
Daniel Sandler4b749ef2013-03-18 21:53:04 -04001085 public void update(Uri uri) {
Dianne Hackborn1dac2772009-06-26 18:16:48 -07001086 ContentResolver resolver = mContext.getContentResolver();
Daniel Sandler4b749ef2013-03-18 21:53:04 -04001087 if (uri == null || NOTIFICATION_LIGHT_PULSE_URI.equals(uri)) {
1088 boolean pulseEnabled = Settings.System.getInt(resolver,
1089 Settings.System.NOTIFICATION_LIGHT_PULSE, 0) != 0;
1090 if (mNotificationPulseEnabled != pulseEnabled) {
1091 mNotificationPulseEnabled = pulseEnabled;
1092 updateNotificationPulse();
1093 }
1094 }
1095 if (uri == null || ENABLED_NOTIFICATION_LISTENERS_URI.equals(uri)) {
Daniel Sandler5feceeb2013-03-22 18:29:23 -07001096 rebindListenerServices();
Mike Lockwoodc22404a2009-12-02 11:15:02 -05001097 }
Dianne Hackborn1dac2772009-06-26 18:16:48 -07001098 }
1099 }
Mike Lockwoodc22404a2009-12-02 11:15:02 -05001100
Daniel Sandler4b749ef2013-03-18 21:53:04 -04001101 private SettingsObserver mSettingsObserver;
1102
Daniel Sandleredbb3802012-11-13 20:49:47 -08001103 static long[] getLongArray(Resources r, int resid, int maxlen, long[] def) {
1104 int[] ar = r.getIntArray(resid);
1105 if (ar == null) {
1106 return def;
1107 }
1108 final int len = ar.length > maxlen ? maxlen : ar.length;
1109 long[] out = new long[len];
1110 for (int i=0; i<len; i++) {
1111 out[i] = ar[i];
1112 }
1113 return out;
1114 }
1115
Joe Onorato089de882010-04-12 08:18:45 -07001116 NotificationManagerService(Context context, StatusBarManagerService statusBar,
Mike Lockwood3a322132009-11-24 00:30:52 -05001117 LightsService lights)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001118 {
1119 super();
1120 mContext = context;
Jeff Brownc2346132012-04-13 01:55:38 -07001121 mVibrator = (Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001122 mAm = ActivityManagerNative.getDefault();
Daniel Sandler4b749ef2013-03-18 21:53:04 -04001123 mUserManager = (UserManager)context.getSystemService(Context.USER_SERVICE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001124 mToastQueue = new ArrayList<ToastRecord>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001125 mHandler = new WorkerHandler();
San Mehat3ee13172010-02-04 20:54:43 -08001126
Daniel Sandler4a900acd2013-01-30 14:04:10 -05001127 mAppOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE);
1128
1129 importOldBlockDb();
Daniel Sandler0da673f2012-04-11 12:33:16 -04001130
Joe Onorato089de882010-04-12 08:18:45 -07001131 mStatusBar = statusBar;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001132 statusBar.setNotificationCallbacks(mNotificationCallbacks);
1133
Mike Lockwood3cb67a32009-11-27 14:25:58 -05001134 mNotificationLight = lights.getLight(LightsService.LIGHT_ID_NOTIFICATIONS);
1135 mAttentionLight = lights.getLight(LightsService.LIGHT_ID_ATTENTION);
1136
Mike Lockwood670f9322010-01-20 12:13:36 -05001137 Resources resources = mContext.getResources();
1138 mDefaultNotificationColor = resources.getColor(
1139 com.android.internal.R.color.config_defaultNotificationColor);
1140 mDefaultNotificationLedOn = resources.getInteger(
1141 com.android.internal.R.integer.config_defaultNotificationLedOn);
1142 mDefaultNotificationLedOff = resources.getInteger(
1143 com.android.internal.R.integer.config_defaultNotificationLedOff);
1144
Daniel Sandleredbb3802012-11-13 20:49:47 -08001145 mDefaultVibrationPattern = getLongArray(resources,
1146 com.android.internal.R.array.config_defaultNotificationVibePattern,
1147 VIBRATE_PATTERN_MAXLEN,
1148 DEFAULT_VIBRATE_PATTERN);
1149
1150 mFallbackVibrationPattern = getLongArray(resources,
1151 com.android.internal.R.array.config_notificationFallbackVibePattern,
1152 VIBRATE_PATTERN_MAXLEN,
1153 DEFAULT_VIBRATE_PATTERN);
1154
Joe Onorato39f5b6a2009-07-23 12:29:19 -04001155 // Don't start allowing notifications until the setup wizard has run once.
1156 // After that, including subsequent boots, init with notifications turned on.
1157 // This works on the first boot because the setup wizard will toggle this
1158 // flag at least once and we'll go back to 0 after that.
Jeff Brownbf6f6f92012-09-25 15:03:20 -07001159 if (0 == Settings.Global.getInt(mContext.getContentResolver(),
1160 Settings.Global.DEVICE_PROVISIONED, 0)) {
Joe Onorato39f5b6a2009-07-23 12:29:19 -04001161 mDisabledNotifications = StatusBarManager.DISABLE_NOTIFICATION_ALERTS;
1162 }
1163
Mike Lockwood35e16bf2010-11-30 19:53:36 -05001164 // register for various Intents
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001165 IntentFilter filter = new IntentFilter();
Mike Lockwoodc22404a2009-12-02 11:15:02 -05001166 filter.addAction(Intent.ACTION_SCREEN_ON);
1167 filter.addAction(Intent.ACTION_SCREEN_OFF);
Daniel Sandlere96ffb12010-03-11 13:38:06 -05001168 filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
Mike Lockwood63b5ad92011-08-30 09:55:30 -04001169 filter.addAction(Intent.ACTION_USER_PRESENT);
Dianne Hackborn80a4af22012-08-27 19:18:31 -07001170 filter.addAction(Intent.ACTION_USER_STOPPED);
Daniel Sandler4b749ef2013-03-18 21:53:04 -04001171 filter.addAction(Intent.ACTION_USER_SWITCHED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001172 mContext.registerReceiver(mIntentReceiver, filter);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08001173 IntentFilter pkgFilter = new IntentFilter();
1174 pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
Daniel Sandleraac0eb02011-08-06 22:51:56 -04001175 pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08001176 pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
1177 pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
1178 pkgFilter.addDataScheme("package");
1179 mContext.registerReceiver(mIntentReceiver, pkgFilter);
Suchi Amalapurapub56ae202010-02-04 22:51:07 -08001180 IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001181 mContext.registerReceiver(mIntentReceiver, sdFilter);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001182
Daniel Sandler4b749ef2013-03-18 21:53:04 -04001183 mSettingsObserver = new SettingsObserver(mHandler);
1184 mSettingsObserver.observe();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001185 }
1186
Daniel Sandler4a900acd2013-01-30 14:04:10 -05001187 /**
1188 * Read the old XML-based app block database and import those blockages into the AppOps system.
1189 */
1190 private void importOldBlockDb() {
1191 loadBlockDb();
1192
1193 PackageManager pm = mContext.getPackageManager();
1194 for (String pkg : mBlockedPackages) {
1195 PackageInfo info = null;
1196 try {
1197 info = pm.getPackageInfo(pkg, 0);
1198 setNotificationsEnabledForPackage(pkg, info.applicationInfo.uid, false);
1199 } catch (NameNotFoundException e) {
1200 // forget you
1201 }
1202 }
1203 mBlockedPackages.clear();
1204 if (mPolicyFile != null) {
1205 mPolicyFile.delete();
1206 }
1207 }
1208
Joe Onorato30275482009-07-08 17:09:14 -07001209 void systemReady() {
Jeff Sharkey098d5802012-04-26 17:30:34 -07001210 mAudioService = IAudioService.Stub.asInterface(
1211 ServiceManager.getService(Context.AUDIO_SERVICE));
1212
Joe Onorato30275482009-07-08 17:09:14 -07001213 // no beeping until we're basically done booting
1214 mSystemReady = true;
Daniel Sandler5feceeb2013-03-22 18:29:23 -07001215
1216 // make sure our listener services are properly bound
1217 rebindListenerServices();
Joe Onorato30275482009-07-08 17:09:14 -07001218 }
1219
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001220 // Toasts
1221 // ============================================================================
1222 public void enqueueToast(String pkg, ITransientNotification callback, int duration)
1223 {
Daniel Sandlera7035902010-03-30 15:45:31 -04001224 if (DBG) Slog.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback + " duration=" + duration);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001225
1226 if (pkg == null || callback == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001227 Slog.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001228 return ;
1229 }
1230
Daniel Sandler0da673f2012-04-11 12:33:16 -04001231 final boolean isSystemToast = ("android".equals(pkg));
1232
Daniel Sandler4a900acd2013-01-30 14:04:10 -05001233 if (ENABLE_BLOCKED_TOASTS && !noteNotificationOp(pkg, Binder.getCallingUid())) {
1234 if (!isSystemToast) {
1235 Slog.e(TAG, "Suppressing toast from package " + pkg + " by user request.");
1236 return;
1237 }
Daniel Sandler0da673f2012-04-11 12:33:16 -04001238 }
1239
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001240 synchronized (mToastQueue) {
1241 int callingPid = Binder.getCallingPid();
1242 long callingId = Binder.clearCallingIdentity();
1243 try {
1244 ToastRecord record;
1245 int index = indexOfToastLocked(pkg, callback);
1246 // If it's already in the queue, we update it in place, we don't
1247 // move it to the end of the queue.
1248 if (index >= 0) {
1249 record = mToastQueue.get(index);
1250 record.update(duration);
1251 } else {
Vairavan Srinivasanf9eb06c2011-01-21 18:08:36 -08001252 // Limit the number of toasts that any given package except the android
1253 // package can enqueue. Prevents DOS attacks and deals with leaks.
Daniel Sandler0da673f2012-04-11 12:33:16 -04001254 if (!isSystemToast) {
Vairavan Srinivasanf9eb06c2011-01-21 18:08:36 -08001255 int count = 0;
1256 final int N = mToastQueue.size();
1257 for (int i=0; i<N; i++) {
1258 final ToastRecord r = mToastQueue.get(i);
1259 if (r.pkg.equals(pkg)) {
1260 count++;
1261 if (count >= MAX_PACKAGE_NOTIFICATIONS) {
1262 Slog.e(TAG, "Package has already posted " + count
1263 + " toasts. Not showing more. Package=" + pkg);
1264 return;
1265 }
1266 }
1267 }
1268 }
1269
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001270 record = new ToastRecord(callingPid, pkg, callback, duration);
1271 mToastQueue.add(record);
1272 index = mToastQueue.size() - 1;
1273 keepProcessAliveLocked(callingPid);
1274 }
1275 // If it's at index 0, it's the current toast. It doesn't matter if it's
1276 // new or just been updated. Call back and tell it to show itself.
1277 // If the callback fails, this will remove it from the list, so don't
1278 // assume that it's valid after this.
1279 if (index == 0) {
1280 showNextToastLocked();
1281 }
1282 } finally {
1283 Binder.restoreCallingIdentity(callingId);
1284 }
1285 }
1286 }
1287
1288 public void cancelToast(String pkg, ITransientNotification callback) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001289 Slog.i(TAG, "cancelToast pkg=" + pkg + " callback=" + callback);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001290
1291 if (pkg == null || callback == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001292 Slog.e(TAG, "Not cancelling notification. pkg=" + pkg + " callback=" + callback);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001293 return ;
1294 }
1295
1296 synchronized (mToastQueue) {
1297 long callingId = Binder.clearCallingIdentity();
1298 try {
1299 int index = indexOfToastLocked(pkg, callback);
1300 if (index >= 0) {
1301 cancelToastLocked(index);
1302 } else {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001303 Slog.w(TAG, "Toast already cancelled. pkg=" + pkg + " callback=" + callback);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001304 }
1305 } finally {
1306 Binder.restoreCallingIdentity(callingId);
1307 }
1308 }
1309 }
1310
1311 private void showNextToastLocked() {
1312 ToastRecord record = mToastQueue.get(0);
1313 while (record != null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001314 if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001315 try {
1316 record.callback.show();
1317 scheduleTimeoutLocked(record, false);
1318 return;
1319 } catch (RemoteException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001320 Slog.w(TAG, "Object died trying to show notification " + record.callback
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001321 + " in package " + record.pkg);
1322 // remove it from the list and let the process die
1323 int index = mToastQueue.indexOf(record);
1324 if (index >= 0) {
1325 mToastQueue.remove(index);
1326 }
1327 keepProcessAliveLocked(record.pid);
1328 if (mToastQueue.size() > 0) {
1329 record = mToastQueue.get(0);
1330 } else {
1331 record = null;
1332 }
1333 }
1334 }
1335 }
1336
1337 private void cancelToastLocked(int index) {
1338 ToastRecord record = mToastQueue.get(index);
1339 try {
1340 record.callback.hide();
1341 } catch (RemoteException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001342 Slog.w(TAG, "Object died trying to hide notification " + record.callback
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001343 + " in package " + record.pkg);
1344 // don't worry about this, we're about to remove it from
1345 // the list anyway
1346 }
1347 mToastQueue.remove(index);
1348 keepProcessAliveLocked(record.pid);
1349 if (mToastQueue.size() > 0) {
1350 // Show the next one. If the callback fails, this will remove
1351 // it from the list, so don't assume that the list hasn't changed
1352 // after this point.
1353 showNextToastLocked();
1354 }
1355 }
1356
1357 private void scheduleTimeoutLocked(ToastRecord r, boolean immediate)
1358 {
1359 Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r);
1360 long delay = immediate ? 0 : (r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY);
1361 mHandler.removeCallbacksAndMessages(r);
1362 mHandler.sendMessageDelayed(m, delay);
1363 }
1364
1365 private void handleTimeout(ToastRecord record)
1366 {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001367 if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001368 synchronized (mToastQueue) {
1369 int index = indexOfToastLocked(record.pkg, record.callback);
1370 if (index >= 0) {
1371 cancelToastLocked(index);
1372 }
1373 }
1374 }
1375
1376 // lock on mToastQueue
1377 private int indexOfToastLocked(String pkg, ITransientNotification callback)
1378 {
1379 IBinder cbak = callback.asBinder();
1380 ArrayList<ToastRecord> list = mToastQueue;
1381 int len = list.size();
1382 for (int i=0; i<len; i++) {
1383 ToastRecord r = list.get(i);
1384 if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) {
1385 return i;
1386 }
1387 }
1388 return -1;
1389 }
1390
1391 // lock on mToastQueue
1392 private void keepProcessAliveLocked(int pid)
1393 {
1394 int toastCount = 0; // toasts from this pid
1395 ArrayList<ToastRecord> list = mToastQueue;
1396 int N = list.size();
1397 for (int i=0; i<N; i++) {
1398 ToastRecord r = list.get(i);
1399 if (r.pid == pid) {
1400 toastCount++;
1401 }
1402 }
1403 try {
1404 mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0);
1405 } catch (RemoteException e) {
1406 // Shouldn't happen.
1407 }
1408 }
1409
1410 private final class WorkerHandler extends Handler
1411 {
1412 @Override
1413 public void handleMessage(Message msg)
1414 {
1415 switch (msg.what)
1416 {
1417 case MESSAGE_TIMEOUT:
1418 handleTimeout((ToastRecord)msg.obj);
1419 break;
1420 }
1421 }
1422 }
1423
1424
1425 // Notifications
1426 // ============================================================================
Dianne Hackbornf265ea92013-01-31 15:00:51 -08001427 public void enqueueNotificationWithTag(String pkg, String basePkg, String tag, int id,
1428 Notification notification, int[] idOut, int userId)
Fred Quintana6ecaff12009-09-25 14:23:13 -07001429 {
Dianne Hackbornf265ea92013-01-31 15:00:51 -08001430 enqueueNotificationInternal(pkg, basePkg, Binder.getCallingUid(), Binder.getCallingPid(),
Dianne Hackborn41203752012-08-31 14:05:51 -07001431 tag, id, notification, idOut, userId);
Daniel Sandlerd0a2f862010-08-03 15:29:31 -04001432 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001433
1434 private final static int clamp(int x, int low, int high) {
1435 return (x < low) ? low : ((x > high) ? high : x);
Daniel Sandlere40451a2011-02-03 14:51:35 -05001436 }
1437
Daniel Sandlerd0a2f862010-08-03 15:29:31 -04001438 // Not exposed via Binder; for system use only (otherwise malicious apps could spoof the
1439 // uid/pid of another application)
Dianne Hackbornf265ea92013-01-31 15:00:51 -08001440 public void enqueueNotificationInternal(String pkg, String basePkg, int callingUid,
1441 int callingPid, String tag, int id, Notification notification, int[] idOut, int userId)
Daniel Sandlerd0a2f862010-08-03 15:29:31 -04001442 {
Daniel Sandler0da673f2012-04-11 12:33:16 -04001443 if (DBG) {
1444 Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id + " notification=" + notification);
1445 }
1446 checkCallerIsSystemOrSameApp(pkg);
1447 final boolean isSystemNotification = ("android".equals(pkg));
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001448
Dianne Hackborn41203752012-08-31 14:05:51 -07001449 userId = ActivityManager.handleIncomingUser(callingPid,
Amith Yamasani2c7ebea2012-10-30 15:28:27 -07001450 callingUid, userId, true, false, "enqueueNotification", pkg);
Jeff Sharkey65c4a2b2012-09-25 17:22:27 -07001451 final UserHandle user = new UserHandle(userId);
Dianne Hackborn41203752012-08-31 14:05:51 -07001452
Joe Onoratobd73d012010-06-04 11:44:54 -07001453 // Limit the number of notifications that any given package except the android
1454 // package can enqueue. Prevents DOS attacks and deals with leaks.
Daniel Sandler0da673f2012-04-11 12:33:16 -04001455 if (!isSystemNotification) {
Joe Onoratobd73d012010-06-04 11:44:54 -07001456 synchronized (mNotificationList) {
1457 int count = 0;
1458 final int N = mNotificationList.size();
1459 for (int i=0; i<N; i++) {
1460 final NotificationRecord r = mNotificationList.get(i);
Daniel Sandlerfde19b12013-01-17 00:21:05 -05001461 if (r.sbn.pkg.equals(pkg) && r.sbn.getUserId() == userId) {
Joe Onoratobd73d012010-06-04 11:44:54 -07001462 count++;
1463 if (count >= MAX_PACKAGE_NOTIFICATIONS) {
1464 Slog.e(TAG, "Package has already posted " + count
1465 + " notifications. Not showing more. package=" + pkg);
1466 return;
1467 }
1468 }
1469 }
1470 }
1471 }
1472
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001473 // This conditional is a dirty hack to limit the logging done on
1474 // behalf of the download manager without affecting other apps.
1475 if (!pkg.equals("com.android.providers.downloads")
1476 || Log.isLoggable("DownloadManager", Log.VERBOSE)) {
Daniel Sandler321e9c52012-10-12 10:59:26 -07001477 EventLog.writeEvent(EventLogTags.NOTIFICATION_ENQUEUE, pkg, id, tag, userId,
Daniel Sandlerb64cb882011-11-29 23:48:29 -05001478 notification.toString());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001479 }
1480
1481 if (pkg == null || notification == null) {
1482 throw new IllegalArgumentException("null not allowed: pkg=" + pkg
1483 + " id=" + id + " notification=" + notification);
1484 }
1485 if (notification.icon != 0) {
1486 if (notification.contentView == null) {
1487 throw new IllegalArgumentException("contentView required: pkg=" + pkg
1488 + " id=" + id + " notification=" + notification);
1489 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001490 }
1491
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001492 // === Scoring ===
Daniel Sandler0da673f2012-04-11 12:33:16 -04001493
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001494 // 0. Sanitize inputs
1495 notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN, Notification.PRIORITY_MAX);
1496 // Migrate notification flags to scores
1497 if (0 != (notification.flags & Notification.FLAG_HIGH_PRIORITY)) {
1498 if (notification.priority < Notification.PRIORITY_MAX) notification.priority = Notification.PRIORITY_MAX;
Daniel Sandler49a2ad12012-03-28 15:46:39 -04001499 } else if (SCORE_ONGOING_HIGHER && 0 != (notification.flags & Notification.FLAG_ONGOING_EVENT)) {
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001500 if (notification.priority < Notification.PRIORITY_HIGH) notification.priority = Notification.PRIORITY_HIGH;
1501 }
Daniel Sandler0da673f2012-04-11 12:33:16 -04001502
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001503 // 1. initial score: buckets of 10, around the app
Daniel Sandler0da673f2012-04-11 12:33:16 -04001504 int score = notification.priority * NOTIFICATION_PRIORITY_MULTIPLIER; //[-20..20]
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001505
Daniel Sandler0da673f2012-04-11 12:33:16 -04001506 // 2. Consult external heuristics (TBD)
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001507
Daniel Sandler0da673f2012-04-11 12:33:16 -04001508 // 3. Apply local rules
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001509
1510 // blocked apps
Daniel Sandler4a900acd2013-01-30 14:04:10 -05001511 if (ENABLE_BLOCKED_NOTIFICATIONS && !noteNotificationOp(pkg, callingUid)) {
1512 if (!isSystemNotification) {
1513 score = JUNK_SCORE;
1514 Slog.e(TAG, "Suppressing notification from package " + pkg + " by user request.");
1515 }
Daniel Sandler0da673f2012-04-11 12:33:16 -04001516 }
1517
1518 if (DBG) {
1519 Slog.v(TAG, "Assigned score=" + score + " to " + notification);
1520 }
1521
1522 if (score < SCORE_DISPLAY_THRESHOLD) {
1523 // Notification will be blocked because the score is too low.
1524 return;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001525 }
1526
Daniel Sandler526fa0e2012-12-04 14:51:50 -05001527 // Should this notification make noise, vibe, or use the LED?
1528 final boolean canInterrupt = (score >= SCORE_INTERRUPTION_THRESHOLD);
1529
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001530 synchronized (mNotificationList) {
Daniel Sandlerfde19b12013-01-17 00:21:05 -05001531 final StatusBarNotification n = new StatusBarNotification(
1532 pkg, id, tag, callingUid, callingPid, score, notification, user);
1533 NotificationRecord r = new NotificationRecord(n);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001534 NotificationRecord old = null;
1535
Dianne Hackborn41203752012-08-31 14:05:51 -07001536 int index = indexOfNotificationLocked(pkg, tag, id, userId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001537 if (index < 0) {
1538 mNotificationList.add(r);
1539 } else {
1540 old = mNotificationList.remove(index);
1541 mNotificationList.add(index, r);
Dianne Hackbornd8a43f62009-08-17 23:33:56 -07001542 // Make sure we don't lose the foreground service state.
1543 if (old != null) {
1544 notification.flags |=
Daniel Sandlerfde19b12013-01-17 00:21:05 -05001545 old.getNotification().flags&Notification.FLAG_FOREGROUND_SERVICE;
Dianne Hackbornd8a43f62009-08-17 23:33:56 -07001546 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001547 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001548
Dianne Hackbornd8a43f62009-08-17 23:33:56 -07001549 // Ensure if this is a foreground service that the proper additional
1550 // flags are set.
1551 if ((notification.flags&Notification.FLAG_FOREGROUND_SERVICE) != 0) {
1552 notification.flags |= Notification.FLAG_ONGOING_EVENT
1553 | Notification.FLAG_NO_CLEAR;
1554 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001555
Svetoslav Ganovc31ed392012-10-10 14:58:28 -07001556 final int currentUser;
1557 final long token = Binder.clearCallingIdentity();
1558 try {
1559 currentUser = ActivityManager.getCurrentUser();
1560 } finally {
1561 Binder.restoreCallingIdentity(token);
1562 }
1563
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001564 if (notification.icon != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001565 if (old != null && old.statusBarKey != null) {
1566 r.statusBarKey = old.statusBarKey;
1567 long identity = Binder.clearCallingIdentity();
1568 try {
Joe Onorato18e69df2010-05-17 22:26:12 -07001569 mStatusBar.updateNotification(r.statusBarKey, n);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001570 }
1571 finally {
1572 Binder.restoreCallingIdentity(identity);
1573 }
1574 } else {
1575 long identity = Binder.clearCallingIdentity();
1576 try {
Joe Onorato18e69df2010-05-17 22:26:12 -07001577 r.statusBarKey = mStatusBar.addNotification(n);
Daniel Sandler526fa0e2012-12-04 14:51:50 -05001578 if ((n.notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0
1579 && canInterrupt) {
Mike Lockwoodece18ef2012-02-13 20:42:19 -08001580 mAttentionLight.pulse();
1581 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001582 }
1583 finally {
1584 Binder.restoreCallingIdentity(identity);
1585 }
1586 }
Svetoslav Ganovc31ed392012-10-10 14:58:28 -07001587 // Send accessibility events only for the current user.
1588 if (currentUser == userId) {
1589 sendAccessibilityEvent(notification, pkg);
1590 }
Daniel Sandlerfde19b12013-01-17 00:21:05 -05001591
Daniel Sandler09a247e2013-02-14 10:24:17 -05001592 notifyPostedLocked(r);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001593 } else {
Daniel Sandlere40451a2011-02-03 14:51:35 -05001594 Slog.e(TAG, "Ignoring notification with icon==0: " + notification);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001595 if (old != null && old.statusBarKey != null) {
1596 long identity = Binder.clearCallingIdentity();
1597 try {
Joe Onorato0cbda992010-05-02 16:28:15 -07001598 mStatusBar.removeNotification(old.statusBarKey);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001599 }
1600 finally {
1601 Binder.restoreCallingIdentity(identity);
1602 }
Daniel Sandler09a247e2013-02-14 10:24:17 -05001603
1604 notifyRemovedLocked(r);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001605 }
Daniel Sandlerfde19b12013-01-17 00:21:05 -05001606 return; // do not play sounds, show lights, etc. for invalid notifications
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001607 }
1608
1609 // If we're not supposed to beep, vibrate, etc. then don't.
1610 if (((mDisabledNotifications & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) == 0)
1611 && (!(old != null
Joe Onorato30275482009-07-08 17:09:14 -07001612 && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 ))
Daniel Sandlerfde19b12013-01-17 00:21:05 -05001613 && (r.getUserId() == UserHandle.USER_ALL ||
1614 (r.getUserId() == userId && r.getUserId() == currentUser))
Daniel Sandler526fa0e2012-12-04 14:51:50 -05001615 && canInterrupt
Joe Onorato30275482009-07-08 17:09:14 -07001616 && mSystemReady) {
Eric Laurent524dc042009-11-27 05:07:55 -08001617
1618 final AudioManager audioManager = (AudioManager) mContext
1619 .getSystemService(Context.AUDIO_SERVICE);
Daniel Sandlerd4d2de22012-11-14 11:25:46 -08001620
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001621 // sound
1622 final boolean useDefaultSound =
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001623 (notification.defaults & Notification.DEFAULT_SOUND) != 0;
Daniel Sandlerd4d2de22012-11-14 11:25:46 -08001624
1625 Uri soundUri = null;
1626 boolean hasValidSound = false;
1627
1628 if (useDefaultSound) {
1629 soundUri = Settings.System.DEFAULT_NOTIFICATION_URI;
1630
1631 // check to see if the default notification sound is silent
1632 ContentResolver resolver = mContext.getContentResolver();
1633 hasValidSound = Settings.System.getString(resolver,
1634 Settings.System.NOTIFICATION_SOUND) != null;
1635 } else if (notification.sound != null) {
1636 soundUri = notification.sound;
1637 hasValidSound = (soundUri != null);
1638 }
1639
1640 if (hasValidSound) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001641 boolean looping = (notification.flags & Notification.FLAG_INSISTENT) != 0;
1642 int audioStreamType;
1643 if (notification.audioStreamType >= 0) {
1644 audioStreamType = notification.audioStreamType;
1645 } else {
1646 audioStreamType = DEFAULT_STREAM_TYPE;
1647 }
1648 mSoundNotification = r;
Eric Laurent524dc042009-11-27 05:07:55 -08001649 // do not play notifications if stream volume is 0
Jean-Michel Trivid6770542012-10-10 12:03:41 -07001650 // (typically because ringer mode is silent) or if speech recognition is active.
1651 if ((audioManager.getStreamVolume(audioStreamType) != 0)
1652 && !audioManager.isSpeechRecognitionActive()) {
Jeff Sharkey098d5802012-04-26 17:30:34 -07001653 final long identity = Binder.clearCallingIdentity();
Eric Laurent524dc042009-11-27 05:07:55 -08001654 try {
Jeff Sharkey098d5802012-04-26 17:30:34 -07001655 final IRingtonePlayer player = mAudioService.getRingtonePlayer();
1656 if (player != null) {
Daniel Sandlerd4d2de22012-11-14 11:25:46 -08001657 player.playAsync(soundUri, user, looping, audioStreamType);
Jeff Sharkey098d5802012-04-26 17:30:34 -07001658 }
1659 } catch (RemoteException e) {
1660 } finally {
Eric Laurent524dc042009-11-27 05:07:55 -08001661 Binder.restoreCallingIdentity(identity);
1662 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001663 }
1664 }
1665
1666 // vibrate
Daniel Sandleredbb3802012-11-13 20:49:47 -08001667 // Does the notification want to specify its own vibration?
1668 final boolean hasCustomVibrate = notification.vibrate != null;
1669
David Agnew71789e12012-11-09 23:03:26 -05001670 // new in 4.2: if there was supposed to be a sound and we're in vibrate mode,
Daniel Sandler4a7a9b92012-11-20 12:59:41 -05001671 // and no other vibration is specified, we fall back to vibration
David Agnew71789e12012-11-09 23:03:26 -05001672 final boolean convertSoundToVibration =
Daniel Sandleredbb3802012-11-13 20:49:47 -08001673 !hasCustomVibrate
Daniel Sandlerd4d2de22012-11-14 11:25:46 -08001674 && hasValidSound
David Agnew71789e12012-11-09 23:03:26 -05001675 && (audioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE);
1676
Daniel Sandler4a7a9b92012-11-20 12:59:41 -05001677 // The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001678 final boolean useDefaultVibrate =
Daniel Sandleredbb3802012-11-13 20:49:47 -08001679 (notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
David Agnew71789e12012-11-09 23:03:26 -05001680
Daniel Sandleredbb3802012-11-13 20:49:47 -08001681 if ((useDefaultVibrate || convertSoundToVibration || hasCustomVibrate)
Eric Laurentbffc3d12012-05-07 17:43:49 -07001682 && !(audioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001683 mVibrateNotification = r;
1684
Daniel Sandleredbb3802012-11-13 20:49:47 -08001685 if (useDefaultVibrate || convertSoundToVibration) {
1686 // Escalate privileges so we can use the vibrator even if the notifying app
1687 // does not have the VIBRATE permission.
1688 long identity = Binder.clearCallingIdentity();
1689 try {
Daniel Sandlerfde19b12013-01-17 00:21:05 -05001690 mVibrator.vibrate(r.sbn.uid, r.sbn.basePkg,
Dianne Hackbornf265ea92013-01-31 15:00:51 -08001691 useDefaultVibrate ? mDefaultVibrationPattern
1692 : mFallbackVibrationPattern,
Daniel Sandleredbb3802012-11-13 20:49:47 -08001693 ((notification.flags & Notification.FLAG_INSISTENT) != 0) ? 0: -1);
1694 } finally {
1695 Binder.restoreCallingIdentity(identity);
1696 }
1697 } else if (notification.vibrate.length > 1) {
1698 // If you want your own vibration pattern, you need the VIBRATE permission
Daniel Sandlerfde19b12013-01-17 00:21:05 -05001699 mVibrator.vibrate(r.sbn.uid, r.sbn.basePkg, notification.vibrate,
Daniel Sandleredbb3802012-11-13 20:49:47 -08001700 ((notification.flags & Notification.FLAG_INSISTENT) != 0) ? 0: -1);
1701 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001702 }
1703 }
1704
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001705 // light
1706 // the most recent thing gets the light
1707 mLights.remove(old);
1708 if (mLedNotification == old) {
1709 mLedNotification = null;
1710 }
Joe Onorato8a9b2202010-02-26 18:56:32 -08001711 //Slog.i(TAG, "notification.lights="
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001712 // + ((old.notification.lights.flags & Notification.FLAG_SHOW_LIGHTS) != 0));
Daniel Sandler526fa0e2012-12-04 14:51:50 -05001713 if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0
1714 && canInterrupt) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001715 mLights.add(r);
1716 updateLightsLocked();
1717 } else {
1718 if (old != null
Daniel Sandlerfde19b12013-01-17 00:21:05 -05001719 && ((old.getFlags() & Notification.FLAG_SHOW_LIGHTS) != 0)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001720 updateLightsLocked();
1721 }
1722 }
1723 }
1724
1725 idOut[0] = id;
1726 }
1727
Joe Onorato30275482009-07-08 17:09:14 -07001728 private void sendAccessibilityEvent(Notification notification, CharSequence packageName) {
svetoslavganov75986cf2009-05-14 22:28:01 -07001729 AccessibilityManager manager = AccessibilityManager.getInstance(mContext);
1730 if (!manager.isEnabled()) {
1731 return;
1732 }
1733
1734 AccessibilityEvent event =
1735 AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
1736 event.setPackageName(packageName);
1737 event.setClassName(Notification.class.getName());
1738 event.setParcelableData(notification);
1739 CharSequence tickerText = notification.tickerText;
1740 if (!TextUtils.isEmpty(tickerText)) {
1741 event.getText().add(tickerText);
1742 }
1743
1744 manager.sendAccessibilityEvent(event);
1745 }
1746
Joe Onorato46439ce2010-11-19 13:56:21 -08001747 private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete) {
1748 // tell the app
1749 if (sendDelete) {
Daniel Sandlerfde19b12013-01-17 00:21:05 -05001750 if (r.getNotification().deleteIntent != null) {
Joe Onorato46439ce2010-11-19 13:56:21 -08001751 try {
Daniel Sandlerfde19b12013-01-17 00:21:05 -05001752 r.getNotification().deleteIntent.send();
Joe Onorato46439ce2010-11-19 13:56:21 -08001753 } catch (PendingIntent.CanceledException ex) {
1754 // do nothing - there's no relevant way to recover, and
1755 // no reason to let this propagate
Daniel Sandlerfde19b12013-01-17 00:21:05 -05001756 Slog.w(TAG, "canceled PendingIntent for " + r.sbn.pkg, ex);
Joe Onorato46439ce2010-11-19 13:56:21 -08001757 }
1758 }
1759 }
1760
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001761 // status bar
Daniel Sandlerfde19b12013-01-17 00:21:05 -05001762 if (r.getNotification().icon != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001763 long identity = Binder.clearCallingIdentity();
1764 try {
Joe Onorato0cbda992010-05-02 16:28:15 -07001765 mStatusBar.removeNotification(r.statusBarKey);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001766 }
1767 finally {
1768 Binder.restoreCallingIdentity(identity);
1769 }
1770 r.statusBarKey = null;
Daniel Sandler09a247e2013-02-14 10:24:17 -05001771 notifyRemovedLocked(r);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001772 }
1773
1774 // sound
1775 if (mSoundNotification == r) {
1776 mSoundNotification = null;
Jeff Sharkey098d5802012-04-26 17:30:34 -07001777 final long identity = Binder.clearCallingIdentity();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001778 try {
Jeff Sharkey098d5802012-04-26 17:30:34 -07001779 final IRingtonePlayer player = mAudioService.getRingtonePlayer();
1780 if (player != null) {
1781 player.stopAsync();
1782 }
1783 } catch (RemoteException e) {
1784 } finally {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001785 Binder.restoreCallingIdentity(identity);
1786 }
1787 }
1788
1789 // vibrate
1790 if (mVibrateNotification == r) {
1791 mVibrateNotification = null;
1792 long identity = Binder.clearCallingIdentity();
1793 try {
1794 mVibrator.cancel();
1795 }
1796 finally {
1797 Binder.restoreCallingIdentity(identity);
1798 }
1799 }
1800
1801 // light
1802 mLights.remove(r);
1803 if (mLedNotification == r) {
1804 mLedNotification = null;
1805 }
Daniel Sandler23d7c702013-03-07 16:32:06 -05001806
1807 // Save it for users of getHistoricalNotifications()
1808 mArchive.record(r.sbn);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001809 }
1810
1811 /**
Dianne Hackbornd8a43f62009-08-17 23:33:56 -07001812 * Cancels a notification ONLY if it has all of the {@code mustHaveFlags}
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001813 * and none of the {@code mustNotHaveFlags}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001814 */
Fred Quintana6ecaff12009-09-25 14:23:13 -07001815 private void cancelNotification(String pkg, String tag, int id, int mustHaveFlags,
Dianne Hackborn41203752012-08-31 14:05:51 -07001816 int mustNotHaveFlags, boolean sendDelete, int userId) {
Daniel Sandler321e9c52012-10-12 10:59:26 -07001817 EventLog.writeEvent(EventLogTags.NOTIFICATION_CANCEL, pkg, id, tag, userId,
Daniel Sandlerb64cb882011-11-29 23:48:29 -05001818 mustHaveFlags, mustNotHaveFlags);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001819
1820 synchronized (mNotificationList) {
Dianne Hackborn41203752012-08-31 14:05:51 -07001821 int index = indexOfNotificationLocked(pkg, tag, id, userId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001822 if (index >= 0) {
Fred Quintana6ecaff12009-09-25 14:23:13 -07001823 NotificationRecord r = mNotificationList.get(index);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001824
Daniel Sandlerfde19b12013-01-17 00:21:05 -05001825 if ((r.getNotification().flags & mustHaveFlags) != mustHaveFlags) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001826 return;
1827 }
Daniel Sandlerfde19b12013-01-17 00:21:05 -05001828 if ((r.getNotification().flags & mustNotHaveFlags) != 0) {
Dianne Hackbornd8a43f62009-08-17 23:33:56 -07001829 return;
1830 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001831
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001832 mNotificationList.remove(index);
1833
Joe Onorato46439ce2010-11-19 13:56:21 -08001834 cancelNotificationLocked(r, sendDelete);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001835 updateLightsLocked();
1836 }
1837 }
1838 }
1839
1840 /**
Daniel Sandler321e9c52012-10-12 10:59:26 -07001841 * Determine whether the userId applies to the notification in question, either because
1842 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard).
1843 */
1844 private boolean notificationMatchesUserId(NotificationRecord r, int userId) {
1845 return
1846 // looking for USER_ALL notifications? match everything
1847 userId == UserHandle.USER_ALL
1848 // a notification sent to USER_ALL matches any query
Daniel Sandlerfde19b12013-01-17 00:21:05 -05001849 || r.getUserId() == UserHandle.USER_ALL
Daniel Sandler321e9c52012-10-12 10:59:26 -07001850 // an exact user match
Daniel Sandlerfde19b12013-01-17 00:21:05 -05001851 || r.getUserId() == userId;
Daniel Sandler321e9c52012-10-12 10:59:26 -07001852 }
1853
1854 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001855 * Cancels all notifications from a given package that have all of the
1856 * {@code mustHaveFlags}.
1857 */
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08001858 boolean cancelAllNotificationsInt(String pkg, int mustHaveFlags,
Dianne Hackborn41203752012-08-31 14:05:51 -07001859 int mustNotHaveFlags, boolean doit, int userId) {
Daniel Sandler321e9c52012-10-12 10:59:26 -07001860 EventLog.writeEvent(EventLogTags.NOTIFICATION_CANCEL_ALL, pkg, userId,
1861 mustHaveFlags, mustNotHaveFlags);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001862
1863 synchronized (mNotificationList) {
1864 final int N = mNotificationList.size();
1865 boolean canceledSomething = false;
1866 for (int i = N-1; i >= 0; --i) {
1867 NotificationRecord r = mNotificationList.get(i);
Daniel Sandler321e9c52012-10-12 10:59:26 -07001868 if (!notificationMatchesUserId(r, userId)) {
Dianne Hackborn41203752012-08-31 14:05:51 -07001869 continue;
1870 }
Amith Yamasani5ec00e92012-11-07 16:58:30 -08001871 // Don't remove notifications to all, if there's no package name specified
Daniel Sandlerfde19b12013-01-17 00:21:05 -05001872 if (r.getUserId() == UserHandle.USER_ALL && pkg == null) {
Amith Yamasani5ec00e92012-11-07 16:58:30 -08001873 continue;
1874 }
Daniel Sandlerfde19b12013-01-17 00:21:05 -05001875 if ((r.getFlags() & mustHaveFlags) != mustHaveFlags) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001876 continue;
1877 }
Daniel Sandlerfde19b12013-01-17 00:21:05 -05001878 if ((r.getFlags() & mustNotHaveFlags) != 0) {
Dianne Hackbornd8a43f62009-08-17 23:33:56 -07001879 continue;
1880 }
Daniel Sandlerfde19b12013-01-17 00:21:05 -05001881 if (pkg != null && !r.sbn.pkg.equals(pkg)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001882 continue;
1883 }
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08001884 canceledSomething = true;
1885 if (!doit) {
1886 return true;
1887 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001888 mNotificationList.remove(i);
Joe Onorato46439ce2010-11-19 13:56:21 -08001889 cancelNotificationLocked(r, false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001890 }
1891 if (canceledSomething) {
1892 updateLightsLocked();
1893 }
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08001894 return canceledSomething;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001895 }
1896 }
1897
Dianne Hackborn41203752012-08-31 14:05:51 -07001898 public void cancelNotificationWithTag(String pkg, String tag, int id, int userId) {
Daniel Sandler0da673f2012-04-11 12:33:16 -04001899 checkCallerIsSystemOrSameApp(pkg);
Dianne Hackborn41203752012-08-31 14:05:51 -07001900 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
Amith Yamasani2c7ebea2012-10-30 15:28:27 -07001901 Binder.getCallingUid(), userId, true, false, "cancelNotificationWithTag", pkg);
Dianne Hackbornd8a43f62009-08-17 23:33:56 -07001902 // Don't allow client applications to cancel foreground service notis.
Fred Quintana6ecaff12009-09-25 14:23:13 -07001903 cancelNotification(pkg, tag, id, 0,
Dianne Hackbornd8a43f62009-08-17 23:33:56 -07001904 Binder.getCallingUid() == Process.SYSTEM_UID
Dianne Hackborn41203752012-08-31 14:05:51 -07001905 ? 0 : Notification.FLAG_FOREGROUND_SERVICE, false, userId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001906 }
1907
Dianne Hackborn41203752012-08-31 14:05:51 -07001908 public void cancelAllNotifications(String pkg, int userId) {
Daniel Sandler0da673f2012-04-11 12:33:16 -04001909 checkCallerIsSystemOrSameApp(pkg);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001910
Dianne Hackborn41203752012-08-31 14:05:51 -07001911 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
Amith Yamasani2c7ebea2012-10-30 15:28:27 -07001912 Binder.getCallingUid(), userId, true, false, "cancelAllNotifications", pkg);
Dianne Hackborn41203752012-08-31 14:05:51 -07001913
Dianne Hackbornd8a43f62009-08-17 23:33:56 -07001914 // Calling from user space, don't allow the canceling of actively
1915 // running foreground services.
Dianne Hackborn41203752012-08-31 14:05:51 -07001916 cancelAllNotificationsInt(pkg, 0, Notification.FLAG_FOREGROUND_SERVICE, true, userId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001917 }
1918
Daniel Sandler0da673f2012-04-11 12:33:16 -04001919 void checkCallerIsSystem() {
1920 int uid = Binder.getCallingUid();
Dianne Hackborn0c380492012-08-20 17:23:30 -07001921 if (UserHandle.getAppId(uid) == Process.SYSTEM_UID || uid == 0) {
Daniel Sandler0da673f2012-04-11 12:33:16 -04001922 return;
1923 }
1924 throw new SecurityException("Disallowed call for uid " + uid);
1925 }
1926
1927 void checkCallerIsSystemOrSameApp(String pkg) {
Dianne Hackbornd8a43f62009-08-17 23:33:56 -07001928 int uid = Binder.getCallingUid();
Dianne Hackborn0c380492012-08-20 17:23:30 -07001929 if (UserHandle.getAppId(uid) == Process.SYSTEM_UID || uid == 0) {
Dianne Hackbornd8a43f62009-08-17 23:33:56 -07001930 return;
1931 }
1932 try {
Amith Yamasanif203aee2012-08-29 18:41:53 -07001933 ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo(
1934 pkg, 0, UserHandle.getCallingUserId());
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07001935 if (!UserHandle.isSameApp(ai.uid, uid)) {
Dianne Hackbornd8a43f62009-08-17 23:33:56 -07001936 throw new SecurityException("Calling uid " + uid + " gave package"
1937 + pkg + " which is owned by uid " + ai.uid);
1938 }
Amith Yamasanif203aee2012-08-29 18:41:53 -07001939 } catch (RemoteException re) {
1940 throw new SecurityException("Unknown package " + pkg + "\n" + re);
Dianne Hackbornd8a43f62009-08-17 23:33:56 -07001941 }
1942 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001943
Dianne Hackborn41203752012-08-31 14:05:51 -07001944 void cancelAll(int userId) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001945 synchronized (mNotificationList) {
1946 final int N = mNotificationList.size();
1947 for (int i=N-1; i>=0; i--) {
1948 NotificationRecord r = mNotificationList.get(i);
1949
Daniel Sandler321e9c52012-10-12 10:59:26 -07001950 if (!notificationMatchesUserId(r, userId)) {
Dianne Hackborn41203752012-08-31 14:05:51 -07001951 continue;
1952 }
1953
Daniel Sandlerfde19b12013-01-17 00:21:05 -05001954 if ((r.getFlags() & (Notification.FLAG_ONGOING_EVENT
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001955 | Notification.FLAG_NO_CLEAR)) == 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001956 mNotificationList.remove(i);
Joe Onorato46439ce2010-11-19 13:56:21 -08001957 cancelNotificationLocked(r, true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001958 }
1959 }
1960
1961 updateLightsLocked();
1962 }
1963 }
1964
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001965 // lock on mNotificationList
1966 private void updateLightsLocked()
1967 {
The Android Open Source Project10592532009-03-18 17:39:46 -07001968 // handle notification lights
1969 if (mLedNotification == null) {
1970 // get next notification, if any
1971 int n = mLights.size();
1972 if (n > 0) {
1973 mLedNotification = mLights.get(n-1);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001974 }
1975 }
Mike Lockwoodc22404a2009-12-02 11:15:02 -05001976
Mike Lockwood63b5ad92011-08-30 09:55:30 -04001977 // Don't flash while we are in a call or screen is on
1978 if (mLedNotification == null || mInCall || mScreenOn) {
Mike Lockwood3cb67a32009-11-27 14:25:58 -05001979 mNotificationLight.turnOff();
The Android Open Source Project10592532009-03-18 17:39:46 -07001980 } else {
Daniel Sandlerfde19b12013-01-17 00:21:05 -05001981 final Notification ledno = mLedNotification.sbn.notification;
1982 int ledARGB = ledno.ledARGB;
1983 int ledOnMS = ledno.ledOnMS;
1984 int ledOffMS = ledno.ledOffMS;
1985 if ((ledno.defaults & Notification.DEFAULT_LIGHTS) != 0) {
Mike Lockwood670f9322010-01-20 12:13:36 -05001986 ledARGB = mDefaultNotificationColor;
1987 ledOnMS = mDefaultNotificationLedOn;
1988 ledOffMS = mDefaultNotificationLedOff;
1989 }
1990 if (mNotificationPulseEnabled) {
1991 // pulse repeatedly
1992 mNotificationLight.setFlashing(ledARGB, LightsService.LIGHT_FLASH_TIMED,
1993 ledOnMS, ledOffMS);
Mike Lockwood670f9322010-01-20 12:13:36 -05001994 }
The Android Open Source Project10592532009-03-18 17:39:46 -07001995 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001996 }
1997
1998 // lock on mNotificationList
Dianne Hackborn41203752012-08-31 14:05:51 -07001999 private int indexOfNotificationLocked(String pkg, String tag, int id, int userId)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002000 {
2001 ArrayList<NotificationRecord> list = mNotificationList;
2002 final int len = list.size();
2003 for (int i=0; i<len; i++) {
2004 NotificationRecord r = list.get(i);
Daniel Sandlerfde19b12013-01-17 00:21:05 -05002005 if (!notificationMatchesUserId(r, userId) || r.sbn.id != id) {
Dianne Hackborn41203752012-08-31 14:05:51 -07002006 continue;
2007 }
Fred Quintana6ecaff12009-09-25 14:23:13 -07002008 if (tag == null) {
Daniel Sandlerfde19b12013-01-17 00:21:05 -05002009 if (r.sbn.tag != null) {
Fred Quintana6ecaff12009-09-25 14:23:13 -07002010 continue;
2011 }
2012 } else {
Daniel Sandlerfde19b12013-01-17 00:21:05 -05002013 if (!tag.equals(r.sbn.tag)) {
Fred Quintana6ecaff12009-09-25 14:23:13 -07002014 continue;
2015 }
2016 }
Daniel Sandlerfde19b12013-01-17 00:21:05 -05002017 if (r.sbn.pkg.equals(pkg)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002018 return i;
2019 }
2020 }
2021 return -1;
2022 }
2023
Mike Lockwoodc22404a2009-12-02 11:15:02 -05002024 private void updateNotificationPulse() {
2025 synchronized (mNotificationList) {
2026 updateLightsLocked();
2027 }
2028 }
2029
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002030 // ======================================================================
2031 @Override
2032 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2033 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
2034 != PackageManager.PERMISSION_GRANTED) {
2035 pw.println("Permission Denial: can't dump NotificationManager from from pid="
2036 + Binder.getCallingPid()
2037 + ", uid=" + Binder.getCallingUid());
2038 return;
2039 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08002040
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002041 pw.println("Current Notification Manager state:");
2042
Daniel Sandler5feceeb2013-03-22 18:29:23 -07002043 pw.println(" Listeners (" + mEnabledListenersForCurrentUser.size()
2044 + ") enabled for current user:");
2045 for (ComponentName cmpt : mEnabledListenersForCurrentUser) {
2046 pw.println(" " + cmpt);
Daniel Sandler4b749ef2013-03-18 21:53:04 -04002047 }
Daniel Sandler4b749ef2013-03-18 21:53:04 -04002048
Daniel Sandler5feceeb2013-03-22 18:29:23 -07002049 pw.println(" Live listeners (" + mListeners.size() + "):");
Daniel Sandler4b749ef2013-03-18 21:53:04 -04002050 for (NotificationListenerInfo info : mListeners) {
Daniel Sandler5feceeb2013-03-22 18:29:23 -07002051 pw.println(" " + info.component
2052 + " (user " + info.userid + "): " + info.listener
2053 + (info.isSystem?" SYSTEM":""));
Daniel Sandler4b749ef2013-03-18 21:53:04 -04002054 }
2055
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002056 int N;
2057
2058 synchronized (mToastQueue) {
2059 N = mToastQueue.size();
2060 if (N > 0) {
2061 pw.println(" Toast Queue:");
2062 for (int i=0; i<N; i++) {
2063 mToastQueue.get(i).dump(pw, " ");
2064 }
2065 pw.println(" ");
2066 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08002067
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002068 }
2069
2070 synchronized (mNotificationList) {
2071 N = mNotificationList.size();
2072 if (N > 0) {
2073 pw.println(" Notification List:");
2074 for (int i=0; i<N; i++) {
2075 mNotificationList.get(i).dump(pw, " ", mContext);
2076 }
2077 pw.println(" ");
2078 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08002079
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002080 N = mLights.size();
2081 if (N > 0) {
2082 pw.println(" Lights List:");
2083 for (int i=0; i<N; i++) {
2084 mLights.get(i).dump(pw, " ", mContext);
2085 }
2086 pw.println(" ");
2087 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08002088
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002089 pw.println(" mSoundNotification=" + mSoundNotification);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002090 pw.println(" mVibrateNotification=" + mVibrateNotification);
Joe Onorato39f5b6a2009-07-23 12:29:19 -04002091 pw.println(" mDisabledNotifications=0x" + Integer.toHexString(mDisabledNotifications));
2092 pw.println(" mSystemReady=" + mSystemReady);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002093 }
2094 }
2095}