blob: 44d730ceb946c72eeab4dfe66ca621c86d492a5d [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;
Daniel Sandler09a247e2013-02-14 10:24:17 -050029import android.app.INotificationListener;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080030import android.app.ITransientNotification;
31import android.app.Notification;
32import android.app.PendingIntent;
33import android.app.StatusBarManager;
34import android.content.BroadcastReceiver;
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;
Dianne Hackbornd8a43f62009-08-17 23:33:56 -070039import android.content.pm.ApplicationInfo;
Daniel Sandler4a900acd2013-01-30 14:04:10 -050040import android.content.pm.PackageInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080041import android.content.pm.PackageManager;
42import android.content.pm.PackageManager.NameNotFoundException;
43import android.content.res.Resources;
Dianne Hackborn1dac2772009-06-26 18:16:48 -070044import android.database.ContentObserver;
svetoslavganov75986cf2009-05-14 22:28:01 -070045import android.media.AudioManager;
Jeff Sharkey098d5802012-04-26 17:30:34 -070046import android.media.IAudioService;
47import android.media.IRingtonePlayer;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080048import android.net.Uri;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080049import android.os.Binder;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080050import android.os.Handler;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080051import android.os.IBinder;
52import android.os.Message;
Dianne Hackbornd8a43f62009-08-17 23:33:56 -070053import android.os.Process;
svetoslavganov75986cf2009-05-14 22:28:01 -070054import android.os.RemoteException;
Jeff Sharkey098d5802012-04-26 17:30:34 -070055import android.os.ServiceManager;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -070056import android.os.UserHandle;
Daniel Sandler4b749ef2013-03-18 21:53:04 -040057import android.os.UserManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080058import android.os.Vibrator;
59import android.provider.Settings;
Daniel Sandlere96ffb12010-03-11 13:38:06 -050060import android.telephony.TelephonyManager;
svetoslavganov75986cf2009-05-14 22:28:01 -070061import android.text.TextUtils;
Dianne Hackborn39606a02012-07-31 17:54:35 -070062import android.util.AtomicFile;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080063import android.util.EventLog;
64import android.util.Log;
Andy Stadler110988c2010-12-03 14:29:16 -080065import android.util.Slog;
Daniel Sandler0da673f2012-04-11 12:33:16 -040066import android.util.Xml;
svetoslavganov75986cf2009-05-14 22:28:01 -070067import android.view.accessibility.AccessibilityEvent;
68import android.view.accessibility.AccessibilityManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080069import android.widget.Toast;
70
Jeff Sharkey098d5802012-04-26 17:30:34 -070071import com.android.internal.statusbar.StatusBarNotification;
Jeff Sharkey098d5802012-04-26 17:30:34 -070072
73import org.xmlpull.v1.XmlPullParser;
74import org.xmlpull.v1.XmlPullParserException;
Jeff Sharkey098d5802012-04-26 17:30:34 -070075
Daniel Sandler0da673f2012-04-11 12:33:16 -040076import java.io.File;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080077import java.io.FileDescriptor;
Daniel Sandler0da673f2012-04-11 12:33:16 -040078import java.io.FileInputStream;
79import java.io.FileNotFoundException;
Daniel Sandler0da673f2012-04-11 12:33:16 -040080import java.io.IOException;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080081import java.io.PrintWriter;
Daniel Sandlerfde19b12013-01-17 00:21:05 -050082import java.util.ArrayDeque;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080083import java.util.ArrayList;
84import java.util.Arrays;
Daniel Sandler0da673f2012-04-11 12:33:16 -040085import java.util.HashSet;
Daniel Sandlerfde19b12013-01-17 00:21:05 -050086import java.util.Iterator;
87import java.util.NoSuchElementException;
Daniel Sandler0da673f2012-04-11 12:33:16 -040088
89import libcore.io.IoUtils;
90
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080091
Daniel Sandlerd0a2f862010-08-03 15:29:31 -040092/** {@hide} */
93public class NotificationManagerService extends INotificationManager.Stub
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080094{
95 private static final String TAG = "NotificationService";
96 private static final boolean DBG = false;
97
Joe Onoratobd73d012010-06-04 11:44:54 -070098 private static final int MAX_PACKAGE_NOTIFICATIONS = 50;
99
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800100 // message codes
101 private static final int MESSAGE_TIMEOUT = 2;
102
103 private static final int LONG_DELAY = 3500; // 3.5 seconds
104 private static final int SHORT_DELAY = 2000; // 2 seconds
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800105
106 private static final long[] DEFAULT_VIBRATE_PATTERN = {0, 250, 250, 250};
Daniel Sandleredbb3802012-11-13 20:49:47 -0800107 private static final int VIBRATE_PATTERN_MAXLEN = 8 * 2 + 1; // up to eight bumps
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800108
109 private static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_NOTIFICATION;
Daniel Sandler49a2ad12012-03-28 15:46:39 -0400110 private static final boolean SCORE_ONGOING_HIGHER = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800111
Daniel Sandler0da673f2012-04-11 12:33:16 -0400112 private static final int JUNK_SCORE = -1000;
113 private static final int NOTIFICATION_PRIORITY_MULTIPLIER = 10;
114 private static final int SCORE_DISPLAY_THRESHOLD = Notification.PRIORITY_MIN * NOTIFICATION_PRIORITY_MULTIPLIER;
115
Daniel Sandler526fa0e2012-12-04 14:51:50 -0500116 // Notifications with scores below this will not interrupt the user, either via LED or
117 // sound or vibration
118 private static final int SCORE_INTERRUPTION_THRESHOLD =
119 Notification.PRIORITY_LOW * NOTIFICATION_PRIORITY_MULTIPLIER;
120
Daniel Sandler0da673f2012-04-11 12:33:16 -0400121 private static final boolean ENABLE_BLOCKED_NOTIFICATIONS = true;
122 private static final boolean ENABLE_BLOCKED_TOASTS = true;
123
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800124 final Context mContext;
125 final IActivityManager mAm;
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400126 final UserManager mUserManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800127 final IBinder mForegroundToken = new Binder();
128
129 private WorkerHandler mHandler;
Joe Onorato089de882010-04-12 08:18:45 -0700130 private StatusBarManagerService mStatusBar;
Mike Lockwood3cb67a32009-11-27 14:25:58 -0500131 private LightsService.Light mNotificationLight;
132 private LightsService.Light mAttentionLight;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800133
Mike Lockwood670f9322010-01-20 12:13:36 -0500134 private int mDefaultNotificationColor;
135 private int mDefaultNotificationLedOn;
136 private int mDefaultNotificationLedOff;
137
Daniel Sandleredbb3802012-11-13 20:49:47 -0800138 private long[] mDefaultVibrationPattern;
139 private long[] mFallbackVibrationPattern;
140
Joe Onorato30275482009-07-08 17:09:14 -0700141 private boolean mSystemReady;
Joe Onorato39f5b6a2009-07-23 12:29:19 -0400142 private int mDisabledNotifications;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800143
Jeff Sharkey098d5802012-04-26 17:30:34 -0700144 private NotificationRecord mSoundNotification;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800145 private NotificationRecord mVibrateNotification;
Jeff Sharkey098d5802012-04-26 17:30:34 -0700146
147 private IAudioService mAudioService;
Jeff Brownc2346132012-04-13 01:55:38 -0700148 private Vibrator mVibrator;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800149
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500150 // for enabling and disabling notification pulse behavior
Mike Lockwood63b5ad92011-08-30 09:55:30 -0400151 private boolean mScreenOn = true;
Daniel Sandlere96ffb12010-03-11 13:38:06 -0500152 private boolean mInCall = false;
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500153 private boolean mNotificationPulseEnabled;
154
Daniel Sandler09a247e2013-02-14 10:24:17 -0500155 // used as a mutex for access to all active notifications & listeners
Fred Quintana6ecaff12009-09-25 14:23:13 -0700156 private final ArrayList<NotificationRecord> mNotificationList =
157 new ArrayList<NotificationRecord>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800158
159 private ArrayList<ToastRecord> mToastQueue;
160
161 private ArrayList<NotificationRecord> mLights = new ArrayList<NotificationRecord>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800162 private NotificationRecord mLedNotification;
svetoslavganov75986cf2009-05-14 22:28:01 -0700163
Daniel Sandler4a900acd2013-01-30 14:04:10 -0500164 private final AppOpsManager mAppOps;
165
Daniel Sandler09a247e2013-02-14 10:24:17 -0500166 private ArrayList<NotificationListenerInfo> mListeners = new ArrayList<NotificationListenerInfo>();
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400167 private ArrayList<String> mEnabledListenersForCurrentUser = new ArrayList<String>();
Daniel Sandler09a247e2013-02-14 10:24:17 -0500168
Daniel Sandler0da673f2012-04-11 12:33:16 -0400169 // Notification control database. For now just contains disabled packages.
170 private AtomicFile mPolicyFile;
171 private HashSet<String> mBlockedPackages = new HashSet<String>();
172
173 private static final int DB_VERSION = 1;
174
175 private static final String TAG_BODY = "notification-policy";
176 private static final String ATTR_VERSION = "version";
177
178 private static final String TAG_BLOCKED_PKGS = "blocked-packages";
179 private static final String TAG_PACKAGE = "package";
180 private static final String ATTR_NAME = "name";
181
Daniel Sandler09a247e2013-02-14 10:24:17 -0500182 private class NotificationListenerInfo implements DeathRecipient {
183 INotificationListener listener;
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400184 String pkg;
Daniel Sandler09a247e2013-02-14 10:24:17 -0500185 int userid;
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400186 boolean isSystem;
187
188 public NotificationListenerInfo(INotificationListener listener, String pkg, int userid,
189 boolean isSystem) {
Daniel Sandler09a247e2013-02-14 10:24:17 -0500190 this.listener = listener;
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400191 this.pkg = pkg;
Daniel Sandler09a247e2013-02-14 10:24:17 -0500192 this.userid = userid;
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400193 this.isSystem = isSystem;
Daniel Sandler09a247e2013-02-14 10:24:17 -0500194 }
195
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400196 boolean enabledAndUserMatches(StatusBarNotification sbn) {
197 final int nid = sbn.getUserId();
198 if (!(isSystem || isEnabledForUser(nid))) return false;
Daniel Sandler21ca44d2013-03-07 13:58:00 -0500199 if (this.userid == UserHandle.USER_ALL) return true;
Daniel Sandler21ca44d2013-03-07 13:58:00 -0500200 return (nid == UserHandle.USER_ALL || nid == this.userid);
201 }
202
Daniel Sandler09a247e2013-02-14 10:24:17 -0500203 public void notifyPostedIfUserMatch(StatusBarNotification sbn) {
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400204 if (!enabledAndUserMatches(sbn)) return;
Daniel Sandler09a247e2013-02-14 10:24:17 -0500205 try {
206 listener.onNotificationPosted(sbn);
207 } catch (RemoteException ex) {
208 // not there?
209 }
210 }
211
212 public void notifyRemovedIfUserMatch(StatusBarNotification sbn) {
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400213 if (!enabledAndUserMatches(sbn)) return;
Daniel Sandler09a247e2013-02-14 10:24:17 -0500214 try {
215 listener.onNotificationRemoved(sbn);
216 } catch (RemoteException ex) {
217 // not there?
218 }
219 }
220
221 @Override
222 public void binderDied() {
223 unregisterListener(this.listener, this.userid);
224 }
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400225
226 /** convenience method for looking in mEnabledListenersForCurrentUser */
227 public boolean isEnabledForUser(int userid) {
228 for (int i=0; i<mEnabledListenersForCurrentUser.size(); i++) {
229 if (this.pkg.equals(mEnabledListenersForCurrentUser.get(i))) return true;
230 }
231 return false;
232 }
Daniel Sandler09a247e2013-02-14 10:24:17 -0500233 }
234
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500235 private static class Archive {
236 static final int BUFFER_SIZE = 1000;
237 ArrayDeque<StatusBarNotification> mBuffer = new ArrayDeque<StatusBarNotification>(BUFFER_SIZE);
238
239 public Archive() {
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500240 }
Jeff Sharkey0c1baf92013-04-03 13:08:52 -0700241
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500242 public void record(StatusBarNotification nr) {
Jeff Sharkey0c1baf92013-04-03 13:08:52 -0700243 // Nuke heavy parts of notification before storing in archive
244 nr.notification.tickerView = null;
245 nr.notification.contentView = null;
246 nr.notification.bigContentView = null;
247 nr.notification.largeIcon = null;
248
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500249 if (mBuffer.size() == BUFFER_SIZE) {
250 mBuffer.removeFirst();
251 }
252 mBuffer.addLast(nr);
253 }
254
255 public void clear() {
256 mBuffer.clear();
257 }
258
259 public Iterator<StatusBarNotification> descendingIterator() {
260 return mBuffer.descendingIterator();
261 }
262 public Iterator<StatusBarNotification> ascendingIterator() {
263 return mBuffer.iterator();
264 }
265 public Iterator<StatusBarNotification> filter(
266 final Iterator<StatusBarNotification> iter, final String pkg, final int userId) {
267 return new Iterator<StatusBarNotification>() {
268 StatusBarNotification mNext = findNext();
269
270 private StatusBarNotification findNext() {
271 while (iter.hasNext()) {
272 StatusBarNotification nr = iter.next();
273 if ((pkg == null || nr.pkg == pkg)
274 && (userId == UserHandle.USER_ALL || nr.getUserId() == userId)) {
275 return nr;
276 }
277 }
278 return null;
279 }
280
281 @Override
282 public boolean hasNext() {
283 return mNext == null;
284 }
285
286 @Override
287 public StatusBarNotification next() {
288 StatusBarNotification next = mNext;
289 if (next == null) {
290 throw new NoSuchElementException();
291 }
292 mNext = findNext();
293 return next;
294 }
295
296 @Override
297 public void remove() {
298 iter.remove();
299 }
300 };
301 }
Daniel Sandler78d0d252013-02-12 08:14:52 -0500302
303 public StatusBarNotification[] getArray(int count) {
304 if (count == 0) count = Archive.BUFFER_SIZE;
305 final StatusBarNotification[] a
306 = new StatusBarNotification[Math.min(count, mBuffer.size())];
307 Iterator<StatusBarNotification> iter = descendingIterator();
308 int i=0;
309 while (iter.hasNext() && i < count) {
310 a[i++] = iter.next();
311 }
312 return a;
313 }
314
315 public StatusBarNotification[] getArray(int count, String pkg, int userId) {
316 if (count == 0) count = Archive.BUFFER_SIZE;
317 final StatusBarNotification[] a
318 = new StatusBarNotification[Math.min(count, mBuffer.size())];
319 Iterator<StatusBarNotification> iter = filter(descendingIterator(), pkg, userId);
320 int i=0;
321 while (iter.hasNext() && i < count) {
322 a[i++] = iter.next();
323 }
324 return a;
325 }
326
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500327 }
328
329 Archive mArchive = new Archive();
330
Daniel Sandler0da673f2012-04-11 12:33:16 -0400331 private void loadBlockDb() {
332 synchronized(mBlockedPackages) {
333 if (mPolicyFile == null) {
334 File dir = new File("/data/system");
335 mPolicyFile = new AtomicFile(new File(dir, "notification_policy.xml"));
336
337 mBlockedPackages.clear();
338
339 FileInputStream infile = null;
340 try {
341 infile = mPolicyFile.openRead();
342 final XmlPullParser parser = Xml.newPullParser();
343 parser.setInput(infile, null);
344
345 int type;
346 String tag;
347 int version = DB_VERSION;
348 while ((type = parser.next()) != END_DOCUMENT) {
349 tag = parser.getName();
350 if (type == START_TAG) {
351 if (TAG_BODY.equals(tag)) {
352 version = Integer.parseInt(parser.getAttributeValue(null, ATTR_VERSION));
353 } else if (TAG_BLOCKED_PKGS.equals(tag)) {
354 while ((type = parser.next()) != END_DOCUMENT) {
355 tag = parser.getName();
356 if (TAG_PACKAGE.equals(tag)) {
357 mBlockedPackages.add(parser.getAttributeValue(null, ATTR_NAME));
358 } else if (TAG_BLOCKED_PKGS.equals(tag) && type == END_TAG) {
359 break;
360 }
361 }
362 }
363 }
364 }
365 } catch (FileNotFoundException e) {
366 // No data yet
367 } catch (IOException e) {
368 Log.wtf(TAG, "Unable to read blocked notifications database", e);
369 } catch (NumberFormatException e) {
370 Log.wtf(TAG, "Unable to parse blocked notifications database", e);
371 } catch (XmlPullParserException e) {
372 Log.wtf(TAG, "Unable to parse blocked notifications database", e);
373 } finally {
374 IoUtils.closeQuietly(infile);
375 }
376 }
377 }
378 }
379
Daniel Sandler4a900acd2013-01-30 14:04:10 -0500380 /**
381 * Use this when you just want to know if notifications are OK for this package.
382 */
383 public boolean areNotificationsEnabledForPackage(String pkg, int uid) {
Daniel Sandler0da673f2012-04-11 12:33:16 -0400384 checkCallerIsSystem();
Daniel Sandler4a900acd2013-01-30 14:04:10 -0500385 return (mAppOps.checkOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
386 == AppOpsManager.MODE_ALLOWED);
Daniel Sandler0da673f2012-04-11 12:33:16 -0400387 }
388
Daniel Sandler4a900acd2013-01-30 14:04:10 -0500389 /** Use this when you actually want to post a notification or toast.
390 *
391 * Unchecked. Not exposed via Binder, but can be called in the course of enqueue*().
392 */
393 private boolean noteNotificationOp(String pkg, int uid) {
394 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
395 != AppOpsManager.MODE_ALLOWED) {
396 Slog.v(TAG, "notifications are disabled by AppOps for " + pkg);
397 return false;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400398 }
Daniel Sandler4a900acd2013-01-30 14:04:10 -0500399 return true;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400400 }
401
Daniel Sandler4a900acd2013-01-30 14:04:10 -0500402 public void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) {
Daniel Sandler0da673f2012-04-11 12:33:16 -0400403 checkCallerIsSystem();
Daniel Sandler4a900acd2013-01-30 14:04:10 -0500404 if (true||DBG) {
Daniel Sandler0da673f2012-04-11 12:33:16 -0400405 Slog.v(TAG, (enabled?"en":"dis") + "abling notifications for " + pkg);
406 }
Daniel Sandler4a900acd2013-01-30 14:04:10 -0500407 mAppOps.setMode(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg,
408 enabled ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED);
Daniel Sandler0da673f2012-04-11 12:33:16 -0400409 }
410
411
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800412 private static String idDebugString(Context baseContext, String packageName, int id) {
413 Context c = null;
414
415 if (packageName != null) {
416 try {
417 c = baseContext.createPackageContext(packageName, 0);
418 } catch (NameNotFoundException e) {
419 c = baseContext;
420 }
421 } else {
422 c = baseContext;
423 }
424
425 String pkg;
426 String type;
427 String name;
428
429 Resources r = c.getResources();
430 try {
431 return r.getResourceName(id);
432 } catch (Resources.NotFoundException e) {
433 return "<name unknown>";
434 }
435 }
436
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500437 public StatusBarNotification[] getActiveNotifications(String callingPkg) {
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400438 // enforce() will ensure the calling uid has the correct permission
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500439 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_NOTIFICATIONS,
Daniel Sandler78d0d252013-02-12 08:14:52 -0500440 "NotificationManagerService.getActiveNotifications");
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500441
442 StatusBarNotification[] tmp = null;
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500443 int uid = Binder.getCallingUid();
444
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400445 // noteOp will check to make sure the callingPkg matches the uid
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500446 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
447 == AppOpsManager.MODE_ALLOWED) {
448 synchronized (mNotificationList) {
449 tmp = new StatusBarNotification[mNotificationList.size()];
450 final int N = mNotificationList.size();
451 for (int i=0; i<N; i++) {
452 tmp[i] = mNotificationList.get(i).sbn;
453 }
454 }
455 }
456 return tmp;
457 }
458
Daniel Sandler78d0d252013-02-12 08:14:52 -0500459 public StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count) {
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400460 // enforce() will ensure the calling uid has the correct permission
Daniel Sandler78d0d252013-02-12 08:14:52 -0500461 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_NOTIFICATIONS,
462 "NotificationManagerService.getHistoricalNotifications");
463
464 StatusBarNotification[] tmp = null;
465 int uid = Binder.getCallingUid();
466
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400467 // noteOp will check to make sure the callingPkg matches the uid
Daniel Sandler78d0d252013-02-12 08:14:52 -0500468 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
469 == AppOpsManager.MODE_ALLOWED) {
470 synchronized (mArchive) {
471 tmp = mArchive.getArray(count);
472 }
473 }
474 return tmp;
475 }
476
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400477 boolean packageCanTapNotificationsForUser(final int uid, final String pkg) {
478 // Make sure the package and uid match, and that the package is allowed access
479 return (AppOpsManager.MODE_ALLOWED
480 == mAppOps.checkOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, pkg));
481 }
482
Daniel Sandler09a247e2013-02-14 10:24:17 -0500483 @Override
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400484 public void registerListener(final INotificationListener listener,
485 final String pkg, final int userid) {
486 // ensure system or allowed pkg
487 int uid = Binder.getCallingUid();
488 boolean isSystem = (UserHandle.getAppId(uid) == Process.SYSTEM_UID || uid == 0);
489 if (!(isSystem || packageCanTapNotificationsForUser(uid, pkg))) {
490 throw new SecurityException("Package " + pkg
491 + " may not listen for notifications");
492 }
493
Daniel Sandler09a247e2013-02-14 10:24:17 -0500494 synchronized (mNotificationList) {
495 try {
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400496 NotificationListenerInfo info
497 = new NotificationListenerInfo(listener, pkg, userid, isSystem);
Daniel Sandler09a247e2013-02-14 10:24:17 -0500498 listener.asBinder().linkToDeath(info, 0);
499 mListeners.add(info);
500 } catch (RemoteException e) {
501 // already dead
502 }
503 }
504 }
505
506 @Override
507 public void unregisterListener(INotificationListener listener, int userid) {
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400508 // no need to check permissions; if your listener binder is in the list,
509 // that's proof that you had permission to add it in the first place
510
Daniel Sandler09a247e2013-02-14 10:24:17 -0500511 synchronized (mNotificationList) {
512 final int N = mListeners.size();
513 for (int i=N-1; i>=0; i--) {
514 final NotificationListenerInfo info = mListeners.get(i);
515 if (info.listener == listener && info.userid == userid) {
516 mListeners.remove(listener);
517 }
518 }
519 }
520 }
521
522 private void notifyPostedLocked(NotificationRecord n) {
523 final StatusBarNotification sbn = n.sbn;
524 for (final NotificationListenerInfo info : mListeners) {
525 mHandler.post(new Runnable() {
526 @Override
527 public void run() {
528 info.notifyPostedIfUserMatch(sbn);
529 }});
530 }
531 }
532
533 private void notifyRemovedLocked(NotificationRecord n) {
534 final StatusBarNotification sbn = n.sbn;
535 for (final NotificationListenerInfo info : mListeners) {
536 mHandler.post(new Runnable() {
537 @Override
538 public void run() {
539 info.notifyRemovedIfUserMatch(sbn);
540 }});
541 }
542 }
543
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500544 public static final class NotificationRecord
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800545 {
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500546 final StatusBarNotification sbn;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800547 IBinder statusBarKey;
548
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500549 NotificationRecord(StatusBarNotification sbn)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800550 {
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500551 this.sbn = sbn;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800552 }
Fred Quintana6ecaff12009-09-25 14:23:13 -0700553
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500554 public Notification getNotification() { return sbn.notification; }
555 public int getFlags() { return sbn.notification.flags; }
556 public int getUserId() { return sbn.getUserId(); }
557
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800558 void dump(PrintWriter pw, String prefix, Context baseContext) {
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500559 final Notification notification = sbn.notification;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800560 pw.println(prefix + this);
561 pw.println(prefix + " icon=0x" + Integer.toHexString(notification.icon)
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500562 + " / " + idDebugString(baseContext, this.sbn.pkg, notification.icon));
Daniel Sandler2561b0b2012-02-13 21:04:12 -0500563 pw.println(prefix + " pri=" + notification.priority);
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500564 pw.println(prefix + " score=" + this.sbn.score);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800565 pw.println(prefix + " contentIntent=" + notification.contentIntent);
566 pw.println(prefix + " deleteIntent=" + notification.deleteIntent);
567 pw.println(prefix + " tickerText=" + notification.tickerText);
568 pw.println(prefix + " contentView=" + notification.contentView);
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500569 pw.println(prefix + " uid=" + this.sbn.uid + " userId=" + this.sbn.getUserId());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800570 pw.println(prefix + " defaults=0x" + Integer.toHexString(notification.defaults));
571 pw.println(prefix + " flags=0x" + Integer.toHexString(notification.flags));
572 pw.println(prefix + " sound=" + notification.sound);
573 pw.println(prefix + " vibrate=" + Arrays.toString(notification.vibrate));
574 pw.println(prefix + " ledARGB=0x" + Integer.toHexString(notification.ledARGB)
575 + " ledOnMS=" + notification.ledOnMS
576 + " ledOffMS=" + notification.ledOffMS);
577 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800578
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800579 @Override
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500580 public final String toString() {
581 return String.format(
582 "NotificationRecord(0x%08x: pkg=%s user=%s id=%d tag=%s score=%d: %s)",
583 System.identityHashCode(this),
584 this.sbn.pkg, this.sbn.user, this.sbn.id, this.sbn.tag,
585 this.sbn.score, this.sbn.notification);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800586 }
587 }
588
589 private static final class ToastRecord
590 {
591 final int pid;
592 final String pkg;
593 final ITransientNotification callback;
594 int duration;
595
596 ToastRecord(int pid, String pkg, ITransientNotification callback, int duration)
597 {
598 this.pid = pid;
599 this.pkg = pkg;
600 this.callback = callback;
601 this.duration = duration;
602 }
603
604 void update(int duration) {
605 this.duration = duration;
606 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800607
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800608 void dump(PrintWriter pw, String prefix) {
609 pw.println(prefix + this);
610 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800611
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800612 @Override
613 public final String toString()
614 {
615 return "ToastRecord{"
616 + Integer.toHexString(System.identityHashCode(this))
617 + " pkg=" + pkg
618 + " callback=" + callback
619 + " duration=" + duration;
620 }
621 }
622
Joe Onorato089de882010-04-12 08:18:45 -0700623 private StatusBarManagerService.NotificationCallbacks mNotificationCallbacks
624 = new StatusBarManagerService.NotificationCallbacks() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800625
626 public void onSetDisabled(int status) {
627 synchronized (mNotificationList) {
628 mDisabledNotifications = status;
629 if ((mDisabledNotifications & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) {
630 // cancel whatever's going on
631 long identity = Binder.clearCallingIdentity();
632 try {
Jeff Sharkey098d5802012-04-26 17:30:34 -0700633 final IRingtonePlayer player = mAudioService.getRingtonePlayer();
634 if (player != null) {
635 player.stopAsync();
636 }
637 } catch (RemoteException e) {
638 } finally {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800639 Binder.restoreCallingIdentity(identity);
640 }
641
642 identity = Binder.clearCallingIdentity();
643 try {
644 mVibrator.cancel();
Jeff Sharkey098d5802012-04-26 17:30:34 -0700645 } finally {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800646 Binder.restoreCallingIdentity(identity);
647 }
648 }
649 }
650 }
651
652 public void onClearAll() {
Dianne Hackborn41203752012-08-31 14:05:51 -0700653 // XXX to be totally correct, the caller should tell us which user
654 // this is for.
655 cancelAll(ActivityManager.getCurrentUser());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800656 }
657
Fred Quintana6ecaff12009-09-25 14:23:13 -0700658 public void onNotificationClick(String pkg, String tag, int id) {
Dianne Hackborn41203752012-08-31 14:05:51 -0700659 // XXX to be totally correct, the caller should tell us which user
660 // this is for.
Fred Quintana6ecaff12009-09-25 14:23:13 -0700661 cancelNotification(pkg, tag, id, Notification.FLAG_AUTO_CANCEL,
Dianne Hackborn41203752012-08-31 14:05:51 -0700662 Notification.FLAG_FOREGROUND_SERVICE, false,
663 ActivityManager.getCurrentUser());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800664 }
665
Daniel Sandler0f0b11c2010-08-04 15:54:58 -0400666 public void onNotificationClear(String pkg, String tag, int id) {
Dianne Hackborn41203752012-08-31 14:05:51 -0700667 // XXX to be totally correct, the caller should tell us which user
668 // this is for.
Joe Onorato46439ce2010-11-19 13:56:21 -0800669 cancelNotification(pkg, tag, id, 0,
670 Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
Dianne Hackborn41203752012-08-31 14:05:51 -0700671 true, ActivityManager.getCurrentUser());
Daniel Sandler0f0b11c2010-08-04 15:54:58 -0400672 }
673
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800674 public void onPanelRevealed() {
675 synchronized (mNotificationList) {
676 // sound
677 mSoundNotification = null;
Jeff Sharkey098d5802012-04-26 17:30:34 -0700678
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800679 long identity = Binder.clearCallingIdentity();
680 try {
Jeff Sharkey098d5802012-04-26 17:30:34 -0700681 final IRingtonePlayer player = mAudioService.getRingtonePlayer();
682 if (player != null) {
683 player.stopAsync();
684 }
685 } catch (RemoteException e) {
686 } finally {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800687 Binder.restoreCallingIdentity(identity);
688 }
689
690 // vibrate
691 mVibrateNotification = null;
692 identity = Binder.clearCallingIdentity();
693 try {
694 mVibrator.cancel();
Jeff Sharkey098d5802012-04-26 17:30:34 -0700695 } finally {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800696 Binder.restoreCallingIdentity(identity);
697 }
698
699 // light
700 mLights.clear();
701 mLedNotification = null;
702 updateLightsLocked();
703 }
704 }
Joe Onorato005847b2010-06-04 16:08:02 -0400705
Dianne Hackborn9d39d0c2010-06-24 15:57:42 -0700706 public void onNotificationError(String pkg, String tag, int id,
707 int uid, int initialPid, String message) {
Daniel Sandlerd0a2f862010-08-03 15:29:31 -0400708 Slog.d(TAG, "onNotification error pkg=" + pkg + " tag=" + tag + " id=" + id
709 + "; will crashApplication(uid=" + uid + ", pid=" + initialPid + ")");
Dianne Hackborn41203752012-08-31 14:05:51 -0700710 // XXX to be totally correct, the caller should tell us which user
711 // this is for.
712 cancelNotification(pkg, tag, id, 0, 0, false, UserHandle.getUserId(uid));
Dianne Hackborn9d39d0c2010-06-24 15:57:42 -0700713 long ident = Binder.clearCallingIdentity();
714 try {
715 ActivityManagerNative.getDefault().crashApplication(uid, initialPid, pkg,
716 "Bad notification posted from package " + pkg
717 + ": " + message);
718 } catch (RemoteException e) {
719 }
720 Binder.restoreCallingIdentity(ident);
Joe Onorato005847b2010-06-04 16:08:02 -0400721 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800722 };
723
724 private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
725 @Override
726 public void onReceive(Context context, Intent intent) {
727 String action = intent.getAction();
728
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800729 boolean queryRestart = false;
Daniel Sandler26ece572012-06-01 15:38:46 -0400730 boolean packageChanged = false;
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800731
Mike Lockwood541c9942011-06-12 19:35:45 -0400732 if (action.equals(Intent.ACTION_PACKAGE_REMOVED)
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800733 || action.equals(Intent.ACTION_PACKAGE_RESTARTED)
Daniel Sandler26ece572012-06-01 15:38:46 -0400734 || (packageChanged=action.equals(Intent.ACTION_PACKAGE_CHANGED))
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800735 || (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART))
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800736 || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800737 String pkgList[] = null;
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800738 if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800739 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800740 } else if (queryRestart) {
741 pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800742 } else {
743 Uri uri = intent.getData();
744 if (uri == null) {
745 return;
746 }
747 String pkgName = uri.getSchemeSpecificPart();
748 if (pkgName == null) {
749 return;
750 }
Daniel Sandler26ece572012-06-01 15:38:46 -0400751 if (packageChanged) {
752 // We cancel notifications for packages which have just been disabled
753 final int enabled = mContext.getPackageManager()
754 .getApplicationEnabledSetting(pkgName);
755 if (enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED
756 || enabled == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
757 return;
758 }
759 }
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800760 pkgList = new String[]{pkgName};
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800761 }
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800762 if (pkgList != null && (pkgList.length > 0)) {
763 for (String pkgName : pkgList) {
Dianne Hackborn41203752012-08-31 14:05:51 -0700764 cancelAllNotificationsInt(pkgName, 0, 0, !queryRestart,
765 UserHandle.USER_ALL);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800766 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800767 }
Mike Lockwood63b5ad92011-08-30 09:55:30 -0400768 } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
769 // Keep track of screen on/off state, but do not turn off the notification light
770 // until user passes through the lock screen or views the notification.
771 mScreenOn = true;
772 } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
773 mScreenOn = false;
Daniel Sandlere96ffb12010-03-11 13:38:06 -0500774 } else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) {
Mike Lockwood63b5ad92011-08-30 09:55:30 -0400775 mInCall = (intent.getStringExtra(TelephonyManager.EXTRA_STATE).equals(
776 TelephonyManager.EXTRA_STATE_OFFHOOK));
Daniel Sandlere96ffb12010-03-11 13:38:06 -0500777 updateNotificationPulse();
Dianne Hackborn80a4af22012-08-27 19:18:31 -0700778 } else if (action.equals(Intent.ACTION_USER_STOPPED)) {
779 int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
780 if (userHandle >= 0) {
Dianne Hackborn41203752012-08-31 14:05:51 -0700781 cancelAllNotificationsInt(null, 0, 0, true, userHandle);
Dianne Hackborn80a4af22012-08-27 19:18:31 -0700782 }
Mike Lockwood63b5ad92011-08-30 09:55:30 -0400783 } else if (action.equals(Intent.ACTION_USER_PRESENT)) {
784 // turn off LED when user passes through lock screen
785 mNotificationLight.turnOff();
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400786 } else if (action.equals(Intent.ACTION_USER_SWITCHED)) {
787 // reload per-user settings
788 mSettingsObserver.update(null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800789 }
790 }
791 };
792
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700793 class SettingsObserver extends ContentObserver {
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400794 private final Uri NOTIFICATION_LIGHT_PULSE_URI
795 = Settings.System.getUriFor(Settings.System.NOTIFICATION_LIGHT_PULSE);
796
797 private final Uri ENABLED_NOTIFICATION_LISTENERS_URI
798 = Settings.System.getUriFor(Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
799
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700800 SettingsObserver(Handler handler) {
801 super(handler);
802 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800803
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700804 void observe() {
805 ContentResolver resolver = mContext.getContentResolver();
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400806 resolver.registerContentObserver(NOTIFICATION_LIGHT_PULSE_URI,
807 false, this);
808 resolver.registerContentObserver(ENABLED_NOTIFICATION_LISTENERS_URI,
809 false, this);
810 update(null);
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700811 }
812
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400813 @Override public void onChange(boolean selfChange, Uri uri) {
814 update(uri);
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700815 }
816
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400817 public void update(Uri uri) {
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700818 ContentResolver resolver = mContext.getContentResolver();
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400819 if (uri == null || NOTIFICATION_LIGHT_PULSE_URI.equals(uri)) {
820 boolean pulseEnabled = Settings.System.getInt(resolver,
821 Settings.System.NOTIFICATION_LIGHT_PULSE, 0) != 0;
822 if (mNotificationPulseEnabled != pulseEnabled) {
823 mNotificationPulseEnabled = pulseEnabled;
824 updateNotificationPulse();
825 }
826 }
827 if (uri == null || ENABLED_NOTIFICATION_LISTENERS_URI.equals(uri)) {
828 String pkglist = Settings.Secure.getString(
829 mContext.getContentResolver(),
830 Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
831 mEnabledListenersForCurrentUser.clear();
832 if (pkglist != null) {
833 String[] pkgs = pkglist.split(";");
834 for (int i=0; i<pkgs.length; i++) {
835 final String pkg = pkgs[i];
836 if (pkg != null && ! "".equals(pkg)) {
837 mEnabledListenersForCurrentUser.add(pkgs[i]);
838 }
839 }
840 }
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500841 }
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700842 }
843 }
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500844
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400845 private SettingsObserver mSettingsObserver;
846
Daniel Sandleredbb3802012-11-13 20:49:47 -0800847 static long[] getLongArray(Resources r, int resid, int maxlen, long[] def) {
848 int[] ar = r.getIntArray(resid);
849 if (ar == null) {
850 return def;
851 }
852 final int len = ar.length > maxlen ? maxlen : ar.length;
853 long[] out = new long[len];
854 for (int i=0; i<len; i++) {
855 out[i] = ar[i];
856 }
857 return out;
858 }
859
Joe Onorato089de882010-04-12 08:18:45 -0700860 NotificationManagerService(Context context, StatusBarManagerService statusBar,
Mike Lockwood3a322132009-11-24 00:30:52 -0500861 LightsService lights)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800862 {
863 super();
864 mContext = context;
Jeff Brownc2346132012-04-13 01:55:38 -0700865 mVibrator = (Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800866 mAm = ActivityManagerNative.getDefault();
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400867 mUserManager = (UserManager)context.getSystemService(Context.USER_SERVICE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800868 mToastQueue = new ArrayList<ToastRecord>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800869 mHandler = new WorkerHandler();
San Mehat3ee13172010-02-04 20:54:43 -0800870
Daniel Sandler4a900acd2013-01-30 14:04:10 -0500871 mAppOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE);
872
873 importOldBlockDb();
Daniel Sandler0da673f2012-04-11 12:33:16 -0400874
Joe Onorato089de882010-04-12 08:18:45 -0700875 mStatusBar = statusBar;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800876 statusBar.setNotificationCallbacks(mNotificationCallbacks);
877
Mike Lockwood3cb67a32009-11-27 14:25:58 -0500878 mNotificationLight = lights.getLight(LightsService.LIGHT_ID_NOTIFICATIONS);
879 mAttentionLight = lights.getLight(LightsService.LIGHT_ID_ATTENTION);
880
Mike Lockwood670f9322010-01-20 12:13:36 -0500881 Resources resources = mContext.getResources();
882 mDefaultNotificationColor = resources.getColor(
883 com.android.internal.R.color.config_defaultNotificationColor);
884 mDefaultNotificationLedOn = resources.getInteger(
885 com.android.internal.R.integer.config_defaultNotificationLedOn);
886 mDefaultNotificationLedOff = resources.getInteger(
887 com.android.internal.R.integer.config_defaultNotificationLedOff);
888
Daniel Sandleredbb3802012-11-13 20:49:47 -0800889 mDefaultVibrationPattern = getLongArray(resources,
890 com.android.internal.R.array.config_defaultNotificationVibePattern,
891 VIBRATE_PATTERN_MAXLEN,
892 DEFAULT_VIBRATE_PATTERN);
893
894 mFallbackVibrationPattern = getLongArray(resources,
895 com.android.internal.R.array.config_notificationFallbackVibePattern,
896 VIBRATE_PATTERN_MAXLEN,
897 DEFAULT_VIBRATE_PATTERN);
898
Joe Onorato39f5b6a2009-07-23 12:29:19 -0400899 // Don't start allowing notifications until the setup wizard has run once.
900 // After that, including subsequent boots, init with notifications turned on.
901 // This works on the first boot because the setup wizard will toggle this
902 // flag at least once and we'll go back to 0 after that.
Jeff Brownbf6f6f92012-09-25 15:03:20 -0700903 if (0 == Settings.Global.getInt(mContext.getContentResolver(),
904 Settings.Global.DEVICE_PROVISIONED, 0)) {
Joe Onorato39f5b6a2009-07-23 12:29:19 -0400905 mDisabledNotifications = StatusBarManager.DISABLE_NOTIFICATION_ALERTS;
906 }
907
Mike Lockwood35e16bf2010-11-30 19:53:36 -0500908 // register for various Intents
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800909 IntentFilter filter = new IntentFilter();
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500910 filter.addAction(Intent.ACTION_SCREEN_ON);
911 filter.addAction(Intent.ACTION_SCREEN_OFF);
Daniel Sandlere96ffb12010-03-11 13:38:06 -0500912 filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
Mike Lockwood63b5ad92011-08-30 09:55:30 -0400913 filter.addAction(Intent.ACTION_USER_PRESENT);
Dianne Hackborn80a4af22012-08-27 19:18:31 -0700914 filter.addAction(Intent.ACTION_USER_STOPPED);
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400915 filter.addAction(Intent.ACTION_USER_SWITCHED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800916 mContext.registerReceiver(mIntentReceiver, filter);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800917 IntentFilter pkgFilter = new IntentFilter();
918 pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
Daniel Sandleraac0eb02011-08-06 22:51:56 -0400919 pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800920 pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
921 pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
922 pkgFilter.addDataScheme("package");
923 mContext.registerReceiver(mIntentReceiver, pkgFilter);
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800924 IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800925 mContext.registerReceiver(mIntentReceiver, sdFilter);
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800926
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400927 mSettingsObserver = new SettingsObserver(mHandler);
928 mSettingsObserver.observe();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800929 }
930
Daniel Sandler4a900acd2013-01-30 14:04:10 -0500931 /**
932 * Read the old XML-based app block database and import those blockages into the AppOps system.
933 */
934 private void importOldBlockDb() {
935 loadBlockDb();
936
937 PackageManager pm = mContext.getPackageManager();
938 for (String pkg : mBlockedPackages) {
939 PackageInfo info = null;
940 try {
941 info = pm.getPackageInfo(pkg, 0);
942 setNotificationsEnabledForPackage(pkg, info.applicationInfo.uid, false);
943 } catch (NameNotFoundException e) {
944 // forget you
945 }
946 }
947 mBlockedPackages.clear();
948 if (mPolicyFile != null) {
949 mPolicyFile.delete();
950 }
951 }
952
Joe Onorato30275482009-07-08 17:09:14 -0700953 void systemReady() {
Jeff Sharkey098d5802012-04-26 17:30:34 -0700954 mAudioService = IAudioService.Stub.asInterface(
955 ServiceManager.getService(Context.AUDIO_SERVICE));
956
Joe Onorato30275482009-07-08 17:09:14 -0700957 // no beeping until we're basically done booting
958 mSystemReady = true;
959 }
960
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800961 // Toasts
962 // ============================================================================
963 public void enqueueToast(String pkg, ITransientNotification callback, int duration)
964 {
Daniel Sandlera7035902010-03-30 15:45:31 -0400965 if (DBG) Slog.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback + " duration=" + duration);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800966
967 if (pkg == null || callback == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800968 Slog.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800969 return ;
970 }
971
Daniel Sandler0da673f2012-04-11 12:33:16 -0400972 final boolean isSystemToast = ("android".equals(pkg));
973
Daniel Sandler4a900acd2013-01-30 14:04:10 -0500974 if (ENABLE_BLOCKED_TOASTS && !noteNotificationOp(pkg, Binder.getCallingUid())) {
975 if (!isSystemToast) {
976 Slog.e(TAG, "Suppressing toast from package " + pkg + " by user request.");
977 return;
978 }
Daniel Sandler0da673f2012-04-11 12:33:16 -0400979 }
980
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800981 synchronized (mToastQueue) {
982 int callingPid = Binder.getCallingPid();
983 long callingId = Binder.clearCallingIdentity();
984 try {
985 ToastRecord record;
986 int index = indexOfToastLocked(pkg, callback);
987 // If it's already in the queue, we update it in place, we don't
988 // move it to the end of the queue.
989 if (index >= 0) {
990 record = mToastQueue.get(index);
991 record.update(duration);
992 } else {
Vairavan Srinivasanf9eb06c2011-01-21 18:08:36 -0800993 // Limit the number of toasts that any given package except the android
994 // package can enqueue. Prevents DOS attacks and deals with leaks.
Daniel Sandler0da673f2012-04-11 12:33:16 -0400995 if (!isSystemToast) {
Vairavan Srinivasanf9eb06c2011-01-21 18:08:36 -0800996 int count = 0;
997 final int N = mToastQueue.size();
998 for (int i=0; i<N; i++) {
999 final ToastRecord r = mToastQueue.get(i);
1000 if (r.pkg.equals(pkg)) {
1001 count++;
1002 if (count >= MAX_PACKAGE_NOTIFICATIONS) {
1003 Slog.e(TAG, "Package has already posted " + count
1004 + " toasts. Not showing more. Package=" + pkg);
1005 return;
1006 }
1007 }
1008 }
1009 }
1010
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001011 record = new ToastRecord(callingPid, pkg, callback, duration);
1012 mToastQueue.add(record);
1013 index = mToastQueue.size() - 1;
1014 keepProcessAliveLocked(callingPid);
1015 }
1016 // If it's at index 0, it's the current toast. It doesn't matter if it's
1017 // new or just been updated. Call back and tell it to show itself.
1018 // If the callback fails, this will remove it from the list, so don't
1019 // assume that it's valid after this.
1020 if (index == 0) {
1021 showNextToastLocked();
1022 }
1023 } finally {
1024 Binder.restoreCallingIdentity(callingId);
1025 }
1026 }
1027 }
1028
1029 public void cancelToast(String pkg, ITransientNotification callback) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001030 Slog.i(TAG, "cancelToast pkg=" + pkg + " callback=" + callback);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001031
1032 if (pkg == null || callback == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001033 Slog.e(TAG, "Not cancelling notification. pkg=" + pkg + " callback=" + callback);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001034 return ;
1035 }
1036
1037 synchronized (mToastQueue) {
1038 long callingId = Binder.clearCallingIdentity();
1039 try {
1040 int index = indexOfToastLocked(pkg, callback);
1041 if (index >= 0) {
1042 cancelToastLocked(index);
1043 } else {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001044 Slog.w(TAG, "Toast already cancelled. pkg=" + pkg + " callback=" + callback);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001045 }
1046 } finally {
1047 Binder.restoreCallingIdentity(callingId);
1048 }
1049 }
1050 }
1051
1052 private void showNextToastLocked() {
1053 ToastRecord record = mToastQueue.get(0);
1054 while (record != null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001055 if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001056 try {
1057 record.callback.show();
1058 scheduleTimeoutLocked(record, false);
1059 return;
1060 } catch (RemoteException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001061 Slog.w(TAG, "Object died trying to show notification " + record.callback
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001062 + " in package " + record.pkg);
1063 // remove it from the list and let the process die
1064 int index = mToastQueue.indexOf(record);
1065 if (index >= 0) {
1066 mToastQueue.remove(index);
1067 }
1068 keepProcessAliveLocked(record.pid);
1069 if (mToastQueue.size() > 0) {
1070 record = mToastQueue.get(0);
1071 } else {
1072 record = null;
1073 }
1074 }
1075 }
1076 }
1077
1078 private void cancelToastLocked(int index) {
1079 ToastRecord record = mToastQueue.get(index);
1080 try {
1081 record.callback.hide();
1082 } catch (RemoteException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001083 Slog.w(TAG, "Object died trying to hide notification " + record.callback
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001084 + " in package " + record.pkg);
1085 // don't worry about this, we're about to remove it from
1086 // the list anyway
1087 }
1088 mToastQueue.remove(index);
1089 keepProcessAliveLocked(record.pid);
1090 if (mToastQueue.size() > 0) {
1091 // Show the next one. If the callback fails, this will remove
1092 // it from the list, so don't assume that the list hasn't changed
1093 // after this point.
1094 showNextToastLocked();
1095 }
1096 }
1097
1098 private void scheduleTimeoutLocked(ToastRecord r, boolean immediate)
1099 {
1100 Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r);
1101 long delay = immediate ? 0 : (r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY);
1102 mHandler.removeCallbacksAndMessages(r);
1103 mHandler.sendMessageDelayed(m, delay);
1104 }
1105
1106 private void handleTimeout(ToastRecord record)
1107 {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001108 if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001109 synchronized (mToastQueue) {
1110 int index = indexOfToastLocked(record.pkg, record.callback);
1111 if (index >= 0) {
1112 cancelToastLocked(index);
1113 }
1114 }
1115 }
1116
1117 // lock on mToastQueue
1118 private int indexOfToastLocked(String pkg, ITransientNotification callback)
1119 {
1120 IBinder cbak = callback.asBinder();
1121 ArrayList<ToastRecord> list = mToastQueue;
1122 int len = list.size();
1123 for (int i=0; i<len; i++) {
1124 ToastRecord r = list.get(i);
1125 if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) {
1126 return i;
1127 }
1128 }
1129 return -1;
1130 }
1131
1132 // lock on mToastQueue
1133 private void keepProcessAliveLocked(int pid)
1134 {
1135 int toastCount = 0; // toasts from this pid
1136 ArrayList<ToastRecord> list = mToastQueue;
1137 int N = list.size();
1138 for (int i=0; i<N; i++) {
1139 ToastRecord r = list.get(i);
1140 if (r.pid == pid) {
1141 toastCount++;
1142 }
1143 }
1144 try {
1145 mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0);
1146 } catch (RemoteException e) {
1147 // Shouldn't happen.
1148 }
1149 }
1150
1151 private final class WorkerHandler extends Handler
1152 {
1153 @Override
1154 public void handleMessage(Message msg)
1155 {
1156 switch (msg.what)
1157 {
1158 case MESSAGE_TIMEOUT:
1159 handleTimeout((ToastRecord)msg.obj);
1160 break;
1161 }
1162 }
1163 }
1164
1165
1166 // Notifications
1167 // ============================================================================
Dianne Hackbornf265ea92013-01-31 15:00:51 -08001168 public void enqueueNotificationWithTag(String pkg, String basePkg, String tag, int id,
1169 Notification notification, int[] idOut, int userId)
Fred Quintana6ecaff12009-09-25 14:23:13 -07001170 {
Dianne Hackbornf265ea92013-01-31 15:00:51 -08001171 enqueueNotificationInternal(pkg, basePkg, Binder.getCallingUid(), Binder.getCallingPid(),
Dianne Hackborn41203752012-08-31 14:05:51 -07001172 tag, id, notification, idOut, userId);
Daniel Sandlerd0a2f862010-08-03 15:29:31 -04001173 }
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001174
1175 private final static int clamp(int x, int low, int high) {
1176 return (x < low) ? low : ((x > high) ? high : x);
Daniel Sandlere40451a2011-02-03 14:51:35 -05001177 }
1178
Daniel Sandlerd0a2f862010-08-03 15:29:31 -04001179 // Not exposed via Binder; for system use only (otherwise malicious apps could spoof the
1180 // uid/pid of another application)
Dianne Hackbornf265ea92013-01-31 15:00:51 -08001181 public void enqueueNotificationInternal(String pkg, String basePkg, int callingUid,
1182 int callingPid, String tag, int id, Notification notification, int[] idOut, int userId)
Daniel Sandlerd0a2f862010-08-03 15:29:31 -04001183 {
Daniel Sandler0da673f2012-04-11 12:33:16 -04001184 if (DBG) {
1185 Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id + " notification=" + notification);
1186 }
1187 checkCallerIsSystemOrSameApp(pkg);
1188 final boolean isSystemNotification = ("android".equals(pkg));
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001189
Dianne Hackborn41203752012-08-31 14:05:51 -07001190 userId = ActivityManager.handleIncomingUser(callingPid,
Amith Yamasani2c7ebea2012-10-30 15:28:27 -07001191 callingUid, userId, true, false, "enqueueNotification", pkg);
Jeff Sharkey65c4a2b2012-09-25 17:22:27 -07001192 final UserHandle user = new UserHandle(userId);
Dianne Hackborn41203752012-08-31 14:05:51 -07001193
Joe Onoratobd73d012010-06-04 11:44:54 -07001194 // Limit the number of notifications that any given package except the android
1195 // package can enqueue. Prevents DOS attacks and deals with leaks.
Daniel Sandler0da673f2012-04-11 12:33:16 -04001196 if (!isSystemNotification) {
Joe Onoratobd73d012010-06-04 11:44:54 -07001197 synchronized (mNotificationList) {
1198 int count = 0;
1199 final int N = mNotificationList.size();
1200 for (int i=0; i<N; i++) {
1201 final NotificationRecord r = mNotificationList.get(i);
Daniel Sandlerfde19b12013-01-17 00:21:05 -05001202 if (r.sbn.pkg.equals(pkg) && r.sbn.getUserId() == userId) {
Joe Onoratobd73d012010-06-04 11:44:54 -07001203 count++;
1204 if (count >= MAX_PACKAGE_NOTIFICATIONS) {
1205 Slog.e(TAG, "Package has already posted " + count
1206 + " notifications. Not showing more. package=" + pkg);
1207 return;
1208 }
1209 }
1210 }
1211 }
1212 }
1213
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001214 // This conditional is a dirty hack to limit the logging done on
1215 // behalf of the download manager without affecting other apps.
1216 if (!pkg.equals("com.android.providers.downloads")
1217 || Log.isLoggable("DownloadManager", Log.VERBOSE)) {
Daniel Sandler321e9c52012-10-12 10:59:26 -07001218 EventLog.writeEvent(EventLogTags.NOTIFICATION_ENQUEUE, pkg, id, tag, userId,
Daniel Sandlerb64cb882011-11-29 23:48:29 -05001219 notification.toString());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001220 }
1221
1222 if (pkg == null || notification == null) {
1223 throw new IllegalArgumentException("null not allowed: pkg=" + pkg
1224 + " id=" + id + " notification=" + notification);
1225 }
1226 if (notification.icon != 0) {
1227 if (notification.contentView == null) {
1228 throw new IllegalArgumentException("contentView required: pkg=" + pkg
1229 + " id=" + id + " notification=" + notification);
1230 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001231 }
1232
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001233 // === Scoring ===
Daniel Sandler0da673f2012-04-11 12:33:16 -04001234
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001235 // 0. Sanitize inputs
1236 notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN, Notification.PRIORITY_MAX);
1237 // Migrate notification flags to scores
1238 if (0 != (notification.flags & Notification.FLAG_HIGH_PRIORITY)) {
1239 if (notification.priority < Notification.PRIORITY_MAX) notification.priority = Notification.PRIORITY_MAX;
Daniel Sandler49a2ad12012-03-28 15:46:39 -04001240 } else if (SCORE_ONGOING_HIGHER && 0 != (notification.flags & Notification.FLAG_ONGOING_EVENT)) {
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001241 if (notification.priority < Notification.PRIORITY_HIGH) notification.priority = Notification.PRIORITY_HIGH;
1242 }
Daniel Sandler0da673f2012-04-11 12:33:16 -04001243
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001244 // 1. initial score: buckets of 10, around the app
Daniel Sandler0da673f2012-04-11 12:33:16 -04001245 int score = notification.priority * NOTIFICATION_PRIORITY_MULTIPLIER; //[-20..20]
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001246
Daniel Sandler0da673f2012-04-11 12:33:16 -04001247 // 2. Consult external heuristics (TBD)
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001248
Daniel Sandler0da673f2012-04-11 12:33:16 -04001249 // 3. Apply local rules
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001250
1251 // blocked apps
Daniel Sandler4a900acd2013-01-30 14:04:10 -05001252 if (ENABLE_BLOCKED_NOTIFICATIONS && !noteNotificationOp(pkg, callingUid)) {
1253 if (!isSystemNotification) {
1254 score = JUNK_SCORE;
1255 Slog.e(TAG, "Suppressing notification from package " + pkg + " by user request.");
1256 }
Daniel Sandler0da673f2012-04-11 12:33:16 -04001257 }
1258
1259 if (DBG) {
1260 Slog.v(TAG, "Assigned score=" + score + " to " + notification);
1261 }
1262
1263 if (score < SCORE_DISPLAY_THRESHOLD) {
1264 // Notification will be blocked because the score is too low.
1265 return;
Daniel Sandler2561b0b2012-02-13 21:04:12 -05001266 }
1267
Daniel Sandler526fa0e2012-12-04 14:51:50 -05001268 // Should this notification make noise, vibe, or use the LED?
1269 final boolean canInterrupt = (score >= SCORE_INTERRUPTION_THRESHOLD);
1270
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001271 synchronized (mNotificationList) {
Daniel Sandlerfde19b12013-01-17 00:21:05 -05001272 final StatusBarNotification n = new StatusBarNotification(
1273 pkg, id, tag, callingUid, callingPid, score, notification, user);
1274 NotificationRecord r = new NotificationRecord(n);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001275 NotificationRecord old = null;
1276
Dianne Hackborn41203752012-08-31 14:05:51 -07001277 int index = indexOfNotificationLocked(pkg, tag, id, userId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001278 if (index < 0) {
1279 mNotificationList.add(r);
1280 } else {
1281 old = mNotificationList.remove(index);
1282 mNotificationList.add(index, r);
Dianne Hackbornd8a43f62009-08-17 23:33:56 -07001283 // Make sure we don't lose the foreground service state.
1284 if (old != null) {
1285 notification.flags |=
Daniel Sandlerfde19b12013-01-17 00:21:05 -05001286 old.getNotification().flags&Notification.FLAG_FOREGROUND_SERVICE;
Dianne Hackbornd8a43f62009-08-17 23:33:56 -07001287 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001288 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001289
Dianne Hackbornd8a43f62009-08-17 23:33:56 -07001290 // Ensure if this is a foreground service that the proper additional
1291 // flags are set.
1292 if ((notification.flags&Notification.FLAG_FOREGROUND_SERVICE) != 0) {
1293 notification.flags |= Notification.FLAG_ONGOING_EVENT
1294 | Notification.FLAG_NO_CLEAR;
1295 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001296
Svetoslav Ganovc31ed392012-10-10 14:58:28 -07001297 final int currentUser;
1298 final long token = Binder.clearCallingIdentity();
1299 try {
1300 currentUser = ActivityManager.getCurrentUser();
1301 } finally {
1302 Binder.restoreCallingIdentity(token);
1303 }
1304
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001305 if (notification.icon != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001306 if (old != null && old.statusBarKey != null) {
1307 r.statusBarKey = old.statusBarKey;
1308 long identity = Binder.clearCallingIdentity();
1309 try {
Joe Onorato18e69df2010-05-17 22:26:12 -07001310 mStatusBar.updateNotification(r.statusBarKey, n);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001311 }
1312 finally {
1313 Binder.restoreCallingIdentity(identity);
1314 }
1315 } else {
1316 long identity = Binder.clearCallingIdentity();
1317 try {
Joe Onorato18e69df2010-05-17 22:26:12 -07001318 r.statusBarKey = mStatusBar.addNotification(n);
Daniel Sandler526fa0e2012-12-04 14:51:50 -05001319 if ((n.notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0
1320 && canInterrupt) {
Mike Lockwoodece18ef2012-02-13 20:42:19 -08001321 mAttentionLight.pulse();
1322 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001323 }
1324 finally {
1325 Binder.restoreCallingIdentity(identity);
1326 }
1327 }
Svetoslav Ganovc31ed392012-10-10 14:58:28 -07001328 // Send accessibility events only for the current user.
1329 if (currentUser == userId) {
1330 sendAccessibilityEvent(notification, pkg);
1331 }
Daniel Sandlerfde19b12013-01-17 00:21:05 -05001332
Daniel Sandler09a247e2013-02-14 10:24:17 -05001333 notifyPostedLocked(r);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001334 } else {
Daniel Sandlere40451a2011-02-03 14:51:35 -05001335 Slog.e(TAG, "Ignoring notification with icon==0: " + notification);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001336 if (old != null && old.statusBarKey != null) {
1337 long identity = Binder.clearCallingIdentity();
1338 try {
Joe Onorato0cbda992010-05-02 16:28:15 -07001339 mStatusBar.removeNotification(old.statusBarKey);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001340 }
1341 finally {
1342 Binder.restoreCallingIdentity(identity);
1343 }
Daniel Sandler09a247e2013-02-14 10:24:17 -05001344
1345 notifyRemovedLocked(r);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001346 }
Daniel Sandlerfde19b12013-01-17 00:21:05 -05001347 return; // do not play sounds, show lights, etc. for invalid notifications
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001348 }
1349
1350 // If we're not supposed to beep, vibrate, etc. then don't.
1351 if (((mDisabledNotifications & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) == 0)
1352 && (!(old != null
Joe Onorato30275482009-07-08 17:09:14 -07001353 && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 ))
Daniel Sandlerfde19b12013-01-17 00:21:05 -05001354 && (r.getUserId() == UserHandle.USER_ALL ||
1355 (r.getUserId() == userId && r.getUserId() == currentUser))
Daniel Sandler526fa0e2012-12-04 14:51:50 -05001356 && canInterrupt
Joe Onorato30275482009-07-08 17:09:14 -07001357 && mSystemReady) {
Eric Laurent524dc042009-11-27 05:07:55 -08001358
1359 final AudioManager audioManager = (AudioManager) mContext
1360 .getSystemService(Context.AUDIO_SERVICE);
Daniel Sandlerd4d2de22012-11-14 11:25:46 -08001361
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001362 // sound
1363 final boolean useDefaultSound =
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001364 (notification.defaults & Notification.DEFAULT_SOUND) != 0;
Daniel Sandlerd4d2de22012-11-14 11:25:46 -08001365
1366 Uri soundUri = null;
1367 boolean hasValidSound = false;
1368
1369 if (useDefaultSound) {
1370 soundUri = Settings.System.DEFAULT_NOTIFICATION_URI;
1371
1372 // check to see if the default notification sound is silent
1373 ContentResolver resolver = mContext.getContentResolver();
1374 hasValidSound = Settings.System.getString(resolver,
1375 Settings.System.NOTIFICATION_SOUND) != null;
1376 } else if (notification.sound != null) {
1377 soundUri = notification.sound;
1378 hasValidSound = (soundUri != null);
1379 }
1380
1381 if (hasValidSound) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001382 boolean looping = (notification.flags & Notification.FLAG_INSISTENT) != 0;
1383 int audioStreamType;
1384 if (notification.audioStreamType >= 0) {
1385 audioStreamType = notification.audioStreamType;
1386 } else {
1387 audioStreamType = DEFAULT_STREAM_TYPE;
1388 }
1389 mSoundNotification = r;
Eric Laurent524dc042009-11-27 05:07:55 -08001390 // do not play notifications if stream volume is 0
Jean-Michel Trivid6770542012-10-10 12:03:41 -07001391 // (typically because ringer mode is silent) or if speech recognition is active.
1392 if ((audioManager.getStreamVolume(audioStreamType) != 0)
1393 && !audioManager.isSpeechRecognitionActive()) {
Jeff Sharkey098d5802012-04-26 17:30:34 -07001394 final long identity = Binder.clearCallingIdentity();
Eric Laurent524dc042009-11-27 05:07:55 -08001395 try {
Jeff Sharkey098d5802012-04-26 17:30:34 -07001396 final IRingtonePlayer player = mAudioService.getRingtonePlayer();
1397 if (player != null) {
Daniel Sandlerd4d2de22012-11-14 11:25:46 -08001398 player.playAsync(soundUri, user, looping, audioStreamType);
Jeff Sharkey098d5802012-04-26 17:30:34 -07001399 }
1400 } catch (RemoteException e) {
1401 } finally {
Eric Laurent524dc042009-11-27 05:07:55 -08001402 Binder.restoreCallingIdentity(identity);
1403 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001404 }
1405 }
1406
1407 // vibrate
Daniel Sandleredbb3802012-11-13 20:49:47 -08001408 // Does the notification want to specify its own vibration?
1409 final boolean hasCustomVibrate = notification.vibrate != null;
1410
David Agnew71789e12012-11-09 23:03:26 -05001411 // 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 -05001412 // and no other vibration is specified, we fall back to vibration
David Agnew71789e12012-11-09 23:03:26 -05001413 final boolean convertSoundToVibration =
Daniel Sandleredbb3802012-11-13 20:49:47 -08001414 !hasCustomVibrate
Daniel Sandlerd4d2de22012-11-14 11:25:46 -08001415 && hasValidSound
David Agnew71789e12012-11-09 23:03:26 -05001416 && (audioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE);
1417
Daniel Sandler4a7a9b92012-11-20 12:59:41 -05001418 // The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001419 final boolean useDefaultVibrate =
Daniel Sandleredbb3802012-11-13 20:49:47 -08001420 (notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
David Agnew71789e12012-11-09 23:03:26 -05001421
Daniel Sandleredbb3802012-11-13 20:49:47 -08001422 if ((useDefaultVibrate || convertSoundToVibration || hasCustomVibrate)
Eric Laurentbffc3d12012-05-07 17:43:49 -07001423 && !(audioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001424 mVibrateNotification = r;
1425
Daniel Sandleredbb3802012-11-13 20:49:47 -08001426 if (useDefaultVibrate || convertSoundToVibration) {
1427 // Escalate privileges so we can use the vibrator even if the notifying app
1428 // does not have the VIBRATE permission.
1429 long identity = Binder.clearCallingIdentity();
1430 try {
Daniel Sandlerfde19b12013-01-17 00:21:05 -05001431 mVibrator.vibrate(r.sbn.uid, r.sbn.basePkg,
Dianne Hackbornf265ea92013-01-31 15:00:51 -08001432 useDefaultVibrate ? mDefaultVibrationPattern
1433 : mFallbackVibrationPattern,
Daniel Sandleredbb3802012-11-13 20:49:47 -08001434 ((notification.flags & Notification.FLAG_INSISTENT) != 0) ? 0: -1);
1435 } finally {
1436 Binder.restoreCallingIdentity(identity);
1437 }
1438 } else if (notification.vibrate.length > 1) {
1439 // If you want your own vibration pattern, you need the VIBRATE permission
Daniel Sandlerfde19b12013-01-17 00:21:05 -05001440 mVibrator.vibrate(r.sbn.uid, r.sbn.basePkg, notification.vibrate,
Daniel Sandleredbb3802012-11-13 20:49:47 -08001441 ((notification.flags & Notification.FLAG_INSISTENT) != 0) ? 0: -1);
1442 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001443 }
1444 }
1445
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001446 // light
1447 // the most recent thing gets the light
1448 mLights.remove(old);
1449 if (mLedNotification == old) {
1450 mLedNotification = null;
1451 }
Joe Onorato8a9b2202010-02-26 18:56:32 -08001452 //Slog.i(TAG, "notification.lights="
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001453 // + ((old.notification.lights.flags & Notification.FLAG_SHOW_LIGHTS) != 0));
Daniel Sandler526fa0e2012-12-04 14:51:50 -05001454 if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0
1455 && canInterrupt) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001456 mLights.add(r);
1457 updateLightsLocked();
1458 } else {
1459 if (old != null
Daniel Sandlerfde19b12013-01-17 00:21:05 -05001460 && ((old.getFlags() & Notification.FLAG_SHOW_LIGHTS) != 0)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001461 updateLightsLocked();
1462 }
1463 }
1464 }
1465
1466 idOut[0] = id;
1467 }
1468
Joe Onorato30275482009-07-08 17:09:14 -07001469 private void sendAccessibilityEvent(Notification notification, CharSequence packageName) {
svetoslavganov75986cf2009-05-14 22:28:01 -07001470 AccessibilityManager manager = AccessibilityManager.getInstance(mContext);
1471 if (!manager.isEnabled()) {
1472 return;
1473 }
1474
1475 AccessibilityEvent event =
1476 AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
1477 event.setPackageName(packageName);
1478 event.setClassName(Notification.class.getName());
1479 event.setParcelableData(notification);
1480 CharSequence tickerText = notification.tickerText;
1481 if (!TextUtils.isEmpty(tickerText)) {
1482 event.getText().add(tickerText);
1483 }
1484
1485 manager.sendAccessibilityEvent(event);
1486 }
1487
Joe Onorato46439ce2010-11-19 13:56:21 -08001488 private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete) {
1489 // tell the app
1490 if (sendDelete) {
Daniel Sandlerfde19b12013-01-17 00:21:05 -05001491 if (r.getNotification().deleteIntent != null) {
Joe Onorato46439ce2010-11-19 13:56:21 -08001492 try {
Daniel Sandlerfde19b12013-01-17 00:21:05 -05001493 r.getNotification().deleteIntent.send();
Joe Onorato46439ce2010-11-19 13:56:21 -08001494 } catch (PendingIntent.CanceledException ex) {
1495 // do nothing - there's no relevant way to recover, and
1496 // no reason to let this propagate
Daniel Sandlerfde19b12013-01-17 00:21:05 -05001497 Slog.w(TAG, "canceled PendingIntent for " + r.sbn.pkg, ex);
Joe Onorato46439ce2010-11-19 13:56:21 -08001498 }
1499 }
1500 }
1501
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001502 // status bar
Daniel Sandlerfde19b12013-01-17 00:21:05 -05001503 if (r.getNotification().icon != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001504 long identity = Binder.clearCallingIdentity();
1505 try {
Joe Onorato0cbda992010-05-02 16:28:15 -07001506 mStatusBar.removeNotification(r.statusBarKey);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001507 }
1508 finally {
1509 Binder.restoreCallingIdentity(identity);
1510 }
1511 r.statusBarKey = null;
Daniel Sandler09a247e2013-02-14 10:24:17 -05001512 notifyRemovedLocked(r);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001513 }
1514
1515 // sound
1516 if (mSoundNotification == r) {
1517 mSoundNotification = null;
Jeff Sharkey098d5802012-04-26 17:30:34 -07001518 final long identity = Binder.clearCallingIdentity();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001519 try {
Jeff Sharkey098d5802012-04-26 17:30:34 -07001520 final IRingtonePlayer player = mAudioService.getRingtonePlayer();
1521 if (player != null) {
1522 player.stopAsync();
1523 }
1524 } catch (RemoteException e) {
1525 } finally {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001526 Binder.restoreCallingIdentity(identity);
1527 }
1528 }
1529
1530 // vibrate
1531 if (mVibrateNotification == r) {
1532 mVibrateNotification = null;
1533 long identity = Binder.clearCallingIdentity();
1534 try {
1535 mVibrator.cancel();
1536 }
1537 finally {
1538 Binder.restoreCallingIdentity(identity);
1539 }
1540 }
1541
1542 // light
1543 mLights.remove(r);
1544 if (mLedNotification == r) {
1545 mLedNotification = null;
1546 }
Daniel Sandler23d7c702013-03-07 16:32:06 -05001547
1548 // Save it for users of getHistoricalNotifications()
1549 mArchive.record(r.sbn);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001550 }
1551
1552 /**
Dianne Hackbornd8a43f62009-08-17 23:33:56 -07001553 * Cancels a notification ONLY if it has all of the {@code mustHaveFlags}
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001554 * and none of the {@code mustNotHaveFlags}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001555 */
Fred Quintana6ecaff12009-09-25 14:23:13 -07001556 private void cancelNotification(String pkg, String tag, int id, int mustHaveFlags,
Dianne Hackborn41203752012-08-31 14:05:51 -07001557 int mustNotHaveFlags, boolean sendDelete, int userId) {
Daniel Sandler321e9c52012-10-12 10:59:26 -07001558 EventLog.writeEvent(EventLogTags.NOTIFICATION_CANCEL, pkg, id, tag, userId,
Daniel Sandlerb64cb882011-11-29 23:48:29 -05001559 mustHaveFlags, mustNotHaveFlags);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001560
1561 synchronized (mNotificationList) {
Dianne Hackborn41203752012-08-31 14:05:51 -07001562 int index = indexOfNotificationLocked(pkg, tag, id, userId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001563 if (index >= 0) {
Fred Quintana6ecaff12009-09-25 14:23:13 -07001564 NotificationRecord r = mNotificationList.get(index);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001565
Daniel Sandlerfde19b12013-01-17 00:21:05 -05001566 if ((r.getNotification().flags & mustHaveFlags) != mustHaveFlags) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001567 return;
1568 }
Daniel Sandlerfde19b12013-01-17 00:21:05 -05001569 if ((r.getNotification().flags & mustNotHaveFlags) != 0) {
Dianne Hackbornd8a43f62009-08-17 23:33:56 -07001570 return;
1571 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001572
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001573 mNotificationList.remove(index);
1574
Joe Onorato46439ce2010-11-19 13:56:21 -08001575 cancelNotificationLocked(r, sendDelete);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001576 updateLightsLocked();
1577 }
1578 }
1579 }
1580
1581 /**
Daniel Sandler321e9c52012-10-12 10:59:26 -07001582 * Determine whether the userId applies to the notification in question, either because
1583 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard).
1584 */
1585 private boolean notificationMatchesUserId(NotificationRecord r, int userId) {
1586 return
1587 // looking for USER_ALL notifications? match everything
1588 userId == UserHandle.USER_ALL
1589 // a notification sent to USER_ALL matches any query
Daniel Sandlerfde19b12013-01-17 00:21:05 -05001590 || r.getUserId() == UserHandle.USER_ALL
Daniel Sandler321e9c52012-10-12 10:59:26 -07001591 // an exact user match
Daniel Sandlerfde19b12013-01-17 00:21:05 -05001592 || r.getUserId() == userId;
Daniel Sandler321e9c52012-10-12 10:59:26 -07001593 }
1594
1595 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001596 * Cancels all notifications from a given package that have all of the
1597 * {@code mustHaveFlags}.
1598 */
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08001599 boolean cancelAllNotificationsInt(String pkg, int mustHaveFlags,
Dianne Hackborn41203752012-08-31 14:05:51 -07001600 int mustNotHaveFlags, boolean doit, int userId) {
Daniel Sandler321e9c52012-10-12 10:59:26 -07001601 EventLog.writeEvent(EventLogTags.NOTIFICATION_CANCEL_ALL, pkg, userId,
1602 mustHaveFlags, mustNotHaveFlags);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001603
1604 synchronized (mNotificationList) {
1605 final int N = mNotificationList.size();
1606 boolean canceledSomething = false;
1607 for (int i = N-1; i >= 0; --i) {
1608 NotificationRecord r = mNotificationList.get(i);
Daniel Sandler321e9c52012-10-12 10:59:26 -07001609 if (!notificationMatchesUserId(r, userId)) {
Dianne Hackborn41203752012-08-31 14:05:51 -07001610 continue;
1611 }
Amith Yamasani5ec00e92012-11-07 16:58:30 -08001612 // Don't remove notifications to all, if there's no package name specified
Daniel Sandlerfde19b12013-01-17 00:21:05 -05001613 if (r.getUserId() == UserHandle.USER_ALL && pkg == null) {
Amith Yamasani5ec00e92012-11-07 16:58:30 -08001614 continue;
1615 }
Daniel Sandlerfde19b12013-01-17 00:21:05 -05001616 if ((r.getFlags() & mustHaveFlags) != mustHaveFlags) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001617 continue;
1618 }
Daniel Sandlerfde19b12013-01-17 00:21:05 -05001619 if ((r.getFlags() & mustNotHaveFlags) != 0) {
Dianne Hackbornd8a43f62009-08-17 23:33:56 -07001620 continue;
1621 }
Daniel Sandlerfde19b12013-01-17 00:21:05 -05001622 if (pkg != null && !r.sbn.pkg.equals(pkg)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001623 continue;
1624 }
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08001625 canceledSomething = true;
1626 if (!doit) {
1627 return true;
1628 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001629 mNotificationList.remove(i);
Joe Onorato46439ce2010-11-19 13:56:21 -08001630 cancelNotificationLocked(r, false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001631 }
1632 if (canceledSomething) {
1633 updateLightsLocked();
1634 }
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08001635 return canceledSomething;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001636 }
1637 }
1638
Dianne Hackborn41203752012-08-31 14:05:51 -07001639 public void cancelNotificationWithTag(String pkg, String tag, int id, int userId) {
Daniel Sandler0da673f2012-04-11 12:33:16 -04001640 checkCallerIsSystemOrSameApp(pkg);
Dianne Hackborn41203752012-08-31 14:05:51 -07001641 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
Amith Yamasani2c7ebea2012-10-30 15:28:27 -07001642 Binder.getCallingUid(), userId, true, false, "cancelNotificationWithTag", pkg);
Dianne Hackbornd8a43f62009-08-17 23:33:56 -07001643 // Don't allow client applications to cancel foreground service notis.
Fred Quintana6ecaff12009-09-25 14:23:13 -07001644 cancelNotification(pkg, tag, id, 0,
Dianne Hackbornd8a43f62009-08-17 23:33:56 -07001645 Binder.getCallingUid() == Process.SYSTEM_UID
Dianne Hackborn41203752012-08-31 14:05:51 -07001646 ? 0 : Notification.FLAG_FOREGROUND_SERVICE, false, userId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001647 }
1648
Dianne Hackborn41203752012-08-31 14:05:51 -07001649 public void cancelAllNotifications(String pkg, int userId) {
Daniel Sandler0da673f2012-04-11 12:33:16 -04001650 checkCallerIsSystemOrSameApp(pkg);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001651
Dianne Hackborn41203752012-08-31 14:05:51 -07001652 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
Amith Yamasani2c7ebea2012-10-30 15:28:27 -07001653 Binder.getCallingUid(), userId, true, false, "cancelAllNotifications", pkg);
Dianne Hackborn41203752012-08-31 14:05:51 -07001654
Dianne Hackbornd8a43f62009-08-17 23:33:56 -07001655 // Calling from user space, don't allow the canceling of actively
1656 // running foreground services.
Dianne Hackborn41203752012-08-31 14:05:51 -07001657 cancelAllNotificationsInt(pkg, 0, Notification.FLAG_FOREGROUND_SERVICE, true, userId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001658 }
1659
Daniel Sandler0da673f2012-04-11 12:33:16 -04001660 void checkCallerIsSystem() {
1661 int uid = Binder.getCallingUid();
Dianne Hackborn0c380492012-08-20 17:23:30 -07001662 if (UserHandle.getAppId(uid) == Process.SYSTEM_UID || uid == 0) {
Daniel Sandler0da673f2012-04-11 12:33:16 -04001663 return;
1664 }
1665 throw new SecurityException("Disallowed call for uid " + uid);
1666 }
1667
1668 void checkCallerIsSystemOrSameApp(String pkg) {
Dianne Hackbornd8a43f62009-08-17 23:33:56 -07001669 int uid = Binder.getCallingUid();
Dianne Hackborn0c380492012-08-20 17:23:30 -07001670 if (UserHandle.getAppId(uid) == Process.SYSTEM_UID || uid == 0) {
Dianne Hackbornd8a43f62009-08-17 23:33:56 -07001671 return;
1672 }
1673 try {
Amith Yamasanif203aee2012-08-29 18:41:53 -07001674 ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo(
1675 pkg, 0, UserHandle.getCallingUserId());
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07001676 if (!UserHandle.isSameApp(ai.uid, uid)) {
Dianne Hackbornd8a43f62009-08-17 23:33:56 -07001677 throw new SecurityException("Calling uid " + uid + " gave package"
1678 + pkg + " which is owned by uid " + ai.uid);
1679 }
Amith Yamasanif203aee2012-08-29 18:41:53 -07001680 } catch (RemoteException re) {
1681 throw new SecurityException("Unknown package " + pkg + "\n" + re);
Dianne Hackbornd8a43f62009-08-17 23:33:56 -07001682 }
1683 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001684
Dianne Hackborn41203752012-08-31 14:05:51 -07001685 void cancelAll(int userId) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001686 synchronized (mNotificationList) {
1687 final int N = mNotificationList.size();
1688 for (int i=N-1; i>=0; i--) {
1689 NotificationRecord r = mNotificationList.get(i);
1690
Daniel Sandler321e9c52012-10-12 10:59:26 -07001691 if (!notificationMatchesUserId(r, userId)) {
Dianne Hackborn41203752012-08-31 14:05:51 -07001692 continue;
1693 }
1694
Daniel Sandlerfde19b12013-01-17 00:21:05 -05001695 if ((r.getFlags() & (Notification.FLAG_ONGOING_EVENT
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001696 | Notification.FLAG_NO_CLEAR)) == 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001697 mNotificationList.remove(i);
Joe Onorato46439ce2010-11-19 13:56:21 -08001698 cancelNotificationLocked(r, true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001699 }
1700 }
1701
1702 updateLightsLocked();
1703 }
1704 }
1705
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001706 // lock on mNotificationList
1707 private void updateLightsLocked()
1708 {
The Android Open Source Project10592532009-03-18 17:39:46 -07001709 // handle notification lights
1710 if (mLedNotification == null) {
1711 // get next notification, if any
1712 int n = mLights.size();
1713 if (n > 0) {
1714 mLedNotification = mLights.get(n-1);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001715 }
1716 }
Mike Lockwoodc22404a2009-12-02 11:15:02 -05001717
Mike Lockwood63b5ad92011-08-30 09:55:30 -04001718 // Don't flash while we are in a call or screen is on
1719 if (mLedNotification == null || mInCall || mScreenOn) {
Mike Lockwood3cb67a32009-11-27 14:25:58 -05001720 mNotificationLight.turnOff();
The Android Open Source Project10592532009-03-18 17:39:46 -07001721 } else {
Daniel Sandlerfde19b12013-01-17 00:21:05 -05001722 final Notification ledno = mLedNotification.sbn.notification;
1723 int ledARGB = ledno.ledARGB;
1724 int ledOnMS = ledno.ledOnMS;
1725 int ledOffMS = ledno.ledOffMS;
1726 if ((ledno.defaults & Notification.DEFAULT_LIGHTS) != 0) {
Mike Lockwood670f9322010-01-20 12:13:36 -05001727 ledARGB = mDefaultNotificationColor;
1728 ledOnMS = mDefaultNotificationLedOn;
1729 ledOffMS = mDefaultNotificationLedOff;
1730 }
1731 if (mNotificationPulseEnabled) {
1732 // pulse repeatedly
1733 mNotificationLight.setFlashing(ledARGB, LightsService.LIGHT_FLASH_TIMED,
1734 ledOnMS, ledOffMS);
Mike Lockwood670f9322010-01-20 12:13:36 -05001735 }
The Android Open Source Project10592532009-03-18 17:39:46 -07001736 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001737 }
1738
1739 // lock on mNotificationList
Dianne Hackborn41203752012-08-31 14:05:51 -07001740 private int indexOfNotificationLocked(String pkg, String tag, int id, int userId)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001741 {
1742 ArrayList<NotificationRecord> list = mNotificationList;
1743 final int len = list.size();
1744 for (int i=0; i<len; i++) {
1745 NotificationRecord r = list.get(i);
Daniel Sandlerfde19b12013-01-17 00:21:05 -05001746 if (!notificationMatchesUserId(r, userId) || r.sbn.id != id) {
Dianne Hackborn41203752012-08-31 14:05:51 -07001747 continue;
1748 }
Fred Quintana6ecaff12009-09-25 14:23:13 -07001749 if (tag == null) {
Daniel Sandlerfde19b12013-01-17 00:21:05 -05001750 if (r.sbn.tag != null) {
Fred Quintana6ecaff12009-09-25 14:23:13 -07001751 continue;
1752 }
1753 } else {
Daniel Sandlerfde19b12013-01-17 00:21:05 -05001754 if (!tag.equals(r.sbn.tag)) {
Fred Quintana6ecaff12009-09-25 14:23:13 -07001755 continue;
1756 }
1757 }
Daniel Sandlerfde19b12013-01-17 00:21:05 -05001758 if (r.sbn.pkg.equals(pkg)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001759 return i;
1760 }
1761 }
1762 return -1;
1763 }
1764
Mike Lockwoodc22404a2009-12-02 11:15:02 -05001765 private void updateNotificationPulse() {
1766 synchronized (mNotificationList) {
1767 updateLightsLocked();
1768 }
1769 }
1770
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001771 // ======================================================================
1772 @Override
1773 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1774 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
1775 != PackageManager.PERMISSION_GRANTED) {
1776 pw.println("Permission Denial: can't dump NotificationManager from from pid="
1777 + Binder.getCallingPid()
1778 + ", uid=" + Binder.getCallingUid());
1779 return;
1780 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001781
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001782 pw.println("Current Notification Manager state:");
1783
Daniel Sandler4b749ef2013-03-18 21:53:04 -04001784 pw.print(" Enabled listeners: [");
1785 for (String pkg : mEnabledListenersForCurrentUser) {
1786 pw.print(" " + pkg);
1787 }
1788 pw.println(" ]");
1789
1790 pw.println(" Live listeners:");
1791 for (NotificationListenerInfo info : mListeners) {
1792 pw.println(" " + info.pkg + " (user " + info.userid + "): " + info.listener
1793 + (info.isSystem?" SYSTEM":""));
1794 }
1795
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001796 int N;
1797
1798 synchronized (mToastQueue) {
1799 N = mToastQueue.size();
1800 if (N > 0) {
1801 pw.println(" Toast Queue:");
1802 for (int i=0; i<N; i++) {
1803 mToastQueue.get(i).dump(pw, " ");
1804 }
1805 pw.println(" ");
1806 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001807
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001808 }
1809
1810 synchronized (mNotificationList) {
1811 N = mNotificationList.size();
1812 if (N > 0) {
1813 pw.println(" Notification List:");
1814 for (int i=0; i<N; i++) {
1815 mNotificationList.get(i).dump(pw, " ", mContext);
1816 }
1817 pw.println(" ");
1818 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001819
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001820 N = mLights.size();
1821 if (N > 0) {
1822 pw.println(" Lights List:");
1823 for (int i=0; i<N; i++) {
1824 mLights.get(i).dump(pw, " ", mContext);
1825 }
1826 pw.println(" ");
1827 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001828
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001829 pw.println(" mSoundNotification=" + mSoundNotification);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001830 pw.println(" mVibrateNotification=" + mVibrateNotification);
Joe Onorato39f5b6a2009-07-23 12:29:19 -04001831 pw.println(" mDisabledNotifications=0x" + Integer.toHexString(mDisabledNotifications));
1832 pw.println(" mSystemReady=" + mSystemReady);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001833 }
1834 }
1835}