blob: 5f096cb2ae278080eae867c054f5df85ed23166c [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
Adam Lesinski182f73f2013-12-05 16:48:06 -080017package com.android.server.notification;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080018
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;
Daniel Sandler4a900ac2013-01-30 14:04:10 -050025import android.app.AppOpsManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080026import android.app.IActivityManager;
27import android.app.INotificationManager;
28import android.app.ITransientNotification;
29import android.app.Notification;
30import android.app.PendingIntent;
31import android.app.StatusBarManager;
32import android.content.BroadcastReceiver;
Daniel Sandler5feceeb2013-03-22 18:29:23 -070033import android.content.ComponentName;
Dianne Hackborn1dac2772009-06-26 18:16:48 -070034import android.content.ContentResolver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080035import android.content.Context;
36import android.content.Intent;
37import android.content.IntentFilter;
Daniel Sandler4a900ac2013-01-30 14:04:10 -050038import android.content.pm.PackageInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080039import android.content.pm.PackageManager;
40import android.content.pm.PackageManager.NameNotFoundException;
Kenny Guy3a7c4a52014-03-03 18:24:03 +000041import android.content.pm.UserInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080042import android.content.res.Resources;
Dianne Hackborn1dac2772009-06-26 18:16:48 -070043import android.database.ContentObserver;
Daniel Sandlerf45564e2013-04-15 15:05:08 -040044import android.graphics.Bitmap;
svetoslavganov75986cf2009-05-14 22:28:01 -070045import android.media.AudioManager;
Jeff Sharkey098d5802012-04-26 17:30:34 -070046import android.media.IRingtonePlayer;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080047import android.net.Uri;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080048import android.os.Binder;
John Spurlock056c5192014-04-20 21:52:01 -040049import android.os.Environment;
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;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -070055import android.os.UserHandle;
Kenny Guy3a7c4a52014-03-03 18:24:03 +000056import android.os.UserManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080057import android.os.Vibrator;
58import android.provider.Settings;
Daniel Sandler5feceeb2013-03-22 18:29:23 -070059import android.service.notification.INotificationListener;
Daniel Sandler5feceeb2013-03-22 18:29:23 -070060import android.service.notification.StatusBarNotification;
John Spurlock056c5192014-04-20 21:52:01 -040061import android.service.notification.ZenModeConfig;
Daniel Sandlere96ffb12010-03-11 13:38:06 -050062import android.telephony.TelephonyManager;
svetoslavganov75986cf2009-05-14 22:28:01 -070063import android.text.TextUtils;
John Spurlocka4294292014-03-24 18:02:32 -040064import android.util.ArrayMap;
Dianne Hackborn39606a02012-07-31 17:54:35 -070065import android.util.AtomicFile;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080066import android.util.Log;
Andy Stadler110988c2010-12-03 14:29:16 -080067import android.util.Slog;
Kenny Guy3a7c4a52014-03-03 18:24:03 +000068import android.util.SparseArray;
Daniel Sandler0da673f2012-04-11 12:33:16 -040069import android.util.Xml;
svetoslavganov75986cf2009-05-14 22:28:01 -070070import android.view.accessibility.AccessibilityEvent;
71import android.view.accessibility.AccessibilityManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080072import android.widget.Toast;
73
Scott Greenwald9a05b312013-06-28 00:37:54 -040074import com.android.internal.R;
Scott Greenwald9a05b312013-06-28 00:37:54 -040075import com.android.internal.notification.NotificationScorer;
John Spurlock056c5192014-04-20 21:52:01 -040076import com.android.internal.util.FastXmlSerializer;
Adam Lesinski182f73f2013-12-05 16:48:06 -080077import com.android.server.EventLogTags;
Adam Lesinski182f73f2013-12-05 16:48:06 -080078import com.android.server.SystemService;
79import com.android.server.lights.Light;
80import com.android.server.lights.LightsManager;
John Spurlockb408e8e2014-04-23 21:12:45 -040081import com.android.server.notification.NotificationListeners.NotificationListenerInfo;
82import com.android.server.notification.NotificationUsageStats.SingleNotificationStats;
83import com.android.server.statusbar.StatusBarManagerInternal;
84
85import libcore.io.IoUtils;
Adam Lesinski182f73f2013-12-05 16:48:06 -080086
Jeff Sharkey098d5802012-04-26 17:30:34 -070087import org.xmlpull.v1.XmlPullParser;
88import org.xmlpull.v1.XmlPullParserException;
John Spurlock056c5192014-04-20 21:52:01 -040089import org.xmlpull.v1.XmlSerializer;
Jeff Sharkey098d5802012-04-26 17:30:34 -070090
Daniel Sandler0da673f2012-04-11 12:33:16 -040091import java.io.File;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080092import java.io.FileDescriptor;
Daniel Sandler0da673f2012-04-11 12:33:16 -040093import java.io.FileInputStream;
94import java.io.FileNotFoundException;
John Spurlock056c5192014-04-20 21:52:01 -040095import java.io.FileOutputStream;
Daniel Sandler0da673f2012-04-11 12:33:16 -040096import java.io.IOException;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080097import java.io.PrintWriter;
Daniel Sandlerf45564e2013-04-15 15:05:08 -040098import java.lang.reflect.Array;
Daniel Sandlerfde19b12013-01-17 00:21:05 -050099import java.util.ArrayDeque;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800100import java.util.ArrayList;
101import java.util.Arrays;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400102import java.util.HashSet;
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500103import java.util.Iterator;
Chris Wrenae9bb572013-05-15 14:50:28 -0400104import java.util.List;
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500105import java.util.NoSuchElementException;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400106
Daniel Sandlerd0a2f862010-08-03 15:29:31 -0400107/** {@hide} */
Adam Lesinski182f73f2013-12-05 16:48:06 -0800108public class NotificationManagerService extends SystemService {
109 static final String TAG = "NotificationService";
110 static final boolean DBG = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800111
Adam Lesinski182f73f2013-12-05 16:48:06 -0800112 static final int MAX_PACKAGE_NOTIFICATIONS = 50;
Joe Onoratobd73d012010-06-04 11:44:54 -0700113
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800114 // message codes
Adam Lesinski182f73f2013-12-05 16:48:06 -0800115 static final int MESSAGE_TIMEOUT = 2;
John Spurlock056c5192014-04-20 21:52:01 -0400116 static final int MESSAGE_SAVE_POLICY_FILE = 3;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800117
Adam Lesinski182f73f2013-12-05 16:48:06 -0800118 static final int LONG_DELAY = 3500; // 3.5 seconds
119 static final int SHORT_DELAY = 2000; // 2 seconds
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800120
Adam Lesinski182f73f2013-12-05 16:48:06 -0800121 static final long[] DEFAULT_VIBRATE_PATTERN = {0, 250, 250, 250};
122 static final int VIBRATE_PATTERN_MAXLEN = 8 * 2 + 1; // up to eight bumps
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800123
Adam Lesinski182f73f2013-12-05 16:48:06 -0800124 static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_NOTIFICATION;
125 static final boolean SCORE_ONGOING_HIGHER = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800126
Adam Lesinski182f73f2013-12-05 16:48:06 -0800127 static final int JUNK_SCORE = -1000;
128 static final int NOTIFICATION_PRIORITY_MULTIPLIER = 10;
129 static final int SCORE_DISPLAY_THRESHOLD = Notification.PRIORITY_MIN * NOTIFICATION_PRIORITY_MULTIPLIER;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400130
Daniel Sandler526fa0e2012-12-04 14:51:50 -0500131 // Notifications with scores below this will not interrupt the user, either via LED or
132 // sound or vibration
Adam Lesinski182f73f2013-12-05 16:48:06 -0800133 static final int SCORE_INTERRUPTION_THRESHOLD =
Daniel Sandler526fa0e2012-12-04 14:51:50 -0500134 Notification.PRIORITY_LOW * NOTIFICATION_PRIORITY_MULTIPLIER;
135
Adam Lesinski182f73f2013-12-05 16:48:06 -0800136 static final boolean ENABLE_BLOCKED_NOTIFICATIONS = true;
137 static final boolean ENABLE_BLOCKED_TOASTS = true;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400138
Adam Lesinski182f73f2013-12-05 16:48:06 -0800139 private IActivityManager mAm;
140 AudioManager mAudioManager;
141 StatusBarManagerInternal mStatusBar;
142 Vibrator mVibrator;
143
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800144 final IBinder mForegroundToken = new Binder();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800145 private WorkerHandler mHandler;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800146
Adam Lesinski182f73f2013-12-05 16:48:06 -0800147 private Light mNotificationLight;
148 Light mAttentionLight;
Mike Lockwood670f9322010-01-20 12:13:36 -0500149 private int mDefaultNotificationColor;
150 private int mDefaultNotificationLedOn;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800151
Mike Lockwood670f9322010-01-20 12:13:36 -0500152 private int mDefaultNotificationLedOff;
Daniel Sandleredbb3802012-11-13 20:49:47 -0800153 private long[] mDefaultVibrationPattern;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800154
Daniel Sandleredbb3802012-11-13 20:49:47 -0800155 private long[] mFallbackVibrationPattern;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800156 boolean mSystemReady;
Daniel Sandleredbb3802012-11-13 20:49:47 -0800157
John Spurlocke677d712014-02-13 12:52:19 -0500158 private boolean mDisableNotificationAlerts;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800159 NotificationRecord mSoundNotification;
160 NotificationRecord mVibrateNotification;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800161
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500162 // for enabling and disabling notification pulse behavior
Mike Lockwood63b5ad92011-08-30 09:55:30 -0400163 private boolean mScreenOn = true;
Daniel Sandlere96ffb12010-03-11 13:38:06 -0500164 private boolean mInCall = false;
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500165 private boolean mNotificationPulseEnabled;
166
Daniel Sandler09a247e2013-02-14 10:24:17 -0500167 // used as a mutex for access to all active notifications & listeners
Adam Lesinski182f73f2013-12-05 16:48:06 -0800168 final ArrayList<NotificationRecord> mNotificationList =
Fred Quintana6ecaff12009-09-25 14:23:13 -0700169 new ArrayList<NotificationRecord>();
John Spurlocka4294292014-03-24 18:02:32 -0400170 final ArrayMap<String, NotificationRecord> mNotificationsByKey =
171 new ArrayMap<String, NotificationRecord>();
Adam Lesinski182f73f2013-12-05 16:48:06 -0800172 final ArrayList<ToastRecord> mToastQueue = new ArrayList<ToastRecord>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800173
Adam Lesinski182f73f2013-12-05 16:48:06 -0800174 ArrayList<NotificationRecord> mLights = new ArrayList<NotificationRecord>();
175 NotificationRecord mLedNotification;
svetoslavganov75986cf2009-05-14 22:28:01 -0700176
Adam Lesinski182f73f2013-12-05 16:48:06 -0800177 private AppOpsManager mAppOps;
Daniel Sandler4a900ac2013-01-30 14:04:10 -0500178
Daniel Sandler0da673f2012-04-11 12:33:16 -0400179 // Notification control database. For now just contains disabled packages.
180 private AtomicFile mPolicyFile;
181 private HashSet<String> mBlockedPackages = new HashSet<String>();
182
183 private static final int DB_VERSION = 1;
184
185 private static final String TAG_BODY = "notification-policy";
186 private static final String ATTR_VERSION = "version";
187
188 private static final String TAG_BLOCKED_PKGS = "blocked-packages";
189 private static final String TAG_PACKAGE = "package";
190 private static final String ATTR_NAME = "name";
191
Adam Lesinski182f73f2013-12-05 16:48:06 -0800192 final ArrayList<NotificationScorer> mScorers = new ArrayList<NotificationScorer>();
Scott Greenwald9a05b312013-06-28 00:37:54 -0400193
John Spurlockb408e8e2014-04-23 21:12:45 -0400194 private NotificationListeners mListeners;
195 private final UserProfiles mUserProfiles = new UserProfiles();
196
Christoph Studer546bec82014-03-14 12:17:12 +0100197 private final NotificationUsageStats mUsageStats = new NotificationUsageStats();
198
John Spurlocke677d712014-02-13 12:52:19 -0500199 private static final String EXTRA_INTERCEPT = "android.intercept";
200
John Spurlocke6a7d932014-03-13 12:29:00 -0400201 private static final int MY_UID = Process.myUid();
202 private static final int MY_PID = Process.myPid();
203 private static final int REASON_DELEGATE_CLICK = 1;
204 private static final int REASON_DELEGATE_CANCEL = 2;
205 private static final int REASON_DELEGATE_CANCEL_ALL = 3;
206 private static final int REASON_DELEGATE_ERROR = 4;
207 private static final int REASON_PACKAGE_CHANGED = 5;
208 private static final int REASON_USER_STOPPED = 6;
209 private static final int REASON_PACKAGE_BANNED = 7;
210 private static final int REASON_NOMAN_CANCEL = 8;
211 private static final int REASON_NOMAN_CANCEL_ALL = 9;
212 private static final int REASON_LISTENER_CANCEL = 10;
213 private static final int REASON_LISTENER_CANCEL_ALL = 11;
214
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500215 private static class Archive {
Daniel Sandler5e62e3a2013-04-15 20:57:02 -0400216 static final int BUFFER_SIZE = 250;
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500217 ArrayDeque<StatusBarNotification> mBuffer = new ArrayDeque<StatusBarNotification>(BUFFER_SIZE);
218
219 public Archive() {
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500220 }
Jeff Sharkey0c1baf92013-04-03 13:08:52 -0700221
Daniel Sandler5e62e3a2013-04-15 20:57:02 -0400222 public String toString() {
223 final StringBuilder sb = new StringBuilder();
224 final int N = mBuffer.size();
225 sb.append("Archive (");
226 sb.append(N);
227 sb.append(" notification");
228 sb.append((N==1)?")":"s)");
229 return sb.toString();
230 }
231
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500232 public void record(StatusBarNotification nr) {
233 if (mBuffer.size() == BUFFER_SIZE) {
234 mBuffer.removeFirst();
235 }
Daniel Sandler26b81d52013-05-20 20:56:43 -0400236
237 // We don't want to store the heavy bits of the notification in the archive,
238 // but other clients in the system process might be using the object, so we
239 // store a (lightened) copy.
240 mBuffer.addLast(nr.cloneLight());
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500241 }
242
Daniel Sandler5e62e3a2013-04-15 20:57:02 -0400243
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500244 public void clear() {
245 mBuffer.clear();
246 }
247
248 public Iterator<StatusBarNotification> descendingIterator() {
249 return mBuffer.descendingIterator();
250 }
251 public Iterator<StatusBarNotification> ascendingIterator() {
252 return mBuffer.iterator();
253 }
254 public Iterator<StatusBarNotification> filter(
255 final Iterator<StatusBarNotification> iter, final String pkg, final int userId) {
256 return new Iterator<StatusBarNotification>() {
257 StatusBarNotification mNext = findNext();
258
259 private StatusBarNotification findNext() {
260 while (iter.hasNext()) {
261 StatusBarNotification nr = iter.next();
Daniel Sandler4f91efd2013-04-25 16:38:41 -0400262 if ((pkg == null || nr.getPackageName() == pkg)
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500263 && (userId == UserHandle.USER_ALL || nr.getUserId() == userId)) {
264 return nr;
265 }
266 }
267 return null;
268 }
269
270 @Override
271 public boolean hasNext() {
272 return mNext == null;
273 }
274
275 @Override
276 public StatusBarNotification next() {
277 StatusBarNotification next = mNext;
278 if (next == null) {
279 throw new NoSuchElementException();
280 }
281 mNext = findNext();
282 return next;
283 }
284
285 @Override
286 public void remove() {
287 iter.remove();
288 }
289 };
290 }
Daniel Sandler78d0d252013-02-12 08:14:52 -0500291
292 public StatusBarNotification[] getArray(int count) {
293 if (count == 0) count = Archive.BUFFER_SIZE;
294 final StatusBarNotification[] a
295 = new StatusBarNotification[Math.min(count, mBuffer.size())];
296 Iterator<StatusBarNotification> iter = descendingIterator();
297 int i=0;
298 while (iter.hasNext() && i < count) {
299 a[i++] = iter.next();
300 }
301 return a;
302 }
303
304 public StatusBarNotification[] getArray(int count, String pkg, int userId) {
305 if (count == 0) count = Archive.BUFFER_SIZE;
306 final StatusBarNotification[] a
307 = new StatusBarNotification[Math.min(count, mBuffer.size())];
308 Iterator<StatusBarNotification> iter = filter(descendingIterator(), pkg, userId);
309 int i=0;
310 while (iter.hasNext() && i < count) {
311 a[i++] = iter.next();
312 }
313 return a;
314 }
315
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500316 }
317
318 Archive mArchive = new Archive();
319
John Spurlock056c5192014-04-20 21:52:01 -0400320 private void loadPolicyFile() {
321 synchronized(mPolicyFile) {
322 mBlockedPackages.clear();
Daniel Sandler0da673f2012-04-11 12:33:16 -0400323
John Spurlock056c5192014-04-20 21:52:01 -0400324 FileInputStream infile = null;
325 try {
326 infile = mPolicyFile.openRead();
327 final XmlPullParser parser = Xml.newPullParser();
328 parser.setInput(infile, null);
Daniel Sandler0da673f2012-04-11 12:33:16 -0400329
John Spurlock056c5192014-04-20 21:52:01 -0400330 int type;
331 String tag;
332 int version = DB_VERSION;
333 while ((type = parser.next()) != END_DOCUMENT) {
334 tag = parser.getName();
335 if (type == START_TAG) {
336 if (TAG_BODY.equals(tag)) {
337 version = Integer.parseInt(
338 parser.getAttributeValue(null, ATTR_VERSION));
339 } else if (TAG_BLOCKED_PKGS.equals(tag)) {
340 while ((type = parser.next()) != END_DOCUMENT) {
341 tag = parser.getName();
342 if (TAG_PACKAGE.equals(tag)) {
343 mBlockedPackages.add(
344 parser.getAttributeValue(null, ATTR_NAME));
345 } else if (TAG_BLOCKED_PKGS.equals(tag) && type == END_TAG) {
346 break;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400347 }
348 }
349 }
350 }
John Spurlock056c5192014-04-20 21:52:01 -0400351 mZenModeHelper.readXml(parser);
Daniel Sandler0da673f2012-04-11 12:33:16 -0400352 }
John Spurlock056c5192014-04-20 21:52:01 -0400353 } catch (FileNotFoundException e) {
354 // No data yet
355 } catch (IOException e) {
356 Log.wtf(TAG, "Unable to read notification policy", e);
357 } catch (NumberFormatException e) {
358 Log.wtf(TAG, "Unable to parse notification policy", e);
359 } catch (XmlPullParserException e) {
360 Log.wtf(TAG, "Unable to parse notification policy", e);
361 } finally {
362 IoUtils.closeQuietly(infile);
363 }
364 }
365 }
366
367 public void savePolicyFile() {
368 mHandler.removeMessages(MESSAGE_SAVE_POLICY_FILE);
369 mHandler.sendEmptyMessage(MESSAGE_SAVE_POLICY_FILE);
370 }
371
372 private void handleSavePolicyFile() {
373 Slog.d(TAG, "handleSavePolicyFile");
374 synchronized (mPolicyFile) {
375 final FileOutputStream stream;
376 try {
377 stream = mPolicyFile.startWrite();
378 } catch (IOException e) {
379 Slog.w(TAG, "Failed to save policy file", e);
380 return;
381 }
382
383 try {
384 final XmlSerializer out = new FastXmlSerializer();
385 out.setOutput(stream, "utf-8");
386 out.startDocument(null, true);
387 out.startTag(null, TAG_BODY);
388 out.attribute(null, ATTR_VERSION, Integer.toString(DB_VERSION));
389 mZenModeHelper.writeXml(out);
390 out.endTag(null, TAG_BODY);
391 out.endDocument();
392 mPolicyFile.finishWrite(stream);
393 } catch (IOException e) {
394 Slog.w(TAG, "Failed to save policy file, restoring backup", e);
395 mPolicyFile.failWrite(stream);
Daniel Sandler0da673f2012-04-11 12:33:16 -0400396 }
397 }
398 }
399
Daniel Sandler4a900ac2013-01-30 14:04:10 -0500400 /** Use this when you actually want to post a notification or toast.
401 *
402 * Unchecked. Not exposed via Binder, but can be called in the course of enqueue*().
403 */
404 private boolean noteNotificationOp(String pkg, int uid) {
405 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
406 != AppOpsManager.MODE_ALLOWED) {
407 Slog.v(TAG, "notifications are disabled by AppOps for " + pkg);
408 return false;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400409 }
Daniel Sandler4a900ac2013-01-30 14:04:10 -0500410 return true;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400411 }
412
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800413 private static String idDebugString(Context baseContext, String packageName, int id) {
414 Context c = null;
415
416 if (packageName != null) {
417 try {
418 c = baseContext.createPackageContext(packageName, 0);
419 } catch (NameNotFoundException e) {
420 c = baseContext;
421 }
422 } else {
423 c = baseContext;
424 }
425
426 String pkg;
427 String type;
428 String name;
429
430 Resources r = c.getResources();
431 try {
432 return r.getResourceName(id);
433 } catch (Resources.NotFoundException e) {
434 return "<name unknown>";
435 }
436 }
437
Daniel Sandler78d0d252013-02-12 08:14:52 -0500438
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700439
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500440 public static final class NotificationRecord
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800441 {
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500442 final StatusBarNotification sbn;
Christoph Studer546bec82014-03-14 12:17:12 +0100443 final SingleNotificationStats stats = new SingleNotificationStats();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800444 IBinder statusBarKey;
445
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500446 NotificationRecord(StatusBarNotification sbn)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800447 {
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500448 this.sbn = sbn;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800449 }
Fred Quintana6ecaff12009-09-25 14:23:13 -0700450
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -0400451 public Notification getNotification() { return sbn.getNotification(); }
452 public int getFlags() { return sbn.getNotification().flags; }
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500453 public int getUserId() { return sbn.getUserId(); }
454
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800455 void dump(PrintWriter pw, String prefix, Context baseContext) {
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -0400456 final Notification notification = sbn.getNotification();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800457 pw.println(prefix + this);
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -0400458 pw.println(prefix + " uid=" + sbn.getUid() + " userId=" + sbn.getUserId());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800459 pw.println(prefix + " icon=0x" + Integer.toHexString(notification.icon)
Daniel Sandler4f91efd2013-04-25 16:38:41 -0400460 + " / " + idDebugString(baseContext, sbn.getPackageName(), notification.icon));
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -0400461 pw.println(prefix + " pri=" + notification.priority + " score=" + sbn.getScore());
John Spurlocka4294292014-03-24 18:02:32 -0400462 pw.println(prefix + " key=" + sbn.getKey());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800463 pw.println(prefix + " contentIntent=" + notification.contentIntent);
464 pw.println(prefix + " deleteIntent=" + notification.deleteIntent);
465 pw.println(prefix + " tickerText=" + notification.tickerText);
466 pw.println(prefix + " contentView=" + notification.contentView);
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400467 pw.println(prefix + String.format(" defaults=0x%08x flags=0x%08x",
468 notification.defaults, notification.flags));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800469 pw.println(prefix + " sound=" + notification.sound);
470 pw.println(prefix + " vibrate=" + Arrays.toString(notification.vibrate));
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400471 pw.println(prefix + String.format(" led=0x%08x onMs=%d offMs=%d",
472 notification.ledARGB, notification.ledOnMS, notification.ledOffMS));
473 if (notification.actions != null && notification.actions.length > 0) {
474 pw.println(prefix + " actions={");
475 final int N = notification.actions.length;
476 for (int i=0; i<N; i++) {
477 final Notification.Action action = notification.actions[i];
478 pw.println(String.format("%s [%d] \"%s\" -> %s",
479 prefix,
480 i,
481 action.title,
482 action.actionIntent.toString()
483 ));
484 }
485 pw.println(prefix + " }");
486 }
487 if (notification.extras != null && notification.extras.size() > 0) {
488 pw.println(prefix + " extras={");
489 for (String key : notification.extras.keySet()) {
490 pw.print(prefix + " " + key + "=");
491 Object val = notification.extras.get(key);
492 if (val == null) {
493 pw.println("null");
494 } else {
495 pw.print(val.toString());
496 if (val instanceof Bitmap) {
497 pw.print(String.format(" (%dx%d)",
498 ((Bitmap) val).getWidth(),
499 ((Bitmap) val).getHeight()));
500 } else if (val.getClass().isArray()) {
501 pw.println(" {");
502 final int N = Array.getLength(val);
503 for (int i=0; i<N; i++) {
504 if (i > 0) pw.println(",");
505 pw.print(prefix + " " + Array.get(val, i));
506 }
507 pw.print("\n" + prefix + " }");
508 }
509 pw.println();
510 }
511 }
512 pw.println(prefix + " }");
513 }
Christoph Studer546bec82014-03-14 12:17:12 +0100514 pw.println(prefix + " stats=" + stats.toString());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800515 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800516
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800517 @Override
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500518 public final String toString() {
519 return String.format(
John Spurlocka4294292014-03-24 18:02:32 -0400520 "NotificationRecord(0x%08x: pkg=%s user=%s id=%d tag=%s score=%d key=%s: %s)",
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500521 System.identityHashCode(this),
Adam Lesinski182f73f2013-12-05 16:48:06 -0800522 this.sbn.getPackageName(), this.sbn.getUser(), this.sbn.getId(),
John Spurlocka4294292014-03-24 18:02:32 -0400523 this.sbn.getTag(), this.sbn.getScore(), this.sbn.getKey(),
524 this.sbn.getNotification());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800525 }
526 }
527
528 private static final class ToastRecord
529 {
530 final int pid;
531 final String pkg;
532 final ITransientNotification callback;
533 int duration;
534
535 ToastRecord(int pid, String pkg, ITransientNotification callback, int duration)
536 {
537 this.pid = pid;
538 this.pkg = pkg;
539 this.callback = callback;
540 this.duration = duration;
541 }
542
543 void update(int duration) {
544 this.duration = duration;
545 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800546
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800547 void dump(PrintWriter pw, String prefix) {
548 pw.println(prefix + this);
549 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800550
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800551 @Override
552 public final String toString()
553 {
554 return "ToastRecord{"
555 + Integer.toHexString(System.identityHashCode(this))
556 + " pkg=" + pkg
557 + " callback=" + callback
558 + " duration=" + duration;
559 }
560 }
561
Adam Lesinski182f73f2013-12-05 16:48:06 -0800562 private final NotificationDelegate mNotificationDelegate = new NotificationDelegate() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800563
Adam Lesinski182f73f2013-12-05 16:48:06 -0800564 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800565 public void onSetDisabled(int status) {
566 synchronized (mNotificationList) {
John Spurlocke677d712014-02-13 12:52:19 -0500567 mDisableNotificationAlerts = (status & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0;
568 if (mDisableNotificationAlerts) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800569 // cancel whatever's going on
570 long identity = Binder.clearCallingIdentity();
571 try {
Adam Lesinski182f73f2013-12-05 16:48:06 -0800572 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
Jeff Sharkey098d5802012-04-26 17:30:34 -0700573 if (player != null) {
574 player.stopAsync();
575 }
576 } catch (RemoteException e) {
577 } finally {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800578 Binder.restoreCallingIdentity(identity);
579 }
580
581 identity = Binder.clearCallingIdentity();
582 try {
583 mVibrator.cancel();
Jeff Sharkey098d5802012-04-26 17:30:34 -0700584 } finally {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800585 Binder.restoreCallingIdentity(identity);
586 }
587 }
588 }
589 }
590
Adam Lesinski182f73f2013-12-05 16:48:06 -0800591 @Override
John Spurlocke6a7d932014-03-13 12:29:00 -0400592 public void onClearAll(int callingUid, int callingPid, int userId) {
Adam Lesinskie8240262014-03-26 16:01:00 -0700593 synchronized (mNotificationList) {
Kenny Guya263e4e2014-03-03 18:24:03 +0000594 cancelAllLocked(callingUid, callingPid, userId, REASON_DELEGATE_CANCEL_ALL, null,
595 /*includeCurrentProfiles*/ true);
Adam Lesinskie8240262014-03-26 16:01:00 -0700596 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800597 }
598
Adam Lesinski182f73f2013-12-05 16:48:06 -0800599 @Override
John Spurlocke6a7d932014-03-13 12:29:00 -0400600 public void onNotificationClick(int callingUid, int callingPid,
601 String pkg, String tag, int id, int userId) {
602 cancelNotification(callingUid, callingPid, pkg, tag, id, Notification.FLAG_AUTO_CANCEL,
603 Notification.FLAG_FOREGROUND_SERVICE, false, userId, REASON_DELEGATE_CLICK, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800604 }
605
Adam Lesinski182f73f2013-12-05 16:48:06 -0800606 @Override
John Spurlocke6a7d932014-03-13 12:29:00 -0400607 public void onNotificationClear(int callingUid, int callingPid,
608 String pkg, String tag, int id, int userId) {
609 cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
Kenny Guy3a7c4a52014-03-03 18:24:03 +0000610 Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
John Spurlocke6a7d932014-03-13 12:29:00 -0400611 true, userId, REASON_DELEGATE_CANCEL, null);
Daniel Sandler0f0b11c2010-08-04 15:54:58 -0400612 }
613
Adam Lesinski182f73f2013-12-05 16:48:06 -0800614 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800615 public void onPanelRevealed() {
Christoph Studer760ea552014-03-21 13:10:21 +0100616 EventLogTags.writeNotificationPanelRevealed();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800617 synchronized (mNotificationList) {
618 // sound
619 mSoundNotification = null;
Jeff Sharkey098d5802012-04-26 17:30:34 -0700620
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800621 long identity = Binder.clearCallingIdentity();
622 try {
Adam Lesinski182f73f2013-12-05 16:48:06 -0800623 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
Jeff Sharkey098d5802012-04-26 17:30:34 -0700624 if (player != null) {
625 player.stopAsync();
626 }
627 } catch (RemoteException e) {
628 } finally {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800629 Binder.restoreCallingIdentity(identity);
630 }
631
632 // vibrate
633 mVibrateNotification = null;
634 identity = Binder.clearCallingIdentity();
635 try {
636 mVibrator.cancel();
Jeff Sharkey098d5802012-04-26 17:30:34 -0700637 } finally {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800638 Binder.restoreCallingIdentity(identity);
639 }
640
641 // light
642 mLights.clear();
643 mLedNotification = null;
644 updateLightsLocked();
645 }
646 }
Joe Onorato005847b2010-06-04 16:08:02 -0400647
Adam Lesinski182f73f2013-12-05 16:48:06 -0800648 @Override
Christoph Studer760ea552014-03-21 13:10:21 +0100649 public void onPanelHidden() {
650 EventLogTags.writeNotificationPanelHidden();
651 }
652
653 @Override
John Spurlocke6a7d932014-03-13 12:29:00 -0400654 public void onNotificationError(int callingUid, int callingPid, String pkg, String tag, int id,
Kenny Guy3a7c4a52014-03-03 18:24:03 +0000655 int uid, int initialPid, String message, int userId) {
Daniel Sandlerd0a2f862010-08-03 15:29:31 -0400656 Slog.d(TAG, "onNotification error pkg=" + pkg + " tag=" + tag + " id=" + id
657 + "; will crashApplication(uid=" + uid + ", pid=" + initialPid + ")");
John Spurlocke6a7d932014-03-13 12:29:00 -0400658 cancelNotification(callingUid, callingPid, pkg, tag, id, 0, 0, false, userId,
659 REASON_DELEGATE_ERROR, null);
Dianne Hackborn9d39d0c2010-06-24 15:57:42 -0700660 long ident = Binder.clearCallingIdentity();
661 try {
662 ActivityManagerNative.getDefault().crashApplication(uid, initialPid, pkg,
663 "Bad notification posted from package " + pkg
664 + ": " + message);
665 } catch (RemoteException e) {
666 }
667 Binder.restoreCallingIdentity(ident);
Joe Onorato005847b2010-06-04 16:08:02 -0400668 }
John Spurlocke677d712014-02-13 12:52:19 -0500669
670 @Override
671 public boolean allowDisable(int what, IBinder token, String pkg) {
John Spurlock056c5192014-04-20 21:52:01 -0400672 return mZenModeHelper.allowDisable(what, token, pkg);
John Spurlocke677d712014-02-13 12:52:19 -0500673 }
Christoph Studer92b389d2014-04-01 18:44:40 +0200674
675 @Override
676 public void onNotificationVisibilityChanged(
677 String[] newlyVisibleKeys, String[] noLongerVisibleKeys) {
678 // Using ';' as separator since eventlogs uses ',' to separate
679 // args.
680 EventLogTags.writeNotificationVisibilityChanged(
681 TextUtils.join(";", newlyVisibleKeys),
682 TextUtils.join(";", noLongerVisibleKeys));
683 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800684 };
685
686 private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
687 @Override
688 public void onReceive(Context context, Intent intent) {
689 String action = intent.getAction();
690
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800691 boolean queryRestart = false;
Chris Wrenae9bb572013-05-15 14:50:28 -0400692 boolean queryRemove = false;
Daniel Sandler26ece572012-06-01 15:38:46 -0400693 boolean packageChanged = false;
John Spurlock79f78922013-05-16 09:10:05 -0400694 boolean cancelNotifications = true;
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800695
Chris Wren3da73022013-05-10 14:41:21 -0400696 if (action.equals(Intent.ACTION_PACKAGE_ADDED)
Chris Wrenae9bb572013-05-15 14:50:28 -0400697 || (queryRemove=action.equals(Intent.ACTION_PACKAGE_REMOVED))
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800698 || action.equals(Intent.ACTION_PACKAGE_RESTARTED)
Daniel Sandler26ece572012-06-01 15:38:46 -0400699 || (packageChanged=action.equals(Intent.ACTION_PACKAGE_CHANGED))
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800700 || (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART))
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800701 || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800702 String pkgList[] = null;
Chris Wrenae9bb572013-05-15 14:50:28 -0400703 boolean queryReplace = queryRemove &&
704 intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
705 if (DBG) Slog.i(TAG, "queryReplace=" + queryReplace);
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800706 if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800707 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800708 } else if (queryRestart) {
709 pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800710 } else {
711 Uri uri = intent.getData();
712 if (uri == null) {
713 return;
714 }
715 String pkgName = uri.getSchemeSpecificPart();
716 if (pkgName == null) {
717 return;
718 }
Daniel Sandler26ece572012-06-01 15:38:46 -0400719 if (packageChanged) {
720 // We cancel notifications for packages which have just been disabled
Christopher Tate06e5fed2013-10-09 14:39:15 -0700721 try {
Adam Lesinski182f73f2013-12-05 16:48:06 -0800722 final int enabled = getContext().getPackageManager()
Christopher Tate06e5fed2013-10-09 14:39:15 -0700723 .getApplicationEnabledSetting(pkgName);
724 if (enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED
725 || enabled == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
726 cancelNotifications = false;
727 }
728 } catch (IllegalArgumentException e) {
729 // Package doesn't exist; probably racing with uninstall.
730 // cancelNotifications is already true, so nothing to do here.
731 if (DBG) {
732 Slog.i(TAG, "Exception trying to look up app enabled setting", e);
733 }
Daniel Sandler26ece572012-06-01 15:38:46 -0400734 }
735 }
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800736 pkgList = new String[]{pkgName};
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800737 }
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700738
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800739 if (pkgList != null && (pkgList.length > 0)) {
740 for (String pkgName : pkgList) {
John Spurlock79f78922013-05-16 09:10:05 -0400741 if (cancelNotifications) {
John Spurlocke6a7d932014-03-13 12:29:00 -0400742 cancelAllNotificationsInt(MY_UID, MY_PID, pkgName, 0, 0, !queryRestart,
743 UserHandle.USER_ALL, REASON_PACKAGE_CHANGED, null);
John Spurlock79f78922013-05-16 09:10:05 -0400744 }
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800745 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800746 }
John Spurlockb408e8e2014-04-23 21:12:45 -0400747 mListeners.onPackagesChanged(queryReplace, pkgList);
Mike Lockwood63b5ad92011-08-30 09:55:30 -0400748 } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
749 // Keep track of screen on/off state, but do not turn off the notification light
750 // until user passes through the lock screen or views the notification.
751 mScreenOn = true;
752 } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
753 mScreenOn = false;
Daniel Sandlere96ffb12010-03-11 13:38:06 -0500754 } else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) {
John Spurlock5d2eeb12014-01-16 10:46:36 -0500755 mInCall = TelephonyManager.EXTRA_STATE_OFFHOOK
756 .equals(intent.getStringExtra(TelephonyManager.EXTRA_STATE));
Daniel Sandlere96ffb12010-03-11 13:38:06 -0500757 updateNotificationPulse();
Dianne Hackborn80a4af22012-08-27 19:18:31 -0700758 } else if (action.equals(Intent.ACTION_USER_STOPPED)) {
759 int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
760 if (userHandle >= 0) {
John Spurlocke6a7d932014-03-13 12:29:00 -0400761 cancelAllNotificationsInt(MY_UID, MY_PID, null, 0, 0, true, userHandle,
762 REASON_USER_STOPPED, null);
Dianne Hackborn80a4af22012-08-27 19:18:31 -0700763 }
Mike Lockwood63b5ad92011-08-30 09:55:30 -0400764 } else if (action.equals(Intent.ACTION_USER_PRESENT)) {
765 // turn off LED when user passes through lock screen
766 mNotificationLight.turnOff();
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400767 } else if (action.equals(Intent.ACTION_USER_SWITCHED)) {
768 // reload per-user settings
769 mSettingsObserver.update(null);
John Spurlockb408e8e2014-04-23 21:12:45 -0400770 mUserProfiles.updateCache(context);
Kenny Guy3a7c4a52014-03-03 18:24:03 +0000771 } else if (action.equals(Intent.ACTION_USER_ADDED)) {
John Spurlockb408e8e2014-04-23 21:12:45 -0400772 mUserProfiles.updateCache(context);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800773 }
774 }
775 };
776
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700777 class SettingsObserver extends ContentObserver {
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400778 private final Uri NOTIFICATION_LIGHT_PULSE_URI
779 = Settings.System.getUriFor(Settings.System.NOTIFICATION_LIGHT_PULSE);
780
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700781 SettingsObserver(Handler handler) {
782 super(handler);
783 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800784
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700785 void observe() {
Adam Lesinski182f73f2013-12-05 16:48:06 -0800786 ContentResolver resolver = getContext().getContentResolver();
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400787 resolver.registerContentObserver(NOTIFICATION_LIGHT_PULSE_URI,
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700788 false, this, UserHandle.USER_ALL);
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400789 update(null);
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700790 }
791
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400792 @Override public void onChange(boolean selfChange, Uri uri) {
793 update(uri);
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700794 }
795
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400796 public void update(Uri uri) {
Adam Lesinski182f73f2013-12-05 16:48:06 -0800797 ContentResolver resolver = getContext().getContentResolver();
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400798 if (uri == null || NOTIFICATION_LIGHT_PULSE_URI.equals(uri)) {
799 boolean pulseEnabled = Settings.System.getInt(resolver,
800 Settings.System.NOTIFICATION_LIGHT_PULSE, 0) != 0;
801 if (mNotificationPulseEnabled != pulseEnabled) {
802 mNotificationPulseEnabled = pulseEnabled;
803 updateNotificationPulse();
804 }
805 }
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700806 }
807 }
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500808
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400809 private SettingsObserver mSettingsObserver;
John Spurlock056c5192014-04-20 21:52:01 -0400810 private ZenModeHelper mZenModeHelper;
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400811
Daniel Sandleredbb3802012-11-13 20:49:47 -0800812 static long[] getLongArray(Resources r, int resid, int maxlen, long[] def) {
813 int[] ar = r.getIntArray(resid);
814 if (ar == null) {
815 return def;
816 }
817 final int len = ar.length > maxlen ? maxlen : ar.length;
818 long[] out = new long[len];
819 for (int i=0; i<len; i++) {
820 out[i] = ar[i];
821 }
822 return out;
823 }
824
Jeff Brownb880d882014-02-10 19:47:07 -0800825 public NotificationManagerService(Context context) {
826 super(context);
827 }
828
Adam Lesinski182f73f2013-12-05 16:48:06 -0800829 @Override
830 public void onStart() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800831 mAm = ActivityManagerNative.getDefault();
Adam Lesinski182f73f2013-12-05 16:48:06 -0800832 mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
833 mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
San Mehat3ee13172010-02-04 20:54:43 -0800834
Adam Lesinski182f73f2013-12-05 16:48:06 -0800835 mHandler = new WorkerHandler();
John Spurlock056c5192014-04-20 21:52:01 -0400836 mZenModeHelper = new ZenModeHelper(getContext(), mHandler);
837 mZenModeHelper.setCallback(new ZenModeHelper.Callback() {
838 @Override
839 public void onConfigChanged() {
840 savePolicyFile();
841 }
842 });
843 final File systemDir = new File(Environment.getDataDirectory(), "system");
844 mPolicyFile = new AtomicFile(new File(systemDir, "notification_policy.xml"));
Daniel Sandler4a900ac2013-01-30 14:04:10 -0500845
846 importOldBlockDb();
Daniel Sandler0da673f2012-04-11 12:33:16 -0400847
John Spurlockb408e8e2014-04-23 21:12:45 -0400848 mListeners = new NotificationListeners(getContext(),
849 mHandler, mNotificationList, mUserProfiles) {
850 @Override
851 public void onServiceAdded(INotificationListener listener) {
852 final String[] keys = getActiveNotificationKeysFromListener(listener);
853 try {
854 listener.onListenerConnected(keys);
855 } catch (RemoteException e) {
856 // we tried
857 }
858 }
859 };
Adam Lesinski182f73f2013-12-05 16:48:06 -0800860 mStatusBar = getLocalService(StatusBarManagerInternal.class);
861 mStatusBar.setNotificationDelegate(mNotificationDelegate);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800862
Adam Lesinski182f73f2013-12-05 16:48:06 -0800863 final LightsManager lights = getLocalService(LightsManager.class);
864 mNotificationLight = lights.getLight(LightsManager.LIGHT_ID_NOTIFICATIONS);
865 mAttentionLight = lights.getLight(LightsManager.LIGHT_ID_ATTENTION);
Mike Lockwood3cb67a32009-11-27 14:25:58 -0500866
Adam Lesinski182f73f2013-12-05 16:48:06 -0800867 Resources resources = getContext().getResources();
Mike Lockwood670f9322010-01-20 12:13:36 -0500868 mDefaultNotificationColor = resources.getColor(
Scott Greenwald9a05b312013-06-28 00:37:54 -0400869 R.color.config_defaultNotificationColor);
Mike Lockwood670f9322010-01-20 12:13:36 -0500870 mDefaultNotificationLedOn = resources.getInteger(
Scott Greenwald9a05b312013-06-28 00:37:54 -0400871 R.integer.config_defaultNotificationLedOn);
Mike Lockwood670f9322010-01-20 12:13:36 -0500872 mDefaultNotificationLedOff = resources.getInteger(
Scott Greenwald9a05b312013-06-28 00:37:54 -0400873 R.integer.config_defaultNotificationLedOff);
Mike Lockwood670f9322010-01-20 12:13:36 -0500874
Daniel Sandleredbb3802012-11-13 20:49:47 -0800875 mDefaultVibrationPattern = getLongArray(resources,
Scott Greenwald9a05b312013-06-28 00:37:54 -0400876 R.array.config_defaultNotificationVibePattern,
Daniel Sandleredbb3802012-11-13 20:49:47 -0800877 VIBRATE_PATTERN_MAXLEN,
878 DEFAULT_VIBRATE_PATTERN);
879
880 mFallbackVibrationPattern = getLongArray(resources,
Scott Greenwald9a05b312013-06-28 00:37:54 -0400881 R.array.config_notificationFallbackVibePattern,
Daniel Sandleredbb3802012-11-13 20:49:47 -0800882 VIBRATE_PATTERN_MAXLEN,
883 DEFAULT_VIBRATE_PATTERN);
884
Joe Onorato39f5b6a2009-07-23 12:29:19 -0400885 // Don't start allowing notifications until the setup wizard has run once.
886 // After that, including subsequent boots, init with notifications turned on.
887 // This works on the first boot because the setup wizard will toggle this
888 // flag at least once and we'll go back to 0 after that.
Adam Lesinski182f73f2013-12-05 16:48:06 -0800889 if (0 == Settings.Global.getInt(getContext().getContentResolver(),
Jeff Brownbf6f6f92012-09-25 15:03:20 -0700890 Settings.Global.DEVICE_PROVISIONED, 0)) {
John Spurlocke677d712014-02-13 12:52:19 -0500891 mDisableNotificationAlerts = true;
Joe Onorato39f5b6a2009-07-23 12:29:19 -0400892 }
John Spurlock056c5192014-04-20 21:52:01 -0400893 mZenModeHelper.updateZenMode();
Joe Onorato39f5b6a2009-07-23 12:29:19 -0400894
John Spurlockb408e8e2014-04-23 21:12:45 -0400895 mUserProfiles.updateCache(getContext());
Kenny Guya263e4e2014-03-03 18:24:03 +0000896
Mike Lockwood35e16bf2010-11-30 19:53:36 -0500897 // register for various Intents
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800898 IntentFilter filter = new IntentFilter();
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500899 filter.addAction(Intent.ACTION_SCREEN_ON);
900 filter.addAction(Intent.ACTION_SCREEN_OFF);
Daniel Sandlere96ffb12010-03-11 13:38:06 -0500901 filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
Mike Lockwood63b5ad92011-08-30 09:55:30 -0400902 filter.addAction(Intent.ACTION_USER_PRESENT);
Dianne Hackborn80a4af22012-08-27 19:18:31 -0700903 filter.addAction(Intent.ACTION_USER_STOPPED);
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400904 filter.addAction(Intent.ACTION_USER_SWITCHED);
Kenny Guy3a7c4a52014-03-03 18:24:03 +0000905 filter.addAction(Intent.ACTION_USER_ADDED);
Adam Lesinski182f73f2013-12-05 16:48:06 -0800906 getContext().registerReceiver(mIntentReceiver, filter);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800907 IntentFilter pkgFilter = new IntentFilter();
Chris Wren3da73022013-05-10 14:41:21 -0400908 pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800909 pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
Daniel Sandleraac0eb02011-08-06 22:51:56 -0400910 pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800911 pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
912 pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
913 pkgFilter.addDataScheme("package");
Adam Lesinski182f73f2013-12-05 16:48:06 -0800914 getContext().registerReceiver(mIntentReceiver, pkgFilter);
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800915 IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
Adam Lesinski182f73f2013-12-05 16:48:06 -0800916 getContext().registerReceiver(mIntentReceiver, sdFilter);
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800917
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400918 mSettingsObserver = new SettingsObserver(mHandler);
Scott Greenwald9a05b312013-06-28 00:37:54 -0400919
920 // spin up NotificationScorers
921 String[] notificationScorerNames = resources.getStringArray(
922 R.array.config_notificationScorers);
923 for (String scorerName : notificationScorerNames) {
924 try {
Adam Lesinski182f73f2013-12-05 16:48:06 -0800925 Class<?> scorerClass = getContext().getClassLoader().loadClass(scorerName);
Scott Greenwald9a05b312013-06-28 00:37:54 -0400926 NotificationScorer scorer = (NotificationScorer) scorerClass.newInstance();
Adam Lesinski182f73f2013-12-05 16:48:06 -0800927 scorer.initialize(getContext());
Scott Greenwald9a05b312013-06-28 00:37:54 -0400928 mScorers.add(scorer);
929 } catch (ClassNotFoundException e) {
930 Slog.w(TAG, "Couldn't find scorer " + scorerName + ".", e);
931 } catch (InstantiationException e) {
932 Slog.w(TAG, "Couldn't instantiate scorer " + scorerName + ".", e);
933 } catch (IllegalAccessException e) {
934 Slog.w(TAG, "Problem accessing scorer " + scorerName + ".", e);
935 }
936 }
Adam Lesinski182f73f2013-12-05 16:48:06 -0800937
938 publishBinderService(Context.NOTIFICATION_SERVICE, mService);
939 publishLocalService(NotificationManagerInternal.class, mInternalService);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800940 }
941
Daniel Sandler4a900ac2013-01-30 14:04:10 -0500942 /**
943 * Read the old XML-based app block database and import those blockages into the AppOps system.
944 */
945 private void importOldBlockDb() {
John Spurlock056c5192014-04-20 21:52:01 -0400946 loadPolicyFile();
Daniel Sandler4a900ac2013-01-30 14:04:10 -0500947
Adam Lesinski182f73f2013-12-05 16:48:06 -0800948 PackageManager pm = getContext().getPackageManager();
Daniel Sandler4a900ac2013-01-30 14:04:10 -0500949 for (String pkg : mBlockedPackages) {
950 PackageInfo info = null;
951 try {
952 info = pm.getPackageInfo(pkg, 0);
Adam Lesinski182f73f2013-12-05 16:48:06 -0800953 setNotificationsEnabledForPackageImpl(pkg, info.applicationInfo.uid, false);
Daniel Sandler4a900ac2013-01-30 14:04:10 -0500954 } catch (NameNotFoundException e) {
955 // forget you
956 }
957 }
958 mBlockedPackages.clear();
Daniel Sandler4a900ac2013-01-30 14:04:10 -0500959 }
960
Adam Lesinski182f73f2013-12-05 16:48:06 -0800961 @Override
962 public void onBootPhase(int phase) {
963 if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
964 // no beeping until we're basically done booting
965 mSystemReady = true;
Jeff Sharkey098d5802012-04-26 17:30:34 -0700966
Adam Lesinski182f73f2013-12-05 16:48:06 -0800967 // Grab our optional AudioService
968 mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700969
Adam Lesinskia6db4ab2014-03-24 12:31:45 -0700970 } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
971 // This observer will force an update when observe is called, causing us to
972 // bind to listener services.
973 mSettingsObserver.observe();
John Spurlockb408e8e2014-04-23 21:12:45 -0400974 mListeners.onBootPhaseAppsCanStart();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800975 }
976 }
977
Adam Lesinski182f73f2013-12-05 16:48:06 -0800978 void setNotificationsEnabledForPackageImpl(String pkg, int uid, boolean enabled) {
979 Slog.v(TAG, (enabled?"en":"dis") + "abling notifications for " + pkg);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800980
Adam Lesinski182f73f2013-12-05 16:48:06 -0800981 mAppOps.setMode(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg,
982 enabled ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800983
Adam Lesinski182f73f2013-12-05 16:48:06 -0800984 // Now, cancel any outstanding notifications that are part of a just-disabled app
985 if (ENABLE_BLOCKED_NOTIFICATIONS && !enabled) {
John Spurlocke6a7d932014-03-13 12:29:00 -0400986 cancelAllNotificationsInt(MY_UID, MY_PID, pkg, 0, 0, true, UserHandle.getUserId(uid),
987 REASON_PACKAGE_BANNED, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800988 }
989 }
990
Adam Lesinski182f73f2013-12-05 16:48:06 -0800991 private final IBinder mService = new INotificationManager.Stub() {
992 // Toasts
993 // ============================================================================
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800994
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800995 @Override
Adam Lesinski182f73f2013-12-05 16:48:06 -0800996 public void enqueueToast(String pkg, ITransientNotification callback, int duration)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800997 {
Adam Lesinski182f73f2013-12-05 16:48:06 -0800998 if (DBG) {
999 Slog.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback
1000 + " duration=" + duration);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001001 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001002
1003 if (pkg == null || callback == null) {
1004 Slog.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback);
1005 return ;
1006 }
1007
John Spurlockb408e8e2014-04-23 21:12:45 -04001008 final boolean isSystemToast =
1009 NotificationUtil.isCallerSystem() || ("android".equals(pkg));
Adam Lesinski182f73f2013-12-05 16:48:06 -08001010
1011 if (ENABLE_BLOCKED_TOASTS && !noteNotificationOp(pkg, Binder.getCallingUid())) {
1012 if (!isSystemToast) {
1013 Slog.e(TAG, "Suppressing toast from package " + pkg + " by user request.");
1014 return;
1015 }
1016 }
1017
1018 synchronized (mToastQueue) {
1019 int callingPid = Binder.getCallingPid();
1020 long callingId = Binder.clearCallingIdentity();
1021 try {
1022 ToastRecord record;
1023 int index = indexOfToastLocked(pkg, callback);
1024 // If it's already in the queue, we update it in place, we don't
1025 // move it to the end of the queue.
1026 if (index >= 0) {
1027 record = mToastQueue.get(index);
1028 record.update(duration);
1029 } else {
1030 // Limit the number of toasts that any given package except the android
1031 // package can enqueue. Prevents DOS attacks and deals with leaks.
1032 if (!isSystemToast) {
1033 int count = 0;
1034 final int N = mToastQueue.size();
1035 for (int i=0; i<N; i++) {
1036 final ToastRecord r = mToastQueue.get(i);
1037 if (r.pkg.equals(pkg)) {
1038 count++;
1039 if (count >= MAX_PACKAGE_NOTIFICATIONS) {
1040 Slog.e(TAG, "Package has already posted " + count
1041 + " toasts. Not showing more. Package=" + pkg);
1042 return;
1043 }
1044 }
1045 }
1046 }
1047
1048 record = new ToastRecord(callingPid, pkg, callback, duration);
1049 mToastQueue.add(record);
1050 index = mToastQueue.size() - 1;
1051 keepProcessAliveLocked(callingPid);
1052 }
1053 // If it's at index 0, it's the current toast. It doesn't matter if it's
1054 // new or just been updated. Call back and tell it to show itself.
1055 // If the callback fails, this will remove it from the list, so don't
1056 // assume that it's valid after this.
1057 if (index == 0) {
1058 showNextToastLocked();
1059 }
1060 } finally {
1061 Binder.restoreCallingIdentity(callingId);
1062 }
1063 }
1064 }
1065
1066 @Override
1067 public void cancelToast(String pkg, ITransientNotification callback) {
1068 Slog.i(TAG, "cancelToast pkg=" + pkg + " callback=" + callback);
1069
1070 if (pkg == null || callback == null) {
1071 Slog.e(TAG, "Not cancelling notification. pkg=" + pkg + " callback=" + callback);
1072 return ;
1073 }
1074
1075 synchronized (mToastQueue) {
1076 long callingId = Binder.clearCallingIdentity();
1077 try {
1078 int index = indexOfToastLocked(pkg, callback);
1079 if (index >= 0) {
1080 cancelToastLocked(index);
1081 } else {
1082 Slog.w(TAG, "Toast already cancelled. pkg=" + pkg
1083 + " callback=" + callback);
1084 }
1085 } finally {
1086 Binder.restoreCallingIdentity(callingId);
1087 }
1088 }
1089 }
1090
1091 @Override
Christoph Studer8fd7f1e2014-04-11 17:35:05 -04001092 public void enqueueNotificationWithTag(String pkg, String opPkg, String tag, int id,
Adam Lesinski182f73f2013-12-05 16:48:06 -08001093 Notification notification, int[] idOut, int userId) throws RemoteException {
Christoph Studer8fd7f1e2014-04-11 17:35:05 -04001094 enqueueNotificationInternal(pkg, opPkg, Binder.getCallingUid(),
Adam Lesinski182f73f2013-12-05 16:48:06 -08001095 Binder.getCallingPid(), tag, id, notification, idOut, userId);
1096 }
1097
1098 @Override
1099 public void cancelNotificationWithTag(String pkg, String tag, int id, int userId) {
John Spurlockb408e8e2014-04-23 21:12:45 -04001100 NotificationUtil.checkCallerIsSystemOrSameApp(pkg);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001101 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1102 Binder.getCallingUid(), userId, true, false, "cancelNotificationWithTag", pkg);
1103 // Don't allow client applications to cancel foreground service notis.
John Spurlocke6a7d932014-03-13 12:29:00 -04001104 cancelNotification(Binder.getCallingUid(), Binder.getCallingPid(), pkg, tag, id, 0,
Adam Lesinski182f73f2013-12-05 16:48:06 -08001105 Binder.getCallingUid() == Process.SYSTEM_UID
John Spurlocke6a7d932014-03-13 12:29:00 -04001106 ? 0 : Notification.FLAG_FOREGROUND_SERVICE, false, userId, REASON_NOMAN_CANCEL,
1107 null);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001108 }
1109
1110 @Override
1111 public void cancelAllNotifications(String pkg, int userId) {
John Spurlockb408e8e2014-04-23 21:12:45 -04001112 NotificationUtil.checkCallerIsSystemOrSameApp(pkg);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001113
1114 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1115 Binder.getCallingUid(), userId, true, false, "cancelAllNotifications", pkg);
1116
1117 // Calling from user space, don't allow the canceling of actively
1118 // running foreground services.
John Spurlocke6a7d932014-03-13 12:29:00 -04001119 cancelAllNotificationsInt(Binder.getCallingUid(), Binder.getCallingPid(),
1120 pkg, 0, Notification.FLAG_FOREGROUND_SERVICE, true, userId,
1121 REASON_NOMAN_CANCEL_ALL, null);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001122 }
1123
1124 @Override
1125 public void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) {
John Spurlockb408e8e2014-04-23 21:12:45 -04001126 NotificationUtil.checkCallerIsSystem();
Adam Lesinski182f73f2013-12-05 16:48:06 -08001127
1128 setNotificationsEnabledForPackageImpl(pkg, uid, enabled);
1129 }
1130
1131 /**
1132 * Use this when you just want to know if notifications are OK for this package.
1133 */
1134 @Override
1135 public boolean areNotificationsEnabledForPackage(String pkg, int uid) {
John Spurlockb408e8e2014-04-23 21:12:45 -04001136 NotificationUtil.checkCallerIsSystem();
Adam Lesinski182f73f2013-12-05 16:48:06 -08001137 return (mAppOps.checkOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
1138 == AppOpsManager.MODE_ALLOWED);
1139 }
1140
1141 /**
1142 * System-only API for getting a list of current (i.e. not cleared) notifications.
1143 *
1144 * Requires ACCESS_NOTIFICATIONS which is signature|system.
1145 */
1146 @Override
1147 public StatusBarNotification[] getActiveNotifications(String callingPkg) {
1148 // enforce() will ensure the calling uid has the correct permission
1149 getContext().enforceCallingOrSelfPermission(
1150 android.Manifest.permission.ACCESS_NOTIFICATIONS,
1151 "NotificationManagerService.getActiveNotifications");
1152
1153 StatusBarNotification[] tmp = null;
1154 int uid = Binder.getCallingUid();
1155
1156 // noteOp will check to make sure the callingPkg matches the uid
1157 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
1158 == AppOpsManager.MODE_ALLOWED) {
1159 synchronized (mNotificationList) {
1160 tmp = new StatusBarNotification[mNotificationList.size()];
1161 final int N = mNotificationList.size();
1162 for (int i=0; i<N; i++) {
1163 tmp[i] = mNotificationList.get(i).sbn;
1164 }
1165 }
1166 }
1167 return tmp;
1168 }
1169
1170 /**
1171 * System-only API for getting a list of recent (cleared, no longer shown) notifications.
1172 *
1173 * Requires ACCESS_NOTIFICATIONS which is signature|system.
1174 */
1175 @Override
1176 public StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count) {
1177 // enforce() will ensure the calling uid has the correct permission
1178 getContext().enforceCallingOrSelfPermission(
1179 android.Manifest.permission.ACCESS_NOTIFICATIONS,
1180 "NotificationManagerService.getHistoricalNotifications");
1181
1182 StatusBarNotification[] tmp = null;
1183 int uid = Binder.getCallingUid();
1184
1185 // noteOp will check to make sure the callingPkg matches the uid
1186 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
1187 == AppOpsManager.MODE_ALLOWED) {
1188 synchronized (mArchive) {
1189 tmp = mArchive.getArray(count);
1190 }
1191 }
1192 return tmp;
1193 }
1194
1195 /**
1196 * Register a listener binder directly with the notification manager.
1197 *
1198 * Only works with system callers. Apps should extend
1199 * {@link android.service.notification.NotificationListenerService}.
1200 */
1201 @Override
1202 public void registerListener(final INotificationListener listener,
1203 final ComponentName component, final int userid) {
John Spurlockb408e8e2014-04-23 21:12:45 -04001204 NotificationUtil.checkCallerIsSystem();
1205 mListeners.registerListener(listener, component, userid);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001206 }
1207
1208 /**
1209 * Remove a listener binder directly
1210 */
1211 @Override
1212 public void unregisterListener(INotificationListener listener, int userid) {
John Spurlockb408e8e2014-04-23 21:12:45 -04001213 mListeners.unregisterListener(listener, userid);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001214 }
1215
1216 /**
1217 * Allow an INotificationListener to simulate a "clear all" operation.
1218 *
1219 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onClearAllNotifications}
1220 *
1221 * @param token The binder for the listener, to check that the caller is allowed
1222 */
1223 @Override
John Spurlocka4294292014-03-24 18:02:32 -04001224 public void cancelNotificationsFromListener(INotificationListener token, String[] keys) {
John Spurlocke6a7d932014-03-13 12:29:00 -04001225 final int callingUid = Binder.getCallingUid();
1226 final int callingPid = Binder.getCallingPid();
Adam Lesinski182f73f2013-12-05 16:48:06 -08001227 long identity = Binder.clearCallingIdentity();
1228 try {
Adam Lesinskie8240262014-03-26 16:01:00 -07001229 synchronized (mNotificationList) {
John Spurlockb408e8e2014-04-23 21:12:45 -04001230 final NotificationListenerInfo info =
1231 mListeners.checkListenerTokenLocked(token);
John Spurlocka4294292014-03-24 18:02:32 -04001232 if (keys != null) {
1233 final int N = keys.length;
1234 for (int i = 0; i < N; i++) {
1235 NotificationRecord r = mNotificationsByKey.get(keys[i]);
Kenny Guya263e4e2014-03-03 18:24:03 +00001236 final int userId = r.sbn.getUserId();
1237 if (userId != info.userid && userId != UserHandle.USER_ALL &&
John Spurlockb408e8e2014-04-23 21:12:45 -04001238 !mUserProfiles.isCurrentProfile(userId)) {
Kenny Guya263e4e2014-03-03 18:24:03 +00001239 throw new SecurityException("Disallowed call from listener: "
1240 + info.listener);
1241 }
John Spurlocka4294292014-03-24 18:02:32 -04001242 if (r != null) {
1243 cancelNotificationFromListenerLocked(info, callingUid, callingPid,
Kenny Guya263e4e2014-03-03 18:24:03 +00001244 r.sbn.getPackageName(), r.sbn.getTag(), r.sbn.getId(),
1245 userId);
John Spurlocka4294292014-03-24 18:02:32 -04001246 }
1247 }
1248 } else {
1249 cancelAllLocked(callingUid, callingPid, info.userid,
Kenny Guya263e4e2014-03-03 18:24:03 +00001250 REASON_LISTENER_CANCEL_ALL, info, info.supportsProfiles());
John Spurlocka4294292014-03-24 18:02:32 -04001251 }
Adam Lesinskie8240262014-03-26 16:01:00 -07001252 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001253 } finally {
1254 Binder.restoreCallingIdentity(identity);
1255 }
1256 }
1257
John Spurlocka4294292014-03-24 18:02:32 -04001258 private void cancelNotificationFromListenerLocked(NotificationListenerInfo info,
Kenny Guya263e4e2014-03-03 18:24:03 +00001259 int callingUid, int callingPid, String pkg, String tag, int id, int userId) {
John Spurlocka4294292014-03-24 18:02:32 -04001260 cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
1261 Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
1262 true,
Kenny Guya263e4e2014-03-03 18:24:03 +00001263 userId, REASON_LISTENER_CANCEL, info);
John Spurlocka4294292014-03-24 18:02:32 -04001264 }
1265
Adam Lesinski182f73f2013-12-05 16:48:06 -08001266 /**
1267 * Allow an INotificationListener to simulate clearing (dismissing) a single notification.
1268 *
1269 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onNotificationClear}
1270 *
1271 * @param token The binder for the listener, to check that the caller is allowed
1272 */
1273 @Override
1274 public void cancelNotificationFromListener(INotificationListener token, String pkg,
1275 String tag, int id) {
John Spurlocke6a7d932014-03-13 12:29:00 -04001276 final int callingUid = Binder.getCallingUid();
1277 final int callingPid = Binder.getCallingPid();
Adam Lesinski182f73f2013-12-05 16:48:06 -08001278 long identity = Binder.clearCallingIdentity();
1279 try {
Adam Lesinskie8240262014-03-26 16:01:00 -07001280 synchronized (mNotificationList) {
John Spurlockb408e8e2014-04-23 21:12:45 -04001281 final NotificationListenerInfo info =
1282 mListeners.checkListenerTokenLocked(token);
Kenny Guya263e4e2014-03-03 18:24:03 +00001283 if (info.supportsProfiles()) {
1284 Log.e(TAG, "Ignoring deprecated cancelNotification(pkg, tag, id) "
1285 + "from " + info.component
1286 + " use cancelNotification(key) instead.");
1287 } else {
1288 cancelNotificationFromListenerLocked(info, callingUid, callingPid,
1289 pkg, tag, id, info.userid);
1290 }
Adam Lesinskie8240262014-03-26 16:01:00 -07001291 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001292 } finally {
1293 Binder.restoreCallingIdentity(identity);
1294 }
1295 }
1296
1297 /**
1298 * Allow an INotificationListener to request the list of outstanding notifications seen by
1299 * the current user. Useful when starting up, after which point the listener callbacks
1300 * should be used.
1301 *
1302 * @param token The binder for the listener, to check that the caller is allowed
1303 */
1304 @Override
1305 public StatusBarNotification[] getActiveNotificationsFromListener(
John Spurlocka4294292014-03-24 18:02:32 -04001306 INotificationListener token, String[] keys) {
Adam Lesinski182f73f2013-12-05 16:48:06 -08001307 synchronized (mNotificationList) {
John Spurlockb408e8e2014-04-23 21:12:45 -04001308 final NotificationListenerInfo info = mListeners.checkListenerTokenLocked(token);
John Spurlocka4294292014-03-24 18:02:32 -04001309 final ArrayList<StatusBarNotification> list
1310 = new ArrayList<StatusBarNotification>();
1311 if (keys == null) {
1312 final int N = mNotificationList.size();
1313 for (int i=0; i<N; i++) {
1314 StatusBarNotification sbn = mNotificationList.get(i).sbn;
1315 if (info.enabledAndUserMatches(sbn)) {
1316 list.add(sbn);
1317 }
1318 }
1319 } else {
1320 final int N = keys.length;
1321 for (int i=0; i<N; i++) {
1322 NotificationRecord r = mNotificationsByKey.get(keys[i]);
1323 if (r != null && info.enabledAndUserMatches(r.sbn)) {
1324 list.add(r.sbn);
1325 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001326 }
1327 }
Adam Lesinski350159c2014-03-27 11:15:11 -07001328 return list.toArray(new StatusBarNotification[list.size()]);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001329 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001330 }
1331
1332 @Override
John Spurlocka4294292014-03-24 18:02:32 -04001333 public String[] getActiveNotificationKeysFromListener(INotificationListener token) {
1334 return NotificationManagerService.this.getActiveNotificationKeysFromListener(token);
1335 }
1336
1337 @Override
John Spurlock056c5192014-04-20 21:52:01 -04001338 public ZenModeConfig getZenModeConfig() {
John Spurlockb408e8e2014-04-23 21:12:45 -04001339 NotificationUtil.checkCallerIsSystem();
John Spurlock056c5192014-04-20 21:52:01 -04001340 return mZenModeHelper.getConfig();
1341 }
1342
1343 @Override
1344 public boolean setZenModeConfig(ZenModeConfig config) {
John Spurlockb408e8e2014-04-23 21:12:45 -04001345 NotificationUtil.checkCallerIsSystem();
John Spurlock056c5192014-04-20 21:52:01 -04001346 return mZenModeHelper.setConfig(config);
1347 }
1348
1349 @Override
Adam Lesinski182f73f2013-12-05 16:48:06 -08001350 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1351 if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
1352 != PackageManager.PERMISSION_GRANTED) {
1353 pw.println("Permission Denial: can't dump NotificationManager from from pid="
1354 + Binder.getCallingPid()
1355 + ", uid=" + Binder.getCallingUid());
1356 return;
1357 }
1358
1359 dumpImpl(pw);
1360 }
1361 };
1362
John Spurlocka4294292014-03-24 18:02:32 -04001363 private String[] getActiveNotificationKeysFromListener(INotificationListener token) {
1364 synchronized (mNotificationList) {
John Spurlockb408e8e2014-04-23 21:12:45 -04001365 final NotificationListenerInfo info = mListeners.checkListenerTokenLocked(token);
John Spurlocka4294292014-03-24 18:02:32 -04001366 final ArrayList<String> keys = new ArrayList<String>();
1367 final int N = mNotificationList.size();
1368 for (int i=0; i<N; i++) {
1369 final StatusBarNotification sbn = mNotificationList.get(i).sbn;
1370 if (info.enabledAndUserMatches(sbn)) {
1371 keys.add(sbn.getKey());
1372 }
1373 }
1374 return keys.toArray(new String[keys.size()]);
1375 }
1376 }
1377
Adam Lesinski182f73f2013-12-05 16:48:06 -08001378 void dumpImpl(PrintWriter pw) {
1379 pw.println("Current Notification Manager state:");
1380
John Spurlockb408e8e2014-04-23 21:12:45 -04001381 mListeners.dump(pw);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001382
1383 int N;
1384
1385 synchronized (mToastQueue) {
1386 N = mToastQueue.size();
1387 if (N > 0) {
1388 pw.println(" Toast Queue:");
1389 for (int i=0; i<N; i++) {
1390 mToastQueue.get(i).dump(pw, " ");
1391 }
1392 pw.println(" ");
1393 }
1394
1395 }
1396
1397 synchronized (mNotificationList) {
1398 N = mNotificationList.size();
1399 if (N > 0) {
1400 pw.println(" Notification List:");
1401 for (int i=0; i<N; i++) {
1402 mNotificationList.get(i).dump(pw, " ", getContext());
1403 }
1404 pw.println(" ");
1405 }
1406
1407 N = mLights.size();
1408 if (N > 0) {
1409 pw.println(" Lights List:");
1410 for (int i=0; i<N; i++) {
1411 pw.println(" " + mLights.get(i));
1412 }
1413 pw.println(" ");
1414 }
1415
1416 pw.println(" mSoundNotification=" + mSoundNotification);
1417 pw.println(" mVibrateNotification=" + mVibrateNotification);
John Spurlocke677d712014-02-13 12:52:19 -05001418 pw.println(" mDisableNotificationAlerts=" + mDisableNotificationAlerts);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001419 pw.println(" mSystemReady=" + mSystemReady);
1420 pw.println(" mArchive=" + mArchive.toString());
1421 Iterator<StatusBarNotification> iter = mArchive.descendingIterator();
1422 int i=0;
1423 while (iter.hasNext()) {
1424 pw.println(" " + iter.next());
1425 if (++i >= 5) {
1426 if (iter.hasNext()) pw.println(" ...");
1427 break;
1428 }
1429 }
1430
Christoph Studer546bec82014-03-14 12:17:12 +01001431 pw.println("\n Usage Stats:");
1432 mUsageStats.dump(pw, " ");
1433
John Spurlock056c5192014-04-20 21:52:01 -04001434 pw.println("\n Zen Mode:");
1435 mZenModeHelper.dump(pw, " ");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001436 }
1437 }
1438
Adam Lesinski182f73f2013-12-05 16:48:06 -08001439 /**
1440 * The private API only accessible to the system process.
1441 */
1442 private final NotificationManagerInternal mInternalService = new NotificationManagerInternal() {
1443 @Override
Christoph Studer8fd7f1e2014-04-11 17:35:05 -04001444 public void enqueueNotification(String pkg, String opPkg, int callingUid, int callingPid,
Adam Lesinski182f73f2013-12-05 16:48:06 -08001445 String tag, int id, Notification notification, int[] idReceived, int userId) {
Christoph Studer8fd7f1e2014-04-11 17:35:05 -04001446 enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, notification,
Adam Lesinski182f73f2013-12-05 16:48:06 -08001447 idReceived, userId);
1448 }
1449 };
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001450
Christoph Studer8fd7f1e2014-04-11 17:35:05 -04001451 void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,
Scott Greenwald9b05c612013-06-25 23:44:05 -04001452 final int callingPid, final String tag, final int id, final Notification notification,
Adam Lesinski182f73f2013-12-05 16:48:06 -08001453 int[] idOut, int incomingUserId) {
Daniel Sandler0da673f2012-04-11 12:33:16 -04001454 if (DBG) {
Adam Lesinski182f73f2013-12-05 16:48:06 -08001455 Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id
1456 + " notification=" + notification);
Daniel Sandler0da673f2012-04-11 12:33:16 -04001457 }
John Spurlockb408e8e2014-04-23 21:12:45 -04001458 NotificationUtil.checkCallerIsSystemOrSameApp(pkg);
1459 final boolean isSystemNotification =
1460 NotificationUtil.isUidSystem(callingUid) || ("android".equals(pkg));
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001461
Scott Greenwald9b05c612013-06-25 23:44:05 -04001462 final int userId = ActivityManager.handleIncomingUser(callingPid,
1463 callingUid, incomingUserId, true, false, "enqueueNotification", pkg);
Jeff Sharkey65c4a2b2012-09-25 17:22:27 -07001464 final UserHandle user = new UserHandle(userId);
Dianne Hackborn41203752012-08-31 14:05:51 -07001465
Joe Onoratobd73d012010-06-04 11:44:54 -07001466 // Limit the number of notifications that any given package except the android
1467 // package can enqueue. Prevents DOS attacks and deals with leaks.
Daniel Sandler0da673f2012-04-11 12:33:16 -04001468 if (!isSystemNotification) {
Joe Onoratobd73d012010-06-04 11:44:54 -07001469 synchronized (mNotificationList) {
1470 int count = 0;
1471 final int N = mNotificationList.size();
1472 for (int i=0; i<N; i++) {
1473 final NotificationRecord r = mNotificationList.get(i);
Daniel Sandler4f91efd2013-04-25 16:38:41 -04001474 if (r.sbn.getPackageName().equals(pkg) && r.sbn.getUserId() == userId) {
Joe Onoratobd73d012010-06-04 11:44:54 -07001475 count++;
1476 if (count >= MAX_PACKAGE_NOTIFICATIONS) {
1477 Slog.e(TAG, "Package has already posted " + count
1478 + " notifications. Not showing more. package=" + pkg);
1479 return;
1480 }
1481 }
1482 }
1483 }
1484 }
1485
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001486 // This conditional is a dirty hack to limit the logging done on
1487 // behalf of the download manager without affecting other apps.
1488 if (!pkg.equals("com.android.providers.downloads")
1489 || Log.isLoggable("DownloadManager", Log.VERBOSE)) {
John Spurlocke6a7d932014-03-13 12:29:00 -04001490 EventLogTags.writeNotificationEnqueue(callingUid, callingPid,
1491 pkg, id, tag, userId, notification.toString());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001492 }
1493
1494 if (pkg == null || notification == null) {
1495 throw new IllegalArgumentException("null not allowed: pkg=" + pkg
1496 + " id=" + id + " notification=" + notification);
1497 }
1498 if (notification.icon != 0) {
1499 if (notification.contentView == null) {
1500 throw new IllegalArgumentException("contentView required: pkg=" + pkg
1501 + " id=" + id + " notification=" + notification);
1502 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001503 }
1504
Scott Greenwald9b05c612013-06-25 23:44:05 -04001505 mHandler.post(new Runnable() {
1506 @Override
1507 public void run() {
Daniel Sandler0da673f2012-04-11 12:33:16 -04001508
Scott Greenwald9b05c612013-06-25 23:44:05 -04001509 // === Scoring ===
Daniel Sandler0da673f2012-04-11 12:33:16 -04001510
Scott Greenwald9b05c612013-06-25 23:44:05 -04001511 // 0. Sanitize inputs
1512 notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN,
1513 Notification.PRIORITY_MAX);
1514 // Migrate notification flags to scores
1515 if (0 != (notification.flags & Notification.FLAG_HIGH_PRIORITY)) {
1516 if (notification.priority < Notification.PRIORITY_MAX) {
1517 notification.priority = Notification.PRIORITY_MAX;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001518 }
Scott Greenwald9b05c612013-06-25 23:44:05 -04001519 } else if (SCORE_ONGOING_HIGHER &&
1520 0 != (notification.flags & Notification.FLAG_ONGOING_EVENT)) {
1521 if (notification.priority < Notification.PRIORITY_HIGH) {
1522 notification.priority = Notification.PRIORITY_HIGH;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001523 }
1524 }
Daniel Sandlerfde19b12013-01-17 00:21:05 -05001525
Scott Greenwald9b05c612013-06-25 23:44:05 -04001526 // 1. initial score: buckets of 10, around the app
1527 int score = notification.priority * NOTIFICATION_PRIORITY_MULTIPLIER; //[-20..20]
1528
1529 // 2. Consult external heuristics (TBD)
1530
1531 // 3. Apply local rules
1532
Scott Greenwald9a05b312013-06-28 00:37:54 -04001533 int initialScore = score;
1534 if (!mScorers.isEmpty()) {
1535 if (DBG) Slog.v(TAG, "Initial score is " + score + ".");
1536 for (NotificationScorer scorer : mScorers) {
1537 try {
1538 score = scorer.getScore(notification, score);
1539 } catch (Throwable t) {
1540 Slog.w(TAG, "Scorer threw on .getScore.", t);
1541 }
1542 }
1543 if (DBG) Slog.v(TAG, "Final score is " + score + ".");
1544 }
1545
1546 // add extra to indicate score modified by NotificationScorer
1547 notification.extras.putBoolean(Notification.EXTRA_SCORE_MODIFIED,
1548 score != initialScore);
1549
Scott Greenwald9b05c612013-06-25 23:44:05 -04001550 // blocked apps
1551 if (ENABLE_BLOCKED_NOTIFICATIONS && !noteNotificationOp(pkg, callingUid)) {
1552 if (!isSystemNotification) {
1553 score = JUNK_SCORE;
1554 Slog.e(TAG, "Suppressing notification from package " + pkg
1555 + " by user request.");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001556 }
Daniel Sandlerd4d2de22012-11-14 11:25:46 -08001557 }
1558
Scott Greenwald9b05c612013-06-25 23:44:05 -04001559 if (DBG) {
1560 Slog.v(TAG, "Assigned score=" + score + " to " + notification);
1561 }
1562
1563 if (score < SCORE_DISPLAY_THRESHOLD) {
1564 // Notification will be blocked because the score is too low.
1565 return;
1566 }
1567
John Spurlocke677d712014-02-13 12:52:19 -05001568 // Is this notification intercepted by zen mode?
John Spurlock056c5192014-04-20 21:52:01 -04001569 final boolean intercept = mZenModeHelper.shouldIntercept(pkg, notification);
John Spurlocke677d712014-02-13 12:52:19 -05001570 notification.extras.putBoolean(EXTRA_INTERCEPT, intercept);
Scott Greenwald9b05c612013-06-25 23:44:05 -04001571
John Spurlocke677d712014-02-13 12:52:19 -05001572 // Should this notification make noise, vibe, or use the LED?
1573 final boolean canInterrupt = (score >= SCORE_INTERRUPTION_THRESHOLD) && !intercept;
1574 if (DBG) Slog.v(TAG, "canInterrupt=" + canInterrupt + " intercept=" + intercept);
Scott Greenwald9b05c612013-06-25 23:44:05 -04001575 synchronized (mNotificationList) {
1576 final StatusBarNotification n = new StatusBarNotification(
Christoph Studer8fd7f1e2014-04-11 17:35:05 -04001577 pkg, opPkg, id, tag, callingUid, callingPid, score, notification,
1578 user);
Scott Greenwald9b05c612013-06-25 23:44:05 -04001579 NotificationRecord r = new NotificationRecord(n);
1580 NotificationRecord old = null;
1581
1582 int index = indexOfNotificationLocked(pkg, tag, id, userId);
1583 if (index < 0) {
1584 mNotificationList.add(r);
Christoph Studer546bec82014-03-14 12:17:12 +01001585 mUsageStats.registerPostedByApp(r);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001586 } else {
Christoph Studerd89188d2014-04-03 00:02:39 +02001587 old = mNotificationList.get(index);
1588 mNotificationList.set(index, r);
Christoph Studer546bec82014-03-14 12:17:12 +01001589 mUsageStats.registerUpdatedByApp(r);
Scott Greenwald9b05c612013-06-25 23:44:05 -04001590 // Make sure we don't lose the foreground service state.
1591 if (old != null) {
1592 notification.flags |=
1593 old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;
1594 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001595 }
John Spurlocka4294292014-03-24 18:02:32 -04001596 if (old != null) {
1597 mNotificationsByKey.remove(old.sbn.getKey());
1598 }
1599 mNotificationsByKey.put(n.getKey(), r);
Scott Greenwald9b05c612013-06-25 23:44:05 -04001600
1601 // Ensure if this is a foreground service that the proper additional
1602 // flags are set.
1603 if ((notification.flags&Notification.FLAG_FOREGROUND_SERVICE) != 0) {
1604 notification.flags |= Notification.FLAG_ONGOING_EVENT
1605 | Notification.FLAG_NO_CLEAR;
1606 }
1607
1608 final int currentUser;
1609 final long token = Binder.clearCallingIdentity();
1610 try {
1611 currentUser = ActivityManager.getCurrentUser();
1612 } finally {
1613 Binder.restoreCallingIdentity(token);
1614 }
1615
1616 if (notification.icon != 0) {
1617 if (old != null && old.statusBarKey != null) {
1618 r.statusBarKey = old.statusBarKey;
Adam Lesinski182f73f2013-12-05 16:48:06 -08001619 final long identity = Binder.clearCallingIdentity();
Scott Greenwald9b05c612013-06-25 23:44:05 -04001620 try {
1621 mStatusBar.updateNotification(r.statusBarKey, n);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001622 } finally {
Scott Greenwald9b05c612013-06-25 23:44:05 -04001623 Binder.restoreCallingIdentity(identity);
1624 }
1625 } else {
Adam Lesinski182f73f2013-12-05 16:48:06 -08001626 final long identity = Binder.clearCallingIdentity();
Scott Greenwald9b05c612013-06-25 23:44:05 -04001627 try {
1628 r.statusBarKey = mStatusBar.addNotification(n);
1629 if ((n.getNotification().flags & Notification.FLAG_SHOW_LIGHTS) != 0
1630 && canInterrupt) {
1631 mAttentionLight.pulse();
1632 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001633 } finally {
Scott Greenwald9b05c612013-06-25 23:44:05 -04001634 Binder.restoreCallingIdentity(identity);
1635 }
1636 }
1637 // Send accessibility events only for the current user.
1638 if (currentUser == userId) {
1639 sendAccessibilityEvent(notification, pkg);
1640 }
1641
John Spurlockb408e8e2014-04-23 21:12:45 -04001642 mListeners.notifyPostedLocked(r.sbn);
Scott Greenwald9b05c612013-06-25 23:44:05 -04001643 } else {
1644 Slog.e(TAG, "Not posting notification with icon==0: " + notification);
1645 if (old != null && old.statusBarKey != null) {
Adam Lesinski182f73f2013-12-05 16:48:06 -08001646 final long identity = Binder.clearCallingIdentity();
Scott Greenwald9b05c612013-06-25 23:44:05 -04001647 try {
1648 mStatusBar.removeNotification(old.statusBarKey);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001649 } finally {
Scott Greenwald9b05c612013-06-25 23:44:05 -04001650 Binder.restoreCallingIdentity(identity);
1651 }
1652
John Spurlockb408e8e2014-04-23 21:12:45 -04001653 mListeners.notifyRemovedLocked(r.sbn);
Scott Greenwald9b05c612013-06-25 23:44:05 -04001654 }
1655 // ATTENTION: in a future release we will bail out here
Adam Lesinski182f73f2013-12-05 16:48:06 -08001656 // so that we do not play sounds, show lights, etc. for invalid
1657 // notifications
Scott Greenwald9b05c612013-06-25 23:44:05 -04001658 Slog.e(TAG, "WARNING: In a future release this will crash the app: "
1659 + n.getPackageName());
1660 }
1661
1662 // If we're not supposed to beep, vibrate, etc. then don't.
John Spurlocke677d712014-02-13 12:52:19 -05001663 if (!mDisableNotificationAlerts
Scott Greenwald9b05c612013-06-25 23:44:05 -04001664 && (!(old != null
1665 && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 ))
1666 && (r.getUserId() == UserHandle.USER_ALL ||
Kenny Guya263e4e2014-03-03 18:24:03 +00001667 (r.getUserId() == userId && r.getUserId() == currentUser) ||
John Spurlockb408e8e2014-04-23 21:12:45 -04001668 mUserProfiles.isCurrentProfile(r.getUserId()))
Scott Greenwald9b05c612013-06-25 23:44:05 -04001669 && canInterrupt
Adam Lesinski182f73f2013-12-05 16:48:06 -08001670 && mSystemReady
1671 && mAudioManager != null) {
John Spurlocke677d712014-02-13 12:52:19 -05001672 if (DBG) Slog.v(TAG, "Interrupting!");
Scott Greenwald9b05c612013-06-25 23:44:05 -04001673 // sound
1674
1675 // should we use the default notification sound? (indicated either by
1676 // DEFAULT_SOUND or because notification.sound is pointing at
1677 // Settings.System.NOTIFICATION_SOUND)
1678 final boolean useDefaultSound =
1679 (notification.defaults & Notification.DEFAULT_SOUND) != 0 ||
1680 Settings.System.DEFAULT_NOTIFICATION_URI
1681 .equals(notification.sound);
1682
1683 Uri soundUri = null;
1684 boolean hasValidSound = false;
1685
1686 if (useDefaultSound) {
1687 soundUri = Settings.System.DEFAULT_NOTIFICATION_URI;
1688
1689 // check to see if the default notification sound is silent
Adam Lesinski182f73f2013-12-05 16:48:06 -08001690 ContentResolver resolver = getContext().getContentResolver();
Scott Greenwald9b05c612013-06-25 23:44:05 -04001691 hasValidSound = Settings.System.getString(resolver,
1692 Settings.System.NOTIFICATION_SOUND) != null;
1693 } else if (notification.sound != null) {
1694 soundUri = notification.sound;
1695 hasValidSound = (soundUri != null);
1696 }
1697
1698 if (hasValidSound) {
Adam Lesinski182f73f2013-12-05 16:48:06 -08001699 boolean looping =
1700 (notification.flags & Notification.FLAG_INSISTENT) != 0;
Scott Greenwald9b05c612013-06-25 23:44:05 -04001701 int audioStreamType;
1702 if (notification.audioStreamType >= 0) {
1703 audioStreamType = notification.audioStreamType;
1704 } else {
1705 audioStreamType = DEFAULT_STREAM_TYPE;
1706 }
1707 mSoundNotification = r;
1708 // do not play notifications if stream volume is 0 (typically because
Jean-Michel Trivi23805662013-07-31 14:19:18 -07001709 // ringer mode is silent) or if there is a user of exclusive audio focus
Adam Lesinski182f73f2013-12-05 16:48:06 -08001710 if ((mAudioManager.getStreamVolume(audioStreamType) != 0)
1711 && !mAudioManager.isAudioFocusExclusive()) {
Scott Greenwald9b05c612013-06-25 23:44:05 -04001712 final long identity = Binder.clearCallingIdentity();
1713 try {
Adam Lesinski182f73f2013-12-05 16:48:06 -08001714 final IRingtonePlayer player =
1715 mAudioManager.getRingtonePlayer();
Scott Greenwald9b05c612013-06-25 23:44:05 -04001716 if (player != null) {
John Spurlocke677d712014-02-13 12:52:19 -05001717 if (DBG) Slog.v(TAG, "Playing sound " + soundUri
1718 + " on stream " + audioStreamType);
Scott Greenwald9b05c612013-06-25 23:44:05 -04001719 player.playAsync(soundUri, user, looping, audioStreamType);
1720 }
1721 } catch (RemoteException e) {
1722 } finally {
1723 Binder.restoreCallingIdentity(identity);
1724 }
1725 }
1726 }
1727
1728 // vibrate
1729 // Does the notification want to specify its own vibration?
1730 final boolean hasCustomVibrate = notification.vibrate != null;
1731
1732 // new in 4.2: if there was supposed to be a sound and we're in vibrate
1733 // mode, and no other vibration is specified, we fall back to vibration
1734 final boolean convertSoundToVibration =
1735 !hasCustomVibrate
1736 && hasValidSound
Adam Lesinski182f73f2013-12-05 16:48:06 -08001737 && (mAudioManager.getRingerMode()
Scott Greenwald9b05c612013-06-25 23:44:05 -04001738 == AudioManager.RINGER_MODE_VIBRATE);
1739
1740 // The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback.
1741 final boolean useDefaultVibrate =
1742 (notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
1743
1744 if ((useDefaultVibrate || convertSoundToVibration || hasCustomVibrate)
Adam Lesinski182f73f2013-12-05 16:48:06 -08001745 && !(mAudioManager.getRingerMode()
Scott Greenwald9b05c612013-06-25 23:44:05 -04001746 == AudioManager.RINGER_MODE_SILENT)) {
1747 mVibrateNotification = r;
1748
1749 if (useDefaultVibrate || convertSoundToVibration) {
1750 // Escalate privileges so we can use the vibrator even if the
1751 // notifying app does not have the VIBRATE permission.
1752 long identity = Binder.clearCallingIdentity();
1753 try {
Christoph Studer8fd7f1e2014-04-11 17:35:05 -04001754 mVibrator.vibrate(r.sbn.getUid(), r.sbn.getOpPkg(),
Scott Greenwald9b05c612013-06-25 23:44:05 -04001755 useDefaultVibrate ? mDefaultVibrationPattern
1756 : mFallbackVibrationPattern,
1757 ((notification.flags & Notification.FLAG_INSISTENT) != 0)
John Spurlock1af30c72014-03-10 08:33:35 -04001758 ? 0: -1, notification.audioStreamType);
Scott Greenwald9b05c612013-06-25 23:44:05 -04001759 } finally {
1760 Binder.restoreCallingIdentity(identity);
1761 }
1762 } else if (notification.vibrate.length > 1) {
1763 // If you want your own vibration pattern, you need the VIBRATE
1764 // permission
Christoph Studer8fd7f1e2014-04-11 17:35:05 -04001765 mVibrator.vibrate(r.sbn.getUid(), r.sbn.getOpPkg(),
Scott Greenwald9b05c612013-06-25 23:44:05 -04001766 notification.vibrate,
1767 ((notification.flags & Notification.FLAG_INSISTENT) != 0)
John Spurlock1af30c72014-03-10 08:33:35 -04001768 ? 0: -1, notification.audioStreamType);
Scott Greenwald9b05c612013-06-25 23:44:05 -04001769 }
1770 }
1771 }
1772
1773 // light
1774 // the most recent thing gets the light
1775 mLights.remove(old);
1776 if (mLedNotification == old) {
1777 mLedNotification = null;
1778 }
1779 //Slog.i(TAG, "notification.lights="
1780 // + ((old.notification.lights.flags & Notification.FLAG_SHOW_LIGHTS)
1781 // != 0));
1782 if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0
1783 && canInterrupt) {
1784 mLights.add(r);
1785 updateLightsLocked();
1786 } else {
1787 if (old != null
1788 && ((old.getFlags() & Notification.FLAG_SHOW_LIGHTS) != 0)) {
1789 updateLightsLocked();
Eric Laurent524dc042009-11-27 05:07:55 -08001790 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001791 }
1792 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001793 }
Scott Greenwald9b05c612013-06-25 23:44:05 -04001794 });
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001795
1796 idOut[0] = id;
1797 }
1798
Adam Lesinski182f73f2013-12-05 16:48:06 -08001799 void showNextToastLocked() {
1800 ToastRecord record = mToastQueue.get(0);
1801 while (record != null) {
1802 if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback);
1803 try {
1804 record.callback.show();
1805 scheduleTimeoutLocked(record);
1806 return;
1807 } catch (RemoteException e) {
1808 Slog.w(TAG, "Object died trying to show notification " + record.callback
1809 + " in package " + record.pkg);
1810 // remove it from the list and let the process die
1811 int index = mToastQueue.indexOf(record);
1812 if (index >= 0) {
1813 mToastQueue.remove(index);
1814 }
1815 keepProcessAliveLocked(record.pid);
1816 if (mToastQueue.size() > 0) {
1817 record = mToastQueue.get(0);
1818 } else {
1819 record = null;
1820 }
1821 }
1822 }
1823 }
1824
1825 void cancelToastLocked(int index) {
1826 ToastRecord record = mToastQueue.get(index);
1827 try {
1828 record.callback.hide();
1829 } catch (RemoteException e) {
1830 Slog.w(TAG, "Object died trying to hide notification " + record.callback
1831 + " in package " + record.pkg);
1832 // don't worry about this, we're about to remove it from
1833 // the list anyway
1834 }
1835 mToastQueue.remove(index);
1836 keepProcessAliveLocked(record.pid);
1837 if (mToastQueue.size() > 0) {
1838 // Show the next one. If the callback fails, this will remove
1839 // it from the list, so don't assume that the list hasn't changed
1840 // after this point.
1841 showNextToastLocked();
1842 }
1843 }
1844
1845 private void scheduleTimeoutLocked(ToastRecord r)
1846 {
1847 mHandler.removeCallbacksAndMessages(r);
1848 Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r);
1849 long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY;
1850 mHandler.sendMessageDelayed(m, delay);
1851 }
1852
1853 private void handleTimeout(ToastRecord record)
1854 {
1855 if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback);
1856 synchronized (mToastQueue) {
1857 int index = indexOfToastLocked(record.pkg, record.callback);
1858 if (index >= 0) {
1859 cancelToastLocked(index);
1860 }
1861 }
1862 }
1863
1864 // lock on mToastQueue
1865 int indexOfToastLocked(String pkg, ITransientNotification callback)
1866 {
1867 IBinder cbak = callback.asBinder();
1868 ArrayList<ToastRecord> list = mToastQueue;
1869 int len = list.size();
1870 for (int i=0; i<len; i++) {
1871 ToastRecord r = list.get(i);
1872 if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) {
1873 return i;
1874 }
1875 }
1876 return -1;
1877 }
1878
1879 // lock on mToastQueue
1880 void keepProcessAliveLocked(int pid)
1881 {
1882 int toastCount = 0; // toasts from this pid
1883 ArrayList<ToastRecord> list = mToastQueue;
1884 int N = list.size();
1885 for (int i=0; i<N; i++) {
1886 ToastRecord r = list.get(i);
1887 if (r.pid == pid) {
1888 toastCount++;
1889 }
1890 }
1891 try {
1892 mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0);
1893 } catch (RemoteException e) {
1894 // Shouldn't happen.
1895 }
1896 }
1897
1898 private final class WorkerHandler extends Handler
1899 {
1900 @Override
1901 public void handleMessage(Message msg)
1902 {
1903 switch (msg.what)
1904 {
1905 case MESSAGE_TIMEOUT:
1906 handleTimeout((ToastRecord)msg.obj);
1907 break;
John Spurlock056c5192014-04-20 21:52:01 -04001908 case MESSAGE_SAVE_POLICY_FILE:
1909 handleSavePolicyFile();
1910 break;
Adam Lesinski182f73f2013-12-05 16:48:06 -08001911 }
1912 }
1913 }
1914
1915
1916 // Notifications
1917 // ============================================================================
1918 static int clamp(int x, int low, int high) {
1919 return (x < low) ? low : ((x > high) ? high : x);
1920 }
1921
1922 void sendAccessibilityEvent(Notification notification, CharSequence packageName) {
1923 AccessibilityManager manager = AccessibilityManager.getInstance(getContext());
svetoslavganov75986cf2009-05-14 22:28:01 -07001924 if (!manager.isEnabled()) {
1925 return;
1926 }
1927
1928 AccessibilityEvent event =
1929 AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
1930 event.setPackageName(packageName);
1931 event.setClassName(Notification.class.getName());
1932 event.setParcelableData(notification);
1933 CharSequence tickerText = notification.tickerText;
1934 if (!TextUtils.isEmpty(tickerText)) {
1935 event.getText().add(tickerText);
1936 }
1937
1938 manager.sendAccessibilityEvent(event);
1939 }
1940
Christoph Studer546bec82014-03-14 12:17:12 +01001941 private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason) {
Joe Onorato46439ce2010-11-19 13:56:21 -08001942 // tell the app
1943 if (sendDelete) {
Daniel Sandlerfde19b12013-01-17 00:21:05 -05001944 if (r.getNotification().deleteIntent != null) {
Joe Onorato46439ce2010-11-19 13:56:21 -08001945 try {
Daniel Sandlerfde19b12013-01-17 00:21:05 -05001946 r.getNotification().deleteIntent.send();
Joe Onorato46439ce2010-11-19 13:56:21 -08001947 } catch (PendingIntent.CanceledException ex) {
1948 // do nothing - there's no relevant way to recover, and
1949 // no reason to let this propagate
Daniel Sandler4f91efd2013-04-25 16:38:41 -04001950 Slog.w(TAG, "canceled PendingIntent for " + r.sbn.getPackageName(), ex);
Joe Onorato46439ce2010-11-19 13:56:21 -08001951 }
1952 }
1953 }
1954
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001955 // status bar
Daniel Sandlerfde19b12013-01-17 00:21:05 -05001956 if (r.getNotification().icon != 0) {
Adam Lesinski182f73f2013-12-05 16:48:06 -08001957 final long identity = Binder.clearCallingIdentity();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001958 try {
Joe Onorato0cbda992010-05-02 16:28:15 -07001959 mStatusBar.removeNotification(r.statusBarKey);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001960 } finally {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001961 Binder.restoreCallingIdentity(identity);
1962 }
1963 r.statusBarKey = null;
John Spurlockb408e8e2014-04-23 21:12:45 -04001964 mListeners.notifyRemovedLocked(r.sbn);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001965 }
1966
1967 // sound
1968 if (mSoundNotification == r) {
1969 mSoundNotification = null;
Jeff Sharkey098d5802012-04-26 17:30:34 -07001970 final long identity = Binder.clearCallingIdentity();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001971 try {
Adam Lesinski182f73f2013-12-05 16:48:06 -08001972 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
Jeff Sharkey098d5802012-04-26 17:30:34 -07001973 if (player != null) {
1974 player.stopAsync();
1975 }
1976 } catch (RemoteException e) {
1977 } finally {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001978 Binder.restoreCallingIdentity(identity);
1979 }
1980 }
1981
1982 // vibrate
1983 if (mVibrateNotification == r) {
1984 mVibrateNotification = null;
1985 long identity = Binder.clearCallingIdentity();
1986 try {
1987 mVibrator.cancel();
1988 }
1989 finally {
1990 Binder.restoreCallingIdentity(identity);
1991 }
1992 }
1993
1994 // light
1995 mLights.remove(r);
1996 if (mLedNotification == r) {
1997 mLedNotification = null;
1998 }
Daniel Sandler23d7c702013-03-07 16:32:06 -05001999
Christoph Studer546bec82014-03-14 12:17:12 +01002000 // Record usage stats
2001 switch (reason) {
2002 case REASON_DELEGATE_CANCEL:
2003 case REASON_DELEGATE_CANCEL_ALL:
2004 case REASON_LISTENER_CANCEL:
2005 case REASON_LISTENER_CANCEL_ALL:
2006 mUsageStats.registerDismissedByUser(r);
2007 break;
2008 case REASON_NOMAN_CANCEL:
2009 case REASON_NOMAN_CANCEL_ALL:
2010 mUsageStats.registerRemovedByApp(r);
2011 break;
2012 case REASON_DELEGATE_CLICK:
2013 mUsageStats.registerCancelDueToClick(r);
2014 break;
2015 default:
2016 mUsageStats.registerCancelUnknown(r);
2017 break;
2018 }
2019
Daniel Sandler23d7c702013-03-07 16:32:06 -05002020 // Save it for users of getHistoricalNotifications()
2021 mArchive.record(r.sbn);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002022 }
2023
2024 /**
Dianne Hackbornd8a43f62009-08-17 23:33:56 -07002025 * Cancels a notification ONLY if it has all of the {@code mustHaveFlags}
Doug Zongkerab5c49c2009-12-04 10:31:43 -08002026 * and none of the {@code mustNotHaveFlags}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002027 */
John Spurlocke6a7d932014-03-13 12:29:00 -04002028 void cancelNotification(final int callingUid, final int callingPid,
2029 final String pkg, final String tag, final int id,
Svetoslav Ganov835835e2013-08-04 20:17:52 -07002030 final int mustHaveFlags, final int mustNotHaveFlags, final boolean sendDelete,
John Spurlocke6a7d932014-03-13 12:29:00 -04002031 final int userId, final int reason, final NotificationListenerInfo listener) {
Svetoslav Ganov835835e2013-08-04 20:17:52 -07002032 // In enqueueNotificationInternal notifications are added by scheduling the
2033 // work on the worker handler. Hence, we also schedule the cancel on this
2034 // handler to avoid a scenario where an add notification call followed by a
2035 // remove notification call ends up in not removing the notification.
2036 mHandler.post(new Runnable() {
2037 @Override
2038 public void run() {
John Spurlocke6a7d932014-03-13 12:29:00 -04002039 EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, id, tag, userId,
2040 mustHaveFlags, mustNotHaveFlags, reason,
2041 listener == null ? null : listener.component.toShortString());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002042
Svetoslav Ganov835835e2013-08-04 20:17:52 -07002043 synchronized (mNotificationList) {
2044 int index = indexOfNotificationLocked(pkg, tag, id, userId);
2045 if (index >= 0) {
2046 NotificationRecord r = mNotificationList.get(index);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08002047
Christoph Studer546bec82014-03-14 12:17:12 +01002048 // Ideally we'd do this in the caller of this method. However, that would
2049 // require the caller to also find the notification.
2050 if (reason == REASON_DELEGATE_CLICK) {
2051 mUsageStats.registerClickedByUser(r);
2052 }
2053
Svetoslav Ganov835835e2013-08-04 20:17:52 -07002054 if ((r.getNotification().flags & mustHaveFlags) != mustHaveFlags) {
2055 return;
2056 }
2057 if ((r.getNotification().flags & mustNotHaveFlags) != 0) {
2058 return;
2059 }
2060
2061 mNotificationList.remove(index);
John Spurlocka4294292014-03-24 18:02:32 -04002062 mNotificationsByKey.remove(r.sbn.getKey());
Svetoslav Ganov835835e2013-08-04 20:17:52 -07002063
Christoph Studer546bec82014-03-14 12:17:12 +01002064 cancelNotificationLocked(r, sendDelete, reason);
Svetoslav Ganov835835e2013-08-04 20:17:52 -07002065 updateLightsLocked();
2066 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002067 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002068 }
Svetoslav Ganov835835e2013-08-04 20:17:52 -07002069 });
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002070 }
2071
2072 /**
Daniel Sandler321e9c52012-10-12 10:59:26 -07002073 * Determine whether the userId applies to the notification in question, either because
2074 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard).
2075 */
2076 private boolean notificationMatchesUserId(NotificationRecord r, int userId) {
2077 return
2078 // looking for USER_ALL notifications? match everything
2079 userId == UserHandle.USER_ALL
2080 // a notification sent to USER_ALL matches any query
Daniel Sandlerfde19b12013-01-17 00:21:05 -05002081 || r.getUserId() == UserHandle.USER_ALL
Daniel Sandler321e9c52012-10-12 10:59:26 -07002082 // an exact user match
Daniel Sandlerfde19b12013-01-17 00:21:05 -05002083 || r.getUserId() == userId;
Daniel Sandler321e9c52012-10-12 10:59:26 -07002084 }
2085
2086 /**
Kenny Guy3a7c4a52014-03-03 18:24:03 +00002087 * Determine whether the userId applies to the notification in question, either because
2088 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard) or
Kenny Guy2a764942014-04-02 13:29:20 +01002089 * because it matches one of the users profiles.
Kenny Guy3a7c4a52014-03-03 18:24:03 +00002090 */
Kenny Guy2a764942014-04-02 13:29:20 +01002091 private boolean notificationMatchesCurrentProfiles(NotificationRecord r, int userId) {
Kenny Guya263e4e2014-03-03 18:24:03 +00002092 return notificationMatchesUserId(r, userId)
John Spurlockb408e8e2014-04-23 21:12:45 -04002093 || mUserProfiles.isCurrentProfile(r.getUserId());
Kenny Guy3a7c4a52014-03-03 18:24:03 +00002094 }
2095
2096 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002097 * Cancels all notifications from a given package that have all of the
2098 * {@code mustHaveFlags}.
2099 */
John Spurlocke6a7d932014-03-13 12:29:00 -04002100 boolean cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, int mustHaveFlags,
2101 int mustNotHaveFlags, boolean doit, int userId, int reason,
2102 NotificationListenerInfo listener) {
2103 EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
2104 pkg, userId, mustHaveFlags, mustNotHaveFlags, reason,
2105 listener == null ? null : listener.component.toShortString());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002106
2107 synchronized (mNotificationList) {
2108 final int N = mNotificationList.size();
2109 boolean canceledSomething = false;
2110 for (int i = N-1; i >= 0; --i) {
2111 NotificationRecord r = mNotificationList.get(i);
Daniel Sandler321e9c52012-10-12 10:59:26 -07002112 if (!notificationMatchesUserId(r, userId)) {
Dianne Hackborn41203752012-08-31 14:05:51 -07002113 continue;
2114 }
Amith Yamasani5ec00e92012-11-07 16:58:30 -08002115 // Don't remove notifications to all, if there's no package name specified
Daniel Sandlerfde19b12013-01-17 00:21:05 -05002116 if (r.getUserId() == UserHandle.USER_ALL && pkg == null) {
Amith Yamasani5ec00e92012-11-07 16:58:30 -08002117 continue;
2118 }
Daniel Sandlerfde19b12013-01-17 00:21:05 -05002119 if ((r.getFlags() & mustHaveFlags) != mustHaveFlags) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002120 continue;
2121 }
Daniel Sandlerfde19b12013-01-17 00:21:05 -05002122 if ((r.getFlags() & mustNotHaveFlags) != 0) {
Dianne Hackbornd8a43f62009-08-17 23:33:56 -07002123 continue;
2124 }
Daniel Sandler4f91efd2013-04-25 16:38:41 -04002125 if (pkg != null && !r.sbn.getPackageName().equals(pkg)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002126 continue;
2127 }
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08002128 canceledSomething = true;
2129 if (!doit) {
2130 return true;
2131 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002132 mNotificationList.remove(i);
John Spurlocka4294292014-03-24 18:02:32 -04002133 mNotificationsByKey.remove(r.sbn.getKey());
Christoph Studer546bec82014-03-14 12:17:12 +01002134 cancelNotificationLocked(r, false, reason);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002135 }
2136 if (canceledSomething) {
2137 updateLightsLocked();
2138 }
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08002139 return canceledSomething;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002140 }
2141 }
2142
Adam Lesinski350159c2014-03-27 11:15:11 -07002143 void cancelAllLocked(int callingUid, int callingPid, int userId, int reason,
Kenny Guya263e4e2014-03-03 18:24:03 +00002144 NotificationListenerInfo listener, boolean includeCurrentProfiles) {
John Spurlocke6a7d932014-03-13 12:29:00 -04002145 EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
2146 null, userId, 0, 0, reason,
2147 listener == null ? null : listener.component.toShortString());
Christoph Studer546bec82014-03-14 12:17:12 +01002148
Adam Lesinskie8240262014-03-26 16:01:00 -07002149 final int N = mNotificationList.size();
2150 for (int i=N-1; i>=0; i--) {
2151 NotificationRecord r = mNotificationList.get(i);
Kenny Guya263e4e2014-03-03 18:24:03 +00002152 if (includeCurrentProfiles) {
2153 if (!notificationMatchesCurrentProfiles(r, userId)) {
2154 continue;
2155 }
2156 } else {
2157 if (!notificationMatchesUserId(r, userId)) {
2158 continue;
2159 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002160 }
2161
Adam Lesinskie8240262014-03-26 16:01:00 -07002162 if ((r.getFlags() & (Notification.FLAG_ONGOING_EVENT
2163 | Notification.FLAG_NO_CLEAR)) == 0) {
2164 mNotificationList.remove(i);
John Spurlocka4294292014-03-24 18:02:32 -04002165 mNotificationsByKey.remove(r.sbn.getKey());
Christoph Studer546bec82014-03-14 12:17:12 +01002166 cancelNotificationLocked(r, true, reason);
Adam Lesinskie8240262014-03-26 16:01:00 -07002167 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002168 }
Adam Lesinskie8240262014-03-26 16:01:00 -07002169 updateLightsLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002170 }
2171
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002172 // lock on mNotificationList
Adam Lesinski182f73f2013-12-05 16:48:06 -08002173 void updateLightsLocked()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002174 {
The Android Open Source Project10592532009-03-18 17:39:46 -07002175 // handle notification lights
2176 if (mLedNotification == null) {
2177 // get next notification, if any
2178 int n = mLights.size();
2179 if (n > 0) {
2180 mLedNotification = mLights.get(n-1);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002181 }
2182 }
Mike Lockwoodc22404a2009-12-02 11:15:02 -05002183
Mike Lockwood63b5ad92011-08-30 09:55:30 -04002184 // Don't flash while we are in a call or screen is on
2185 if (mLedNotification == null || mInCall || mScreenOn) {
Mike Lockwood3cb67a32009-11-27 14:25:58 -05002186 mNotificationLight.turnOff();
The Android Open Source Project10592532009-03-18 17:39:46 -07002187 } else {
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -04002188 final Notification ledno = mLedNotification.sbn.getNotification();
Daniel Sandlerfde19b12013-01-17 00:21:05 -05002189 int ledARGB = ledno.ledARGB;
2190 int ledOnMS = ledno.ledOnMS;
2191 int ledOffMS = ledno.ledOffMS;
2192 if ((ledno.defaults & Notification.DEFAULT_LIGHTS) != 0) {
Mike Lockwood670f9322010-01-20 12:13:36 -05002193 ledARGB = mDefaultNotificationColor;
2194 ledOnMS = mDefaultNotificationLedOn;
2195 ledOffMS = mDefaultNotificationLedOff;
2196 }
2197 if (mNotificationPulseEnabled) {
2198 // pulse repeatedly
Adam Lesinski182f73f2013-12-05 16:48:06 -08002199 mNotificationLight.setFlashing(ledARGB, Light.LIGHT_FLASH_TIMED,
Mike Lockwood670f9322010-01-20 12:13:36 -05002200 ledOnMS, ledOffMS);
Mike Lockwood670f9322010-01-20 12:13:36 -05002201 }
The Android Open Source Project10592532009-03-18 17:39:46 -07002202 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002203 }
2204
2205 // lock on mNotificationList
Adam Lesinski182f73f2013-12-05 16:48:06 -08002206 int indexOfNotificationLocked(String pkg, String tag, int id, int userId)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002207 {
2208 ArrayList<NotificationRecord> list = mNotificationList;
2209 final int len = list.size();
2210 for (int i=0; i<len; i++) {
2211 NotificationRecord r = list.get(i);
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -04002212 if (!notificationMatchesUserId(r, userId) || r.sbn.getId() != id) {
Dianne Hackborn41203752012-08-31 14:05:51 -07002213 continue;
2214 }
Fred Quintana6ecaff12009-09-25 14:23:13 -07002215 if (tag == null) {
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -04002216 if (r.sbn.getTag() != null) {
Fred Quintana6ecaff12009-09-25 14:23:13 -07002217 continue;
2218 }
2219 } else {
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -04002220 if (!tag.equals(r.sbn.getTag())) {
Fred Quintana6ecaff12009-09-25 14:23:13 -07002221 continue;
2222 }
2223 }
Daniel Sandler4f91efd2013-04-25 16:38:41 -04002224 if (r.sbn.getPackageName().equals(pkg)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002225 return i;
2226 }
2227 }
2228 return -1;
2229 }
2230
Mike Lockwoodc22404a2009-12-02 11:15:02 -05002231 private void updateNotificationPulse() {
2232 synchronized (mNotificationList) {
2233 updateLightsLocked();
2234 }
2235 }
John Spurlocke677d712014-02-13 12:52:19 -05002236
John Spurlockb408e8e2014-04-23 21:12:45 -04002237 public static class UserProfiles {
2238 // Profiles of the current user.
2239 private final SparseArray<UserInfo> mCurrentProfiles = new SparseArray<UserInfo>();
2240
2241 private void updateCache(Context context) {
2242 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
2243 if (userManager != null) {
2244 int currentUserId = ActivityManager.getCurrentUser();
2245 List<UserInfo> profiles = userManager.getProfiles(currentUserId);
2246 synchronized (mCurrentProfiles) {
2247 mCurrentProfiles.clear();
2248 for (UserInfo user : profiles) {
2249 mCurrentProfiles.put(user.id, user);
2250 }
Kenny Guy3a7c4a52014-03-03 18:24:03 +00002251 }
2252 }
2253 }
Kenny Guy3a7c4a52014-03-03 18:24:03 +00002254
John Spurlockb408e8e2014-04-23 21:12:45 -04002255 public int[] getCurrentProfileIds() {
2256 synchronized (mCurrentProfiles) {
2257 int[] users = new int[mCurrentProfiles.size()];
2258 final int N = mCurrentProfiles.size();
2259 for (int i = 0; i < N; ++i) {
2260 users[i] = mCurrentProfiles.keyAt(i);
2261 }
2262 return users;
Kenny Guya263e4e2014-03-03 18:24:03 +00002263 }
Kenny Guya263e4e2014-03-03 18:24:03 +00002264 }
Kenny Guya263e4e2014-03-03 18:24:03 +00002265
John Spurlockb408e8e2014-04-23 21:12:45 -04002266 public boolean isCurrentProfile(int userId) {
2267 synchronized (mCurrentProfiles) {
2268 return mCurrentProfiles.get(userId) != null;
2269 }
Kenny Guya263e4e2014-03-03 18:24:03 +00002270 }
2271 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002272}