blob: 2f1d29196692dd7448dfdcf4619a01d386f805cc [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;
John Spurlock7340fc82014-04-24 18:50:12 -040025import android.app.AppGlobals;
Daniel Sandler4a900ac2013-01-30 14:04:10 -050026import android.app.AppOpsManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080027import android.app.IActivityManager;
28import android.app.INotificationManager;
29import android.app.ITransientNotification;
30import android.app.Notification;
31import android.app.PendingIntent;
32import android.app.StatusBarManager;
33import android.content.BroadcastReceiver;
Daniel Sandler5feceeb2013-03-22 18:29:23 -070034import android.content.ComponentName;
Dianne Hackborn1dac2772009-06-26 18:16:48 -070035import android.content.ContentResolver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080036import android.content.Context;
37import android.content.Intent;
38import android.content.IntentFilter;
John Spurlock7340fc82014-04-24 18:50:12 -040039import android.content.pm.ApplicationInfo;
Daniel Sandler4a900ac2013-01-30 14:04:10 -050040import android.content.pm.PackageInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080041import android.content.pm.PackageManager;
42import android.content.pm.PackageManager.NameNotFoundException;
43import android.content.res.Resources;
Dianne Hackborn1dac2772009-06-26 18:16:48 -070044import android.database.ContentObserver;
Daniel Sandlerf45564e2013-04-15 15:05:08 -040045import android.graphics.Bitmap;
svetoslavganov75986cf2009-05-14 22:28:01 -070046import android.media.AudioManager;
Jeff Sharkey098d5802012-04-26 17:30:34 -070047import android.media.IRingtonePlayer;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080048import android.net.Uri;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080049import android.os.Binder;
John Spurlock056c5192014-04-20 21:52:01 -040050import android.os.Environment;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080051import android.os.Handler;
Chris Wrenf9536642014-04-17 10:01:54 -040052import android.os.HandlerThread;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080053import android.os.IBinder;
John Spurlock7340fc82014-04-24 18:50:12 -040054import android.os.IInterface;
Chris Wrenf9536642014-04-17 10:01:54 -040055import android.os.Looper;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080056import android.os.Message;
Dianne Hackbornd8a43f62009-08-17 23:33:56 -070057import android.os.Process;
svetoslavganov75986cf2009-05-14 22:28:01 -070058import android.os.RemoteException;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -070059import android.os.UserHandle;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080060import android.os.Vibrator;
61import android.provider.Settings;
Daniel Sandler5feceeb2013-03-22 18:29:23 -070062import android.service.notification.INotificationListener;
John Spurlocke77bb362014-04-26 10:24:59 -040063import android.service.notification.IConditionListener;
John Spurlock7340fc82014-04-24 18:50:12 -040064import android.service.notification.IConditionProvider;
65import android.service.notification.NotificationListenerService;
Chris Wrenf9536642014-04-17 10:01:54 -040066import android.service.notification.NotificationOrderUpdate;
Daniel Sandler5feceeb2013-03-22 18:29:23 -070067import android.service.notification.StatusBarNotification;
John Spurlock7340fc82014-04-24 18:50:12 -040068import android.service.notification.Condition;
John Spurlock056c5192014-04-20 21:52:01 -040069import android.service.notification.ZenModeConfig;
Daniel Sandlere96ffb12010-03-11 13:38:06 -050070import android.telephony.TelephonyManager;
svetoslavganov75986cf2009-05-14 22:28:01 -070071import android.text.TextUtils;
John Spurlocka4294292014-03-24 18:02:32 -040072import android.util.ArrayMap;
Dianne Hackborn39606a02012-07-31 17:54:35 -070073import android.util.AtomicFile;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080074import android.util.Log;
Andy Stadler110988c2010-12-03 14:29:16 -080075import android.util.Slog;
Daniel Sandler0da673f2012-04-11 12:33:16 -040076import android.util.Xml;
svetoslavganov75986cf2009-05-14 22:28:01 -070077import android.view.accessibility.AccessibilityEvent;
78import android.view.accessibility.AccessibilityManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080079import android.widget.Toast;
80
Scott Greenwald9a05b312013-06-28 00:37:54 -040081import com.android.internal.R;
John Spurlock056c5192014-04-20 21:52:01 -040082import com.android.internal.util.FastXmlSerializer;
Adam Lesinski182f73f2013-12-05 16:48:06 -080083import com.android.server.EventLogTags;
Adam Lesinski182f73f2013-12-05 16:48:06 -080084import com.android.server.SystemService;
85import com.android.server.lights.Light;
86import com.android.server.lights.LightsManager;
John Spurlock7340fc82014-04-24 18:50:12 -040087import com.android.server.notification.ManagedServices.ManagedServiceInfo;
88import com.android.server.notification.ManagedServices.UserProfiles;
John Spurlockb408e8e2014-04-23 21:12:45 -040089import com.android.server.notification.NotificationUsageStats.SingleNotificationStats;
90import com.android.server.statusbar.StatusBarManagerInternal;
91
92import libcore.io.IoUtils;
Adam Lesinski182f73f2013-12-05 16:48:06 -080093
Jeff Sharkey098d5802012-04-26 17:30:34 -070094import org.xmlpull.v1.XmlPullParser;
95import org.xmlpull.v1.XmlPullParserException;
John Spurlock056c5192014-04-20 21:52:01 -040096import org.xmlpull.v1.XmlSerializer;
Jeff Sharkey098d5802012-04-26 17:30:34 -070097
Daniel Sandler0da673f2012-04-11 12:33:16 -040098import java.io.File;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080099import java.io.FileDescriptor;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400100import java.io.FileInputStream;
101import java.io.FileNotFoundException;
John Spurlock056c5192014-04-20 21:52:01 -0400102import java.io.FileOutputStream;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400103import java.io.IOException;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800104import java.io.PrintWriter;
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400105import java.lang.reflect.Array;
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500106import java.util.ArrayDeque;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800107import java.util.ArrayList;
108import java.util.Arrays;
Chris Wrenf9536642014-04-17 10:01:54 -0400109import java.util.Collections;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400110import java.util.HashSet;
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500111import java.util.Iterator;
112import java.util.NoSuchElementException;
Chris Wrenf9536642014-04-17 10:01:54 -0400113import java.util.concurrent.ExecutionException;
114import java.util.concurrent.TimeUnit;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400115
Daniel Sandlerd0a2f862010-08-03 15:29:31 -0400116/** {@hide} */
Adam Lesinski182f73f2013-12-05 16:48:06 -0800117public class NotificationManagerService extends SystemService {
118 static final String TAG = "NotificationService";
119 static final boolean DBG = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800120
Adam Lesinski182f73f2013-12-05 16:48:06 -0800121 static final int MAX_PACKAGE_NOTIFICATIONS = 50;
Joe Onoratobd73d012010-06-04 11:44:54 -0700122
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800123 // message codes
Adam Lesinski182f73f2013-12-05 16:48:06 -0800124 static final int MESSAGE_TIMEOUT = 2;
John Spurlock056c5192014-04-20 21:52:01 -0400125 static final int MESSAGE_SAVE_POLICY_FILE = 3;
Chris Wrenf9536642014-04-17 10:01:54 -0400126 static final int MESSAGE_RECONSIDER_RANKING = 4;
127 static final int MESSAGE_SEND_RANKING_UPDATE = 5;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800128
Adam Lesinski182f73f2013-12-05 16:48:06 -0800129 static final int LONG_DELAY = 3500; // 3.5 seconds
130 static final int SHORT_DELAY = 2000; // 2 seconds
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800131
Adam Lesinski182f73f2013-12-05 16:48:06 -0800132 static final long[] DEFAULT_VIBRATE_PATTERN = {0, 250, 250, 250};
133 static final int VIBRATE_PATTERN_MAXLEN = 8 * 2 + 1; // up to eight bumps
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800134
Adam Lesinski182f73f2013-12-05 16:48:06 -0800135 static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_NOTIFICATION;
136 static final boolean SCORE_ONGOING_HIGHER = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800137
Adam Lesinski182f73f2013-12-05 16:48:06 -0800138 static final int JUNK_SCORE = -1000;
139 static final int NOTIFICATION_PRIORITY_MULTIPLIER = 10;
140 static final int SCORE_DISPLAY_THRESHOLD = Notification.PRIORITY_MIN * NOTIFICATION_PRIORITY_MULTIPLIER;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400141
Daniel Sandler526fa0e2012-12-04 14:51:50 -0500142 // Notifications with scores below this will not interrupt the user, either via LED or
143 // sound or vibration
Adam Lesinski182f73f2013-12-05 16:48:06 -0800144 static final int SCORE_INTERRUPTION_THRESHOLD =
Daniel Sandler526fa0e2012-12-04 14:51:50 -0500145 Notification.PRIORITY_LOW * NOTIFICATION_PRIORITY_MULTIPLIER;
146
Adam Lesinski182f73f2013-12-05 16:48:06 -0800147 static final boolean ENABLE_BLOCKED_NOTIFICATIONS = true;
148 static final boolean ENABLE_BLOCKED_TOASTS = true;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400149
Adam Lesinski182f73f2013-12-05 16:48:06 -0800150 private IActivityManager mAm;
151 AudioManager mAudioManager;
152 StatusBarManagerInternal mStatusBar;
153 Vibrator mVibrator;
154
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800155 final IBinder mForegroundToken = new Binder();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800156 private WorkerHandler mHandler;
Chris Wrenf9536642014-04-17 10:01:54 -0400157 private final HandlerThread mRankingThread = new HandlerThread("ranker",
158 Process.THREAD_PRIORITY_BACKGROUND);
159 private Handler mRankingHandler = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800160
Adam Lesinski182f73f2013-12-05 16:48:06 -0800161 private Light mNotificationLight;
162 Light mAttentionLight;
Mike Lockwood670f9322010-01-20 12:13:36 -0500163 private int mDefaultNotificationColor;
164 private int mDefaultNotificationLedOn;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800165
Mike Lockwood670f9322010-01-20 12:13:36 -0500166 private int mDefaultNotificationLedOff;
Daniel Sandleredbb3802012-11-13 20:49:47 -0800167 private long[] mDefaultVibrationPattern;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800168
Daniel Sandleredbb3802012-11-13 20:49:47 -0800169 private long[] mFallbackVibrationPattern;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800170 boolean mSystemReady;
Daniel Sandleredbb3802012-11-13 20:49:47 -0800171
John Spurlocke677d712014-02-13 12:52:19 -0500172 private boolean mDisableNotificationAlerts;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800173 NotificationRecord mSoundNotification;
174 NotificationRecord mVibrateNotification;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800175
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500176 // for enabling and disabling notification pulse behavior
Mike Lockwood63b5ad92011-08-30 09:55:30 -0400177 private boolean mScreenOn = true;
Daniel Sandlere96ffb12010-03-11 13:38:06 -0500178 private boolean mInCall = false;
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500179 private boolean mNotificationPulseEnabled;
180
Daniel Sandler09a247e2013-02-14 10:24:17 -0500181 // used as a mutex for access to all active notifications & listeners
Adam Lesinski182f73f2013-12-05 16:48:06 -0800182 final ArrayList<NotificationRecord> mNotificationList =
Fred Quintana6ecaff12009-09-25 14:23:13 -0700183 new ArrayList<NotificationRecord>();
Chris Wrenf9536642014-04-17 10:01:54 -0400184 final NotificationComparator mRankingComparator = new NotificationComparator();
John Spurlocka4294292014-03-24 18:02:32 -0400185 final ArrayMap<String, NotificationRecord> mNotificationsByKey =
186 new ArrayMap<String, NotificationRecord>();
Adam Lesinski182f73f2013-12-05 16:48:06 -0800187 final ArrayList<ToastRecord> mToastQueue = new ArrayList<ToastRecord>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800188
Adam Lesinski182f73f2013-12-05 16:48:06 -0800189 ArrayList<NotificationRecord> mLights = new ArrayList<NotificationRecord>();
190 NotificationRecord mLedNotification;
svetoslavganov75986cf2009-05-14 22:28:01 -0700191
Adam Lesinski182f73f2013-12-05 16:48:06 -0800192 private AppOpsManager mAppOps;
Daniel Sandler4a900ac2013-01-30 14:04:10 -0500193
Daniel Sandler0da673f2012-04-11 12:33:16 -0400194 // Notification control database. For now just contains disabled packages.
195 private AtomicFile mPolicyFile;
196 private HashSet<String> mBlockedPackages = new HashSet<String>();
197
198 private static final int DB_VERSION = 1;
199
200 private static final String TAG_BODY = "notification-policy";
201 private static final String ATTR_VERSION = "version";
202
203 private static final String TAG_BLOCKED_PKGS = "blocked-packages";
204 private static final String TAG_PACKAGE = "package";
205 private static final String ATTR_NAME = "name";
206
Chris Wrenf9536642014-04-17 10:01:54 -0400207 final ArrayList<NotificationSignalExtractor> mSignalExtractors = new ArrayList<NotificationSignalExtractor>();
Scott Greenwald9a05b312013-06-28 00:37:54 -0400208
John Spurlockb408e8e2014-04-23 21:12:45 -0400209 private final UserProfiles mUserProfiles = new UserProfiles();
John Spurlock7340fc82014-04-24 18:50:12 -0400210 private NotificationListeners mListeners;
211 private ConditionProviders mConditionProviders;
Christoph Studer1c3f81f2014-04-16 15:05:56 +0200212 private NotificationUsageStats mUsageStats;
Christoph Studer546bec82014-03-14 12:17:12 +0100213
John Spurlocke677d712014-02-13 12:52:19 -0500214 private static final String EXTRA_INTERCEPT = "android.intercept";
215
John Spurlocke6a7d932014-03-13 12:29:00 -0400216 private static final int MY_UID = Process.myUid();
217 private static final int MY_PID = Process.myPid();
218 private static final int REASON_DELEGATE_CLICK = 1;
219 private static final int REASON_DELEGATE_CANCEL = 2;
220 private static final int REASON_DELEGATE_CANCEL_ALL = 3;
221 private static final int REASON_DELEGATE_ERROR = 4;
222 private static final int REASON_PACKAGE_CHANGED = 5;
223 private static final int REASON_USER_STOPPED = 6;
224 private static final int REASON_PACKAGE_BANNED = 7;
225 private static final int REASON_NOMAN_CANCEL = 8;
226 private static final int REASON_NOMAN_CANCEL_ALL = 9;
227 private static final int REASON_LISTENER_CANCEL = 10;
228 private static final int REASON_LISTENER_CANCEL_ALL = 11;
229
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500230 private static class Archive {
Daniel Sandler5e62e3a2013-04-15 20:57:02 -0400231 static final int BUFFER_SIZE = 250;
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500232 ArrayDeque<StatusBarNotification> mBuffer = new ArrayDeque<StatusBarNotification>(BUFFER_SIZE);
233
234 public Archive() {
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500235 }
Jeff Sharkey0c1baf92013-04-03 13:08:52 -0700236
Daniel Sandler5e62e3a2013-04-15 20:57:02 -0400237 public String toString() {
238 final StringBuilder sb = new StringBuilder();
239 final int N = mBuffer.size();
240 sb.append("Archive (");
241 sb.append(N);
242 sb.append(" notification");
243 sb.append((N==1)?")":"s)");
244 return sb.toString();
245 }
246
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500247 public void record(StatusBarNotification nr) {
248 if (mBuffer.size() == BUFFER_SIZE) {
249 mBuffer.removeFirst();
250 }
Daniel Sandler26b81d52013-05-20 20:56:43 -0400251
252 // We don't want to store the heavy bits of the notification in the archive,
253 // but other clients in the system process might be using the object, so we
254 // store a (lightened) copy.
255 mBuffer.addLast(nr.cloneLight());
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500256 }
257
Daniel Sandler5e62e3a2013-04-15 20:57:02 -0400258
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500259 public void clear() {
260 mBuffer.clear();
261 }
262
263 public Iterator<StatusBarNotification> descendingIterator() {
264 return mBuffer.descendingIterator();
265 }
266 public Iterator<StatusBarNotification> ascendingIterator() {
267 return mBuffer.iterator();
268 }
269 public Iterator<StatusBarNotification> filter(
270 final Iterator<StatusBarNotification> iter, final String pkg, final int userId) {
271 return new Iterator<StatusBarNotification>() {
272 StatusBarNotification mNext = findNext();
273
274 private StatusBarNotification findNext() {
275 while (iter.hasNext()) {
276 StatusBarNotification nr = iter.next();
Daniel Sandler4f91efd2013-04-25 16:38:41 -0400277 if ((pkg == null || nr.getPackageName() == pkg)
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500278 && (userId == UserHandle.USER_ALL || nr.getUserId() == userId)) {
279 return nr;
280 }
281 }
282 return null;
283 }
284
285 @Override
286 public boolean hasNext() {
287 return mNext == null;
288 }
289
290 @Override
291 public StatusBarNotification next() {
292 StatusBarNotification next = mNext;
293 if (next == null) {
294 throw new NoSuchElementException();
295 }
296 mNext = findNext();
297 return next;
298 }
299
300 @Override
301 public void remove() {
302 iter.remove();
303 }
304 };
305 }
Daniel Sandler78d0d252013-02-12 08:14:52 -0500306
307 public StatusBarNotification[] getArray(int count) {
308 if (count == 0) count = Archive.BUFFER_SIZE;
309 final StatusBarNotification[] a
310 = new StatusBarNotification[Math.min(count, mBuffer.size())];
311 Iterator<StatusBarNotification> iter = descendingIterator();
312 int i=0;
313 while (iter.hasNext() && i < count) {
314 a[i++] = iter.next();
315 }
316 return a;
317 }
318
319 public StatusBarNotification[] getArray(int count, String pkg, int userId) {
320 if (count == 0) count = Archive.BUFFER_SIZE;
321 final StatusBarNotification[] a
322 = new StatusBarNotification[Math.min(count, mBuffer.size())];
323 Iterator<StatusBarNotification> iter = filter(descendingIterator(), pkg, userId);
324 int i=0;
325 while (iter.hasNext() && i < count) {
326 a[i++] = iter.next();
327 }
328 return a;
329 }
330
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500331 }
332
333 Archive mArchive = new Archive();
334
John Spurlock056c5192014-04-20 21:52:01 -0400335 private void loadPolicyFile() {
336 synchronized(mPolicyFile) {
337 mBlockedPackages.clear();
Daniel Sandler0da673f2012-04-11 12:33:16 -0400338
John Spurlock056c5192014-04-20 21:52:01 -0400339 FileInputStream infile = null;
340 try {
341 infile = mPolicyFile.openRead();
342 final XmlPullParser parser = Xml.newPullParser();
343 parser.setInput(infile, null);
Daniel Sandler0da673f2012-04-11 12:33:16 -0400344
John Spurlock056c5192014-04-20 21:52:01 -0400345 int type;
346 String tag;
347 int version = DB_VERSION;
348 while ((type = parser.next()) != END_DOCUMENT) {
349 tag = parser.getName();
350 if (type == START_TAG) {
351 if (TAG_BODY.equals(tag)) {
352 version = Integer.parseInt(
353 parser.getAttributeValue(null, ATTR_VERSION));
354 } else if (TAG_BLOCKED_PKGS.equals(tag)) {
355 while ((type = parser.next()) != END_DOCUMENT) {
356 tag = parser.getName();
357 if (TAG_PACKAGE.equals(tag)) {
358 mBlockedPackages.add(
359 parser.getAttributeValue(null, ATTR_NAME));
360 } else if (TAG_BLOCKED_PKGS.equals(tag) && type == END_TAG) {
361 break;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400362 }
363 }
364 }
365 }
John Spurlock056c5192014-04-20 21:52:01 -0400366 mZenModeHelper.readXml(parser);
Daniel Sandler0da673f2012-04-11 12:33:16 -0400367 }
John Spurlock056c5192014-04-20 21:52:01 -0400368 } catch (FileNotFoundException e) {
369 // No data yet
370 } catch (IOException e) {
371 Log.wtf(TAG, "Unable to read notification policy", e);
372 } catch (NumberFormatException e) {
373 Log.wtf(TAG, "Unable to parse notification policy", e);
374 } catch (XmlPullParserException e) {
375 Log.wtf(TAG, "Unable to parse notification policy", e);
376 } finally {
377 IoUtils.closeQuietly(infile);
378 }
379 }
380 }
381
382 public void savePolicyFile() {
383 mHandler.removeMessages(MESSAGE_SAVE_POLICY_FILE);
384 mHandler.sendEmptyMessage(MESSAGE_SAVE_POLICY_FILE);
385 }
386
387 private void handleSavePolicyFile() {
388 Slog.d(TAG, "handleSavePolicyFile");
389 synchronized (mPolicyFile) {
390 final FileOutputStream stream;
391 try {
392 stream = mPolicyFile.startWrite();
393 } catch (IOException e) {
394 Slog.w(TAG, "Failed to save policy file", e);
395 return;
396 }
397
398 try {
399 final XmlSerializer out = new FastXmlSerializer();
400 out.setOutput(stream, "utf-8");
401 out.startDocument(null, true);
402 out.startTag(null, TAG_BODY);
403 out.attribute(null, ATTR_VERSION, Integer.toString(DB_VERSION));
404 mZenModeHelper.writeXml(out);
405 out.endTag(null, TAG_BODY);
406 out.endDocument();
407 mPolicyFile.finishWrite(stream);
408 } catch (IOException e) {
409 Slog.w(TAG, "Failed to save policy file, restoring backup", e);
410 mPolicyFile.failWrite(stream);
Daniel Sandler0da673f2012-04-11 12:33:16 -0400411 }
412 }
413 }
414
Daniel Sandler4a900ac2013-01-30 14:04:10 -0500415 /** Use this when you actually want to post a notification or toast.
416 *
417 * Unchecked. Not exposed via Binder, but can be called in the course of enqueue*().
418 */
419 private boolean noteNotificationOp(String pkg, int uid) {
420 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
421 != AppOpsManager.MODE_ALLOWED) {
422 Slog.v(TAG, "notifications are disabled by AppOps for " + pkg);
423 return false;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400424 }
Daniel Sandler4a900ac2013-01-30 14:04:10 -0500425 return true;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400426 }
427
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800428 private static String idDebugString(Context baseContext, String packageName, int id) {
429 Context c = null;
430
431 if (packageName != null) {
432 try {
433 c = baseContext.createPackageContext(packageName, 0);
434 } catch (NameNotFoundException e) {
435 c = baseContext;
436 }
437 } else {
438 c = baseContext;
439 }
440
441 String pkg;
442 String type;
443 String name;
444
445 Resources r = c.getResources();
446 try {
447 return r.getResourceName(id);
448 } catch (Resources.NotFoundException e) {
449 return "<name unknown>";
450 }
451 }
452
Daniel Sandler78d0d252013-02-12 08:14:52 -0500453
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700454
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500455 public static final class NotificationRecord
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800456 {
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500457 final StatusBarNotification sbn;
Christoph Studer061dee22014-05-09 12:28:55 +0200458 SingleNotificationStats stats;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800459 IBinder statusBarKey;
Chris Wren5190c0f2014-05-08 10:42:09 -0400460
461 // These members are used by NotificationSignalExtractors
462 // to communicate with the ranking module.
Chris Wrenf9536642014-04-17 10:01:54 -0400463 private float mContactAffinity;
Chris Wren5190c0f2014-05-08 10:42:09 -0400464 private boolean mRecentlyIntrusive;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800465
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500466 NotificationRecord(StatusBarNotification sbn)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800467 {
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500468 this.sbn = sbn;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800469 }
Fred Quintana6ecaff12009-09-25 14:23:13 -0700470
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -0400471 public Notification getNotification() { return sbn.getNotification(); }
472 public int getFlags() { return sbn.getNotification().flags; }
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500473 public int getUserId() { return sbn.getUserId(); }
474
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800475 void dump(PrintWriter pw, String prefix, Context baseContext) {
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -0400476 final Notification notification = sbn.getNotification();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800477 pw.println(prefix + this);
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -0400478 pw.println(prefix + " uid=" + sbn.getUid() + " userId=" + sbn.getUserId());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800479 pw.println(prefix + " icon=0x" + Integer.toHexString(notification.icon)
Daniel Sandler4f91efd2013-04-25 16:38:41 -0400480 + " / " + idDebugString(baseContext, sbn.getPackageName(), notification.icon));
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -0400481 pw.println(prefix + " pri=" + notification.priority + " score=" + sbn.getScore());
John Spurlocka4294292014-03-24 18:02:32 -0400482 pw.println(prefix + " key=" + sbn.getKey());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800483 pw.println(prefix + " contentIntent=" + notification.contentIntent);
484 pw.println(prefix + " deleteIntent=" + notification.deleteIntent);
485 pw.println(prefix + " tickerText=" + notification.tickerText);
486 pw.println(prefix + " contentView=" + notification.contentView);
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400487 pw.println(prefix + String.format(" defaults=0x%08x flags=0x%08x",
488 notification.defaults, notification.flags));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800489 pw.println(prefix + " sound=" + notification.sound);
Dan Sandler26e81cf2014-05-06 10:01:27 -0400490 pw.println(prefix + String.format(" color=0x%08x", notification.color));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800491 pw.println(prefix + " vibrate=" + Arrays.toString(notification.vibrate));
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400492 pw.println(prefix + String.format(" led=0x%08x onMs=%d offMs=%d",
493 notification.ledARGB, notification.ledOnMS, notification.ledOffMS));
494 if (notification.actions != null && notification.actions.length > 0) {
495 pw.println(prefix + " actions={");
496 final int N = notification.actions.length;
497 for (int i=0; i<N; i++) {
498 final Notification.Action action = notification.actions[i];
499 pw.println(String.format("%s [%d] \"%s\" -> %s",
500 prefix,
501 i,
502 action.title,
503 action.actionIntent.toString()
504 ));
505 }
506 pw.println(prefix + " }");
507 }
508 if (notification.extras != null && notification.extras.size() > 0) {
509 pw.println(prefix + " extras={");
510 for (String key : notification.extras.keySet()) {
511 pw.print(prefix + " " + key + "=");
512 Object val = notification.extras.get(key);
513 if (val == null) {
514 pw.println("null");
515 } else {
516 pw.print(val.toString());
517 if (val instanceof Bitmap) {
518 pw.print(String.format(" (%dx%d)",
519 ((Bitmap) val).getWidth(),
520 ((Bitmap) val).getHeight()));
521 } else if (val.getClass().isArray()) {
522 pw.println(" {");
523 final int N = Array.getLength(val);
524 for (int i=0; i<N; i++) {
525 if (i > 0) pw.println(",");
526 pw.print(prefix + " " + Array.get(val, i));
527 }
528 pw.print("\n" + prefix + " }");
529 }
530 pw.println();
531 }
532 }
533 pw.println(prefix + " }");
534 }
Christoph Studer546bec82014-03-14 12:17:12 +0100535 pw.println(prefix + " stats=" + stats.toString());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800536 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800537
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800538 @Override
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500539 public final String toString() {
540 return String.format(
John Spurlocka4294292014-03-24 18:02:32 -0400541 "NotificationRecord(0x%08x: pkg=%s user=%s id=%d tag=%s score=%d key=%s: %s)",
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500542 System.identityHashCode(this),
Adam Lesinski182f73f2013-12-05 16:48:06 -0800543 this.sbn.getPackageName(), this.sbn.getUser(), this.sbn.getId(),
John Spurlocka4294292014-03-24 18:02:32 -0400544 this.sbn.getTag(), this.sbn.getScore(), this.sbn.getKey(),
545 this.sbn.getNotification());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800546 }
Chris Wrenf9536642014-04-17 10:01:54 -0400547
548 public void setContactAffinity(float contactAffinity) {
549 mContactAffinity = contactAffinity;
550 }
551
552 public float getContactAffinity() {
553 return mContactAffinity;
554 }
Chris Wren5190c0f2014-05-08 10:42:09 -0400555
556 public boolean isRecentlyIntrusive() {
557 return mRecentlyIntrusive;
558 }
559
560 public void setRecentlyIntusive(boolean recentlyIntrusive) {
561 mRecentlyIntrusive = recentlyIntrusive;
562 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800563 }
564
565 private static final class ToastRecord
566 {
567 final int pid;
568 final String pkg;
569 final ITransientNotification callback;
570 int duration;
571
572 ToastRecord(int pid, String pkg, ITransientNotification callback, int duration)
573 {
574 this.pid = pid;
575 this.pkg = pkg;
576 this.callback = callback;
577 this.duration = duration;
578 }
579
580 void update(int duration) {
581 this.duration = duration;
582 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800583
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800584 void dump(PrintWriter pw, String prefix) {
585 pw.println(prefix + this);
586 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800587
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800588 @Override
589 public final String toString()
590 {
591 return "ToastRecord{"
592 + Integer.toHexString(System.identityHashCode(this))
593 + " pkg=" + pkg
594 + " callback=" + callback
595 + " duration=" + duration;
596 }
597 }
598
Adam Lesinski182f73f2013-12-05 16:48:06 -0800599 private final NotificationDelegate mNotificationDelegate = new NotificationDelegate() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800600
Adam Lesinski182f73f2013-12-05 16:48:06 -0800601 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800602 public void onSetDisabled(int status) {
603 synchronized (mNotificationList) {
John Spurlocke677d712014-02-13 12:52:19 -0500604 mDisableNotificationAlerts = (status & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0;
605 if (mDisableNotificationAlerts) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800606 // cancel whatever's going on
607 long identity = Binder.clearCallingIdentity();
608 try {
Adam Lesinski182f73f2013-12-05 16:48:06 -0800609 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
Jeff Sharkey098d5802012-04-26 17:30:34 -0700610 if (player != null) {
611 player.stopAsync();
612 }
613 } catch (RemoteException e) {
614 } finally {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800615 Binder.restoreCallingIdentity(identity);
616 }
617
618 identity = Binder.clearCallingIdentity();
619 try {
620 mVibrator.cancel();
Jeff Sharkey098d5802012-04-26 17:30:34 -0700621 } finally {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800622 Binder.restoreCallingIdentity(identity);
623 }
624 }
625 }
626 }
627
Adam Lesinski182f73f2013-12-05 16:48:06 -0800628 @Override
John Spurlocke6a7d932014-03-13 12:29:00 -0400629 public void onClearAll(int callingUid, int callingPid, int userId) {
Adam Lesinskie8240262014-03-26 16:01:00 -0700630 synchronized (mNotificationList) {
Kenny Guya263e4e2014-03-03 18:24:03 +0000631 cancelAllLocked(callingUid, callingPid, userId, REASON_DELEGATE_CANCEL_ALL, null,
632 /*includeCurrentProfiles*/ true);
Adam Lesinskie8240262014-03-26 16:01:00 -0700633 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800634 }
635
Adam Lesinski182f73f2013-12-05 16:48:06 -0800636 @Override
Christoph Studer03b87a22014-04-30 17:33:27 +0200637 public void onNotificationClick(int callingUid, int callingPid, String key) {
638 synchronized (mNotificationList) {
639 EventLogTags.writeNotificationClicked(key);
640 NotificationRecord r = mNotificationsByKey.get(key);
641 if (r == null) {
642 Log.w(TAG, "No notification with key: " + key);
643 return;
644 }
645 StatusBarNotification sbn = r.sbn;
646 cancelNotification(callingUid, callingPid, sbn.getPackageName(), sbn.getTag(),
647 sbn.getId(), Notification.FLAG_AUTO_CANCEL,
648 Notification.FLAG_FOREGROUND_SERVICE, false, r.getUserId(),
649 REASON_DELEGATE_CLICK, null);
650 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800651 }
652
Adam Lesinski182f73f2013-12-05 16:48:06 -0800653 @Override
John Spurlocke6a7d932014-03-13 12:29:00 -0400654 public void onNotificationClear(int callingUid, int callingPid,
655 String pkg, String tag, int id, int userId) {
656 cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
Kenny Guy3a7c4a52014-03-03 18:24:03 +0000657 Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
John Spurlocke6a7d932014-03-13 12:29:00 -0400658 true, userId, REASON_DELEGATE_CANCEL, null);
Daniel Sandler0f0b11c2010-08-04 15:54:58 -0400659 }
660
Adam Lesinski182f73f2013-12-05 16:48:06 -0800661 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800662 public void onPanelRevealed() {
Christoph Studer760ea552014-03-21 13:10:21 +0100663 EventLogTags.writeNotificationPanelRevealed();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800664 synchronized (mNotificationList) {
665 // sound
666 mSoundNotification = null;
Jeff Sharkey098d5802012-04-26 17:30:34 -0700667
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800668 long identity = Binder.clearCallingIdentity();
669 try {
Adam Lesinski182f73f2013-12-05 16:48:06 -0800670 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
Jeff Sharkey098d5802012-04-26 17:30:34 -0700671 if (player != null) {
672 player.stopAsync();
673 }
674 } catch (RemoteException e) {
675 } finally {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800676 Binder.restoreCallingIdentity(identity);
677 }
678
679 // vibrate
680 mVibrateNotification = null;
681 identity = Binder.clearCallingIdentity();
682 try {
683 mVibrator.cancel();
Jeff Sharkey098d5802012-04-26 17:30:34 -0700684 } finally {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800685 Binder.restoreCallingIdentity(identity);
686 }
687
688 // light
689 mLights.clear();
690 mLedNotification = null;
691 updateLightsLocked();
692 }
693 }
Joe Onorato005847b2010-06-04 16:08:02 -0400694
Adam Lesinski182f73f2013-12-05 16:48:06 -0800695 @Override
Christoph Studer760ea552014-03-21 13:10:21 +0100696 public void onPanelHidden() {
697 EventLogTags.writeNotificationPanelHidden();
698 }
699
700 @Override
John Spurlocke6a7d932014-03-13 12:29:00 -0400701 public void onNotificationError(int callingUid, int callingPid, String pkg, String tag, int id,
Kenny Guy3a7c4a52014-03-03 18:24:03 +0000702 int uid, int initialPid, String message, int userId) {
Daniel Sandlerd0a2f862010-08-03 15:29:31 -0400703 Slog.d(TAG, "onNotification error pkg=" + pkg + " tag=" + tag + " id=" + id
704 + "; will crashApplication(uid=" + uid + ", pid=" + initialPid + ")");
John Spurlocke6a7d932014-03-13 12:29:00 -0400705 cancelNotification(callingUid, callingPid, pkg, tag, id, 0, 0, false, userId,
706 REASON_DELEGATE_ERROR, null);
Dianne Hackborn9d39d0c2010-06-24 15:57:42 -0700707 long ident = Binder.clearCallingIdentity();
708 try {
709 ActivityManagerNative.getDefault().crashApplication(uid, initialPid, pkg,
710 "Bad notification posted from package " + pkg
711 + ": " + message);
712 } catch (RemoteException e) {
713 }
714 Binder.restoreCallingIdentity(ident);
Joe Onorato005847b2010-06-04 16:08:02 -0400715 }
John Spurlocke677d712014-02-13 12:52:19 -0500716
717 @Override
718 public boolean allowDisable(int what, IBinder token, String pkg) {
John Spurlock056c5192014-04-20 21:52:01 -0400719 return mZenModeHelper.allowDisable(what, token, pkg);
John Spurlocke677d712014-02-13 12:52:19 -0500720 }
Christoph Studer92b389d2014-04-01 18:44:40 +0200721
722 @Override
723 public void onNotificationVisibilityChanged(
724 String[] newlyVisibleKeys, String[] noLongerVisibleKeys) {
725 // Using ';' as separator since eventlogs uses ',' to separate
726 // args.
727 EventLogTags.writeNotificationVisibilityChanged(
728 TextUtils.join(";", newlyVisibleKeys),
729 TextUtils.join(";", noLongerVisibleKeys));
730 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800731 };
732
733 private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
734 @Override
735 public void onReceive(Context context, Intent intent) {
736 String action = intent.getAction();
737
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800738 boolean queryRestart = false;
Chris Wrenae9bb572013-05-15 14:50:28 -0400739 boolean queryRemove = false;
Daniel Sandler26ece572012-06-01 15:38:46 -0400740 boolean packageChanged = false;
John Spurlock79f78922013-05-16 09:10:05 -0400741 boolean cancelNotifications = true;
Chris Wrenf9536642014-04-17 10:01:54 -0400742
Chris Wren3da73022013-05-10 14:41:21 -0400743 if (action.equals(Intent.ACTION_PACKAGE_ADDED)
Chris Wrenae9bb572013-05-15 14:50:28 -0400744 || (queryRemove=action.equals(Intent.ACTION_PACKAGE_REMOVED))
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800745 || action.equals(Intent.ACTION_PACKAGE_RESTARTED)
Daniel Sandler26ece572012-06-01 15:38:46 -0400746 || (packageChanged=action.equals(Intent.ACTION_PACKAGE_CHANGED))
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800747 || (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART))
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800748 || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800749 String pkgList[] = null;
Chris Wrenae9bb572013-05-15 14:50:28 -0400750 boolean queryReplace = queryRemove &&
751 intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
John Spurlocke77bb362014-04-26 10:24:59 -0400752 if (DBG) Slog.i(TAG, "action=" + action + " queryReplace=" + queryReplace);
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800753 if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800754 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800755 } else if (queryRestart) {
756 pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800757 } else {
758 Uri uri = intent.getData();
759 if (uri == null) {
760 return;
761 }
762 String pkgName = uri.getSchemeSpecificPart();
763 if (pkgName == null) {
764 return;
765 }
Daniel Sandler26ece572012-06-01 15:38:46 -0400766 if (packageChanged) {
767 // We cancel notifications for packages which have just been disabled
Christopher Tate06e5fed2013-10-09 14:39:15 -0700768 try {
Adam Lesinski182f73f2013-12-05 16:48:06 -0800769 final int enabled = getContext().getPackageManager()
Christopher Tate06e5fed2013-10-09 14:39:15 -0700770 .getApplicationEnabledSetting(pkgName);
771 if (enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED
772 || enabled == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
773 cancelNotifications = false;
774 }
775 } catch (IllegalArgumentException e) {
776 // Package doesn't exist; probably racing with uninstall.
777 // cancelNotifications is already true, so nothing to do here.
778 if (DBG) {
779 Slog.i(TAG, "Exception trying to look up app enabled setting", e);
780 }
Daniel Sandler26ece572012-06-01 15:38:46 -0400781 }
782 }
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800783 pkgList = new String[]{pkgName};
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800784 }
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700785
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800786 if (pkgList != null && (pkgList.length > 0)) {
787 for (String pkgName : pkgList) {
John Spurlock79f78922013-05-16 09:10:05 -0400788 if (cancelNotifications) {
John Spurlocke6a7d932014-03-13 12:29:00 -0400789 cancelAllNotificationsInt(MY_UID, MY_PID, pkgName, 0, 0, !queryRestart,
790 UserHandle.USER_ALL, REASON_PACKAGE_CHANGED, null);
John Spurlock79f78922013-05-16 09:10:05 -0400791 }
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800792 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800793 }
John Spurlockb408e8e2014-04-23 21:12:45 -0400794 mListeners.onPackagesChanged(queryReplace, pkgList);
John Spurlock7340fc82014-04-24 18:50:12 -0400795 mConditionProviders.onPackagesChanged(queryReplace, pkgList);
Mike Lockwood63b5ad92011-08-30 09:55:30 -0400796 } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
797 // Keep track of screen on/off state, but do not turn off the notification light
798 // until user passes through the lock screen or views the notification.
799 mScreenOn = true;
800 } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
801 mScreenOn = false;
Daniel Sandlere96ffb12010-03-11 13:38:06 -0500802 } else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) {
John Spurlock5d2eeb12014-01-16 10:46:36 -0500803 mInCall = TelephonyManager.EXTRA_STATE_OFFHOOK
804 .equals(intent.getStringExtra(TelephonyManager.EXTRA_STATE));
Daniel Sandlere96ffb12010-03-11 13:38:06 -0500805 updateNotificationPulse();
Dianne Hackborn80a4af22012-08-27 19:18:31 -0700806 } else if (action.equals(Intent.ACTION_USER_STOPPED)) {
807 int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
808 if (userHandle >= 0) {
John Spurlocke6a7d932014-03-13 12:29:00 -0400809 cancelAllNotificationsInt(MY_UID, MY_PID, null, 0, 0, true, userHandle,
810 REASON_USER_STOPPED, null);
Dianne Hackborn80a4af22012-08-27 19:18:31 -0700811 }
Mike Lockwood63b5ad92011-08-30 09:55:30 -0400812 } else if (action.equals(Intent.ACTION_USER_PRESENT)) {
813 // turn off LED when user passes through lock screen
814 mNotificationLight.turnOff();
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400815 } else if (action.equals(Intent.ACTION_USER_SWITCHED)) {
816 // reload per-user settings
817 mSettingsObserver.update(null);
John Spurlockb408e8e2014-04-23 21:12:45 -0400818 mUserProfiles.updateCache(context);
Kenny Guy3a7c4a52014-03-03 18:24:03 +0000819 } else if (action.equals(Intent.ACTION_USER_ADDED)) {
John Spurlockb408e8e2014-04-23 21:12:45 -0400820 mUserProfiles.updateCache(context);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800821 }
822 }
823 };
824
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700825 class SettingsObserver extends ContentObserver {
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400826 private final Uri NOTIFICATION_LIGHT_PULSE_URI
827 = Settings.System.getUriFor(Settings.System.NOTIFICATION_LIGHT_PULSE);
828
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700829 SettingsObserver(Handler handler) {
830 super(handler);
831 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800832
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700833 void observe() {
Adam Lesinski182f73f2013-12-05 16:48:06 -0800834 ContentResolver resolver = getContext().getContentResolver();
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400835 resolver.registerContentObserver(NOTIFICATION_LIGHT_PULSE_URI,
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700836 false, this, UserHandle.USER_ALL);
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400837 update(null);
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700838 }
839
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400840 @Override public void onChange(boolean selfChange, Uri uri) {
841 update(uri);
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700842 }
843
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400844 public void update(Uri uri) {
Adam Lesinski182f73f2013-12-05 16:48:06 -0800845 ContentResolver resolver = getContext().getContentResolver();
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400846 if (uri == null || NOTIFICATION_LIGHT_PULSE_URI.equals(uri)) {
847 boolean pulseEnabled = Settings.System.getInt(resolver,
848 Settings.System.NOTIFICATION_LIGHT_PULSE, 0) != 0;
849 if (mNotificationPulseEnabled != pulseEnabled) {
850 mNotificationPulseEnabled = pulseEnabled;
851 updateNotificationPulse();
852 }
853 }
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700854 }
855 }
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500856
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400857 private SettingsObserver mSettingsObserver;
John Spurlock056c5192014-04-20 21:52:01 -0400858 private ZenModeHelper mZenModeHelper;
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400859
Daniel Sandleredbb3802012-11-13 20:49:47 -0800860 static long[] getLongArray(Resources r, int resid, int maxlen, long[] def) {
861 int[] ar = r.getIntArray(resid);
862 if (ar == null) {
863 return def;
864 }
865 final int len = ar.length > maxlen ? maxlen : ar.length;
866 long[] out = new long[len];
867 for (int i=0; i<len; i++) {
868 out[i] = ar[i];
869 }
870 return out;
871 }
872
Jeff Brownb880d882014-02-10 19:47:07 -0800873 public NotificationManagerService(Context context) {
874 super(context);
875 }
876
Adam Lesinski182f73f2013-12-05 16:48:06 -0800877 @Override
878 public void onStart() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800879 mAm = ActivityManagerNative.getDefault();
Adam Lesinski182f73f2013-12-05 16:48:06 -0800880 mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
881 mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
San Mehat3ee13172010-02-04 20:54:43 -0800882
Adam Lesinski182f73f2013-12-05 16:48:06 -0800883 mHandler = new WorkerHandler();
Chris Wrenf9536642014-04-17 10:01:54 -0400884 mRankingThread.start();
885 mRankingHandler = new RankingWorkerHandler(mRankingThread.getLooper());
John Spurlock056c5192014-04-20 21:52:01 -0400886 mZenModeHelper = new ZenModeHelper(getContext(), mHandler);
John Spurlock1c923a32014-04-27 16:42:29 -0400887 mZenModeHelper.addCallback(new ZenModeHelper.Callback() {
John Spurlock056c5192014-04-20 21:52:01 -0400888 @Override
889 public void onConfigChanged() {
890 savePolicyFile();
891 }
892 });
893 final File systemDir = new File(Environment.getDataDirectory(), "system");
894 mPolicyFile = new AtomicFile(new File(systemDir, "notification_policy.xml"));
Christoph Studer1c3f81f2014-04-16 15:05:56 +0200895 mUsageStats = new NotificationUsageStats(getContext());
Daniel Sandler4a900ac2013-01-30 14:04:10 -0500896
897 importOldBlockDb();
Daniel Sandler0da673f2012-04-11 12:33:16 -0400898
John Spurlock7340fc82014-04-24 18:50:12 -0400899 mListeners = new NotificationListeners();
900 mConditionProviders = new ConditionProviders(getContext(),
John Spurlocke77bb362014-04-26 10:24:59 -0400901 mHandler, mUserProfiles, mZenModeHelper);
Adam Lesinski182f73f2013-12-05 16:48:06 -0800902 mStatusBar = getLocalService(StatusBarManagerInternal.class);
903 mStatusBar.setNotificationDelegate(mNotificationDelegate);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800904
Adam Lesinski182f73f2013-12-05 16:48:06 -0800905 final LightsManager lights = getLocalService(LightsManager.class);
906 mNotificationLight = lights.getLight(LightsManager.LIGHT_ID_NOTIFICATIONS);
907 mAttentionLight = lights.getLight(LightsManager.LIGHT_ID_ATTENTION);
Mike Lockwood3cb67a32009-11-27 14:25:58 -0500908
Adam Lesinski182f73f2013-12-05 16:48:06 -0800909 Resources resources = getContext().getResources();
Mike Lockwood670f9322010-01-20 12:13:36 -0500910 mDefaultNotificationColor = resources.getColor(
Scott Greenwald9a05b312013-06-28 00:37:54 -0400911 R.color.config_defaultNotificationColor);
Mike Lockwood670f9322010-01-20 12:13:36 -0500912 mDefaultNotificationLedOn = resources.getInteger(
Scott Greenwald9a05b312013-06-28 00:37:54 -0400913 R.integer.config_defaultNotificationLedOn);
Mike Lockwood670f9322010-01-20 12:13:36 -0500914 mDefaultNotificationLedOff = resources.getInteger(
Scott Greenwald9a05b312013-06-28 00:37:54 -0400915 R.integer.config_defaultNotificationLedOff);
Mike Lockwood670f9322010-01-20 12:13:36 -0500916
Daniel Sandleredbb3802012-11-13 20:49:47 -0800917 mDefaultVibrationPattern = getLongArray(resources,
Scott Greenwald9a05b312013-06-28 00:37:54 -0400918 R.array.config_defaultNotificationVibePattern,
Daniel Sandleredbb3802012-11-13 20:49:47 -0800919 VIBRATE_PATTERN_MAXLEN,
920 DEFAULT_VIBRATE_PATTERN);
921
922 mFallbackVibrationPattern = getLongArray(resources,
Scott Greenwald9a05b312013-06-28 00:37:54 -0400923 R.array.config_notificationFallbackVibePattern,
Daniel Sandleredbb3802012-11-13 20:49:47 -0800924 VIBRATE_PATTERN_MAXLEN,
925 DEFAULT_VIBRATE_PATTERN);
926
Joe Onorato39f5b6a2009-07-23 12:29:19 -0400927 // Don't start allowing notifications until the setup wizard has run once.
928 // After that, including subsequent boots, init with notifications turned on.
929 // This works on the first boot because the setup wizard will toggle this
930 // flag at least once and we'll go back to 0 after that.
Adam Lesinski182f73f2013-12-05 16:48:06 -0800931 if (0 == Settings.Global.getInt(getContext().getContentResolver(),
Jeff Brownbf6f6f92012-09-25 15:03:20 -0700932 Settings.Global.DEVICE_PROVISIONED, 0)) {
John Spurlocke677d712014-02-13 12:52:19 -0500933 mDisableNotificationAlerts = true;
Joe Onorato39f5b6a2009-07-23 12:29:19 -0400934 }
John Spurlock056c5192014-04-20 21:52:01 -0400935 mZenModeHelper.updateZenMode();
Joe Onorato39f5b6a2009-07-23 12:29:19 -0400936
John Spurlockb408e8e2014-04-23 21:12:45 -0400937 mUserProfiles.updateCache(getContext());
Kenny Guya263e4e2014-03-03 18:24:03 +0000938
Mike Lockwood35e16bf2010-11-30 19:53:36 -0500939 // register for various Intents
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800940 IntentFilter filter = new IntentFilter();
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500941 filter.addAction(Intent.ACTION_SCREEN_ON);
942 filter.addAction(Intent.ACTION_SCREEN_OFF);
Daniel Sandlere96ffb12010-03-11 13:38:06 -0500943 filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
Mike Lockwood63b5ad92011-08-30 09:55:30 -0400944 filter.addAction(Intent.ACTION_USER_PRESENT);
Dianne Hackborn80a4af22012-08-27 19:18:31 -0700945 filter.addAction(Intent.ACTION_USER_STOPPED);
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400946 filter.addAction(Intent.ACTION_USER_SWITCHED);
Kenny Guy3a7c4a52014-03-03 18:24:03 +0000947 filter.addAction(Intent.ACTION_USER_ADDED);
Adam Lesinski182f73f2013-12-05 16:48:06 -0800948 getContext().registerReceiver(mIntentReceiver, filter);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800949 IntentFilter pkgFilter = new IntentFilter();
Chris Wren3da73022013-05-10 14:41:21 -0400950 pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800951 pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
Daniel Sandleraac0eb02011-08-06 22:51:56 -0400952 pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800953 pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
954 pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
955 pkgFilter.addDataScheme("package");
Adam Lesinski182f73f2013-12-05 16:48:06 -0800956 getContext().registerReceiver(mIntentReceiver, pkgFilter);
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800957 IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
Adam Lesinski182f73f2013-12-05 16:48:06 -0800958 getContext().registerReceiver(mIntentReceiver, sdFilter);
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800959
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400960 mSettingsObserver = new SettingsObserver(mHandler);
Scott Greenwald9a05b312013-06-28 00:37:54 -0400961
Chris Wrenf9536642014-04-17 10:01:54 -0400962 // spin up NotificationSignalExtractors
963 String[] extractorNames = resources.getStringArray(
964 R.array.config_notificationSignalExtractors);
965 for (String extractorName : extractorNames) {
Scott Greenwald9a05b312013-06-28 00:37:54 -0400966 try {
Chris Wrenf9536642014-04-17 10:01:54 -0400967 Class<?> extractorClass = getContext().getClassLoader().loadClass(extractorName);
968 NotificationSignalExtractor extractor =
969 (NotificationSignalExtractor) extractorClass.newInstance();
970 extractor.initialize(getContext());
971 mSignalExtractors.add(extractor);
Scott Greenwald9a05b312013-06-28 00:37:54 -0400972 } catch (ClassNotFoundException e) {
Chris Wrenf9536642014-04-17 10:01:54 -0400973 Slog.w(TAG, "Couldn't find extractor " + extractorName + ".", e);
Scott Greenwald9a05b312013-06-28 00:37:54 -0400974 } catch (InstantiationException e) {
Chris Wrenf9536642014-04-17 10:01:54 -0400975 Slog.w(TAG, "Couldn't instantiate extractor " + extractorName + ".", e);
Scott Greenwald9a05b312013-06-28 00:37:54 -0400976 } catch (IllegalAccessException e) {
Chris Wrenf9536642014-04-17 10:01:54 -0400977 Slog.w(TAG, "Problem accessing extractor " + extractorName + ".", e);
Scott Greenwald9a05b312013-06-28 00:37:54 -0400978 }
979 }
Adam Lesinski182f73f2013-12-05 16:48:06 -0800980
981 publishBinderService(Context.NOTIFICATION_SERVICE, mService);
982 publishLocalService(NotificationManagerInternal.class, mInternalService);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800983 }
984
Daniel Sandler4a900ac2013-01-30 14:04:10 -0500985 /**
986 * Read the old XML-based app block database and import those blockages into the AppOps system.
987 */
988 private void importOldBlockDb() {
John Spurlock056c5192014-04-20 21:52:01 -0400989 loadPolicyFile();
Daniel Sandler4a900ac2013-01-30 14:04:10 -0500990
Adam Lesinski182f73f2013-12-05 16:48:06 -0800991 PackageManager pm = getContext().getPackageManager();
Daniel Sandler4a900ac2013-01-30 14:04:10 -0500992 for (String pkg : mBlockedPackages) {
993 PackageInfo info = null;
994 try {
995 info = pm.getPackageInfo(pkg, 0);
Adam Lesinski182f73f2013-12-05 16:48:06 -0800996 setNotificationsEnabledForPackageImpl(pkg, info.applicationInfo.uid, false);
Daniel Sandler4a900ac2013-01-30 14:04:10 -0500997 } catch (NameNotFoundException e) {
998 // forget you
999 }
1000 }
1001 mBlockedPackages.clear();
Daniel Sandler4a900ac2013-01-30 14:04:10 -05001002 }
1003
Adam Lesinski182f73f2013-12-05 16:48:06 -08001004 @Override
1005 public void onBootPhase(int phase) {
1006 if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
1007 // no beeping until we're basically done booting
1008 mSystemReady = true;
Jeff Sharkey098d5802012-04-26 17:30:34 -07001009
Adam Lesinski182f73f2013-12-05 16:48:06 -08001010 // Grab our optional AudioService
1011 mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
Daniel Sandler5feceeb2013-03-22 18:29:23 -07001012
Adam Lesinskia6db4ab2014-03-24 12:31:45 -07001013 } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
1014 // This observer will force an update when observe is called, causing us to
1015 // bind to listener services.
1016 mSettingsObserver.observe();
John Spurlockb408e8e2014-04-23 21:12:45 -04001017 mListeners.onBootPhaseAppsCanStart();
John Spurlock7340fc82014-04-24 18:50:12 -04001018 mConditionProviders.onBootPhaseAppsCanStart();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001019 }
1020 }
1021
Adam Lesinski182f73f2013-12-05 16:48:06 -08001022 void setNotificationsEnabledForPackageImpl(String pkg, int uid, boolean enabled) {
1023 Slog.v(TAG, (enabled?"en":"dis") + "abling notifications for " + pkg);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001024
Adam Lesinski182f73f2013-12-05 16:48:06 -08001025 mAppOps.setMode(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg,
1026 enabled ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001027
Adam Lesinski182f73f2013-12-05 16:48:06 -08001028 // Now, cancel any outstanding notifications that are part of a just-disabled app
1029 if (ENABLE_BLOCKED_NOTIFICATIONS && !enabled) {
John Spurlocke6a7d932014-03-13 12:29:00 -04001030 cancelAllNotificationsInt(MY_UID, MY_PID, pkg, 0, 0, true, UserHandle.getUserId(uid),
1031 REASON_PACKAGE_BANNED, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001032 }
1033 }
1034
Adam Lesinski182f73f2013-12-05 16:48:06 -08001035 private final IBinder mService = new INotificationManager.Stub() {
1036 // Toasts
1037 // ============================================================================
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001038
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001039 @Override
Adam Lesinski182f73f2013-12-05 16:48:06 -08001040 public void enqueueToast(String pkg, ITransientNotification callback, int duration)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001041 {
Adam Lesinski182f73f2013-12-05 16:48:06 -08001042 if (DBG) {
1043 Slog.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback
1044 + " duration=" + duration);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001045 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001046
1047 if (pkg == null || callback == null) {
1048 Slog.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback);
1049 return ;
1050 }
1051
John Spurlock7340fc82014-04-24 18:50:12 -04001052 final boolean isSystemToast = isCallerSystem() || ("android".equals(pkg));
Adam Lesinski182f73f2013-12-05 16:48:06 -08001053
1054 if (ENABLE_BLOCKED_TOASTS && !noteNotificationOp(pkg, Binder.getCallingUid())) {
1055 if (!isSystemToast) {
1056 Slog.e(TAG, "Suppressing toast from package " + pkg + " by user request.");
1057 return;
1058 }
1059 }
1060
1061 synchronized (mToastQueue) {
1062 int callingPid = Binder.getCallingPid();
1063 long callingId = Binder.clearCallingIdentity();
1064 try {
1065 ToastRecord record;
1066 int index = indexOfToastLocked(pkg, callback);
1067 // If it's already in the queue, we update it in place, we don't
1068 // move it to the end of the queue.
1069 if (index >= 0) {
1070 record = mToastQueue.get(index);
1071 record.update(duration);
1072 } else {
1073 // Limit the number of toasts that any given package except the android
1074 // package can enqueue. Prevents DOS attacks and deals with leaks.
1075 if (!isSystemToast) {
1076 int count = 0;
1077 final int N = mToastQueue.size();
1078 for (int i=0; i<N; i++) {
1079 final ToastRecord r = mToastQueue.get(i);
1080 if (r.pkg.equals(pkg)) {
1081 count++;
1082 if (count >= MAX_PACKAGE_NOTIFICATIONS) {
1083 Slog.e(TAG, "Package has already posted " + count
1084 + " toasts. Not showing more. Package=" + pkg);
1085 return;
1086 }
1087 }
1088 }
1089 }
1090
1091 record = new ToastRecord(callingPid, pkg, callback, duration);
1092 mToastQueue.add(record);
1093 index = mToastQueue.size() - 1;
1094 keepProcessAliveLocked(callingPid);
1095 }
1096 // If it's at index 0, it's the current toast. It doesn't matter if it's
1097 // new or just been updated. Call back and tell it to show itself.
1098 // If the callback fails, this will remove it from the list, so don't
1099 // assume that it's valid after this.
1100 if (index == 0) {
1101 showNextToastLocked();
1102 }
1103 } finally {
1104 Binder.restoreCallingIdentity(callingId);
1105 }
1106 }
1107 }
1108
1109 @Override
1110 public void cancelToast(String pkg, ITransientNotification callback) {
1111 Slog.i(TAG, "cancelToast pkg=" + pkg + " callback=" + callback);
1112
1113 if (pkg == null || callback == null) {
1114 Slog.e(TAG, "Not cancelling notification. pkg=" + pkg + " callback=" + callback);
1115 return ;
1116 }
1117
1118 synchronized (mToastQueue) {
1119 long callingId = Binder.clearCallingIdentity();
1120 try {
1121 int index = indexOfToastLocked(pkg, callback);
1122 if (index >= 0) {
1123 cancelToastLocked(index);
1124 } else {
1125 Slog.w(TAG, "Toast already cancelled. pkg=" + pkg
1126 + " callback=" + callback);
1127 }
1128 } finally {
1129 Binder.restoreCallingIdentity(callingId);
1130 }
1131 }
1132 }
1133
1134 @Override
Christoph Studer8fd7f1e2014-04-11 17:35:05 -04001135 public void enqueueNotificationWithTag(String pkg, String opPkg, String tag, int id,
Adam Lesinski182f73f2013-12-05 16:48:06 -08001136 Notification notification, int[] idOut, int userId) throws RemoteException {
Christoph Studer8fd7f1e2014-04-11 17:35:05 -04001137 enqueueNotificationInternal(pkg, opPkg, Binder.getCallingUid(),
Adam Lesinski182f73f2013-12-05 16:48:06 -08001138 Binder.getCallingPid(), tag, id, notification, idOut, userId);
1139 }
1140
1141 @Override
1142 public void cancelNotificationWithTag(String pkg, String tag, int id, int userId) {
John Spurlock7340fc82014-04-24 18:50:12 -04001143 checkCallerIsSystemOrSameApp(pkg);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001144 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1145 Binder.getCallingUid(), userId, true, false, "cancelNotificationWithTag", pkg);
1146 // Don't allow client applications to cancel foreground service notis.
John Spurlocke6a7d932014-03-13 12:29:00 -04001147 cancelNotification(Binder.getCallingUid(), Binder.getCallingPid(), pkg, tag, id, 0,
Adam Lesinski182f73f2013-12-05 16:48:06 -08001148 Binder.getCallingUid() == Process.SYSTEM_UID
John Spurlocke6a7d932014-03-13 12:29:00 -04001149 ? 0 : Notification.FLAG_FOREGROUND_SERVICE, false, userId, REASON_NOMAN_CANCEL,
1150 null);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001151 }
1152
1153 @Override
1154 public void cancelAllNotifications(String pkg, int userId) {
John Spurlock7340fc82014-04-24 18:50:12 -04001155 checkCallerIsSystemOrSameApp(pkg);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001156
1157 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1158 Binder.getCallingUid(), userId, true, false, "cancelAllNotifications", pkg);
1159
1160 // Calling from user space, don't allow the canceling of actively
1161 // running foreground services.
John Spurlocke6a7d932014-03-13 12:29:00 -04001162 cancelAllNotificationsInt(Binder.getCallingUid(), Binder.getCallingPid(),
1163 pkg, 0, Notification.FLAG_FOREGROUND_SERVICE, true, userId,
1164 REASON_NOMAN_CANCEL_ALL, null);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001165 }
1166
1167 @Override
1168 public void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) {
John Spurlock7340fc82014-04-24 18:50:12 -04001169 checkCallerIsSystem();
Adam Lesinski182f73f2013-12-05 16:48:06 -08001170
1171 setNotificationsEnabledForPackageImpl(pkg, uid, enabled);
1172 }
1173
1174 /**
1175 * Use this when you just want to know if notifications are OK for this package.
1176 */
1177 @Override
1178 public boolean areNotificationsEnabledForPackage(String pkg, int uid) {
John Spurlock7340fc82014-04-24 18:50:12 -04001179 checkCallerIsSystem();
Adam Lesinski182f73f2013-12-05 16:48:06 -08001180 return (mAppOps.checkOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
1181 == AppOpsManager.MODE_ALLOWED);
1182 }
1183
1184 /**
1185 * System-only API for getting a list of current (i.e. not cleared) notifications.
1186 *
1187 * Requires ACCESS_NOTIFICATIONS which is signature|system.
Chris Wrenf9536642014-04-17 10:01:54 -04001188 * @returns A list of all the notifications, in natural order.
Adam Lesinski182f73f2013-12-05 16:48:06 -08001189 */
1190 @Override
1191 public StatusBarNotification[] getActiveNotifications(String callingPkg) {
1192 // enforce() will ensure the calling uid has the correct permission
1193 getContext().enforceCallingOrSelfPermission(
1194 android.Manifest.permission.ACCESS_NOTIFICATIONS,
1195 "NotificationManagerService.getActiveNotifications");
1196
1197 StatusBarNotification[] tmp = null;
1198 int uid = Binder.getCallingUid();
1199
1200 // noteOp will check to make sure the callingPkg matches the uid
1201 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
1202 == AppOpsManager.MODE_ALLOWED) {
1203 synchronized (mNotificationList) {
1204 tmp = new StatusBarNotification[mNotificationList.size()];
1205 final int N = mNotificationList.size();
1206 for (int i=0; i<N; i++) {
1207 tmp[i] = mNotificationList.get(i).sbn;
1208 }
1209 }
1210 }
1211 return tmp;
1212 }
1213
1214 /**
1215 * System-only API for getting a list of recent (cleared, no longer shown) notifications.
1216 *
1217 * Requires ACCESS_NOTIFICATIONS which is signature|system.
1218 */
1219 @Override
1220 public StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count) {
1221 // enforce() will ensure the calling uid has the correct permission
1222 getContext().enforceCallingOrSelfPermission(
1223 android.Manifest.permission.ACCESS_NOTIFICATIONS,
1224 "NotificationManagerService.getHistoricalNotifications");
1225
1226 StatusBarNotification[] tmp = null;
1227 int uid = Binder.getCallingUid();
1228
1229 // noteOp will check to make sure the callingPkg matches the uid
1230 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
1231 == AppOpsManager.MODE_ALLOWED) {
1232 synchronized (mArchive) {
1233 tmp = mArchive.getArray(count);
1234 }
1235 }
1236 return tmp;
1237 }
1238
1239 /**
1240 * Register a listener binder directly with the notification manager.
1241 *
1242 * Only works with system callers. Apps should extend
1243 * {@link android.service.notification.NotificationListenerService}.
1244 */
1245 @Override
1246 public void registerListener(final INotificationListener listener,
1247 final ComponentName component, final int userid) {
John Spurlock7340fc82014-04-24 18:50:12 -04001248 checkCallerIsSystem();
1249 mListeners.registerService(listener, component, userid);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001250 }
1251
1252 /**
1253 * Remove a listener binder directly
1254 */
1255 @Override
1256 public void unregisterListener(INotificationListener listener, int userid) {
John Spurlock7340fc82014-04-24 18:50:12 -04001257 mListeners.unregisterService(listener, userid);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001258 }
1259
1260 /**
1261 * Allow an INotificationListener to simulate a "clear all" operation.
1262 *
1263 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onClearAllNotifications}
1264 *
1265 * @param token The binder for the listener, to check that the caller is allowed
1266 */
1267 @Override
John Spurlocka4294292014-03-24 18:02:32 -04001268 public void cancelNotificationsFromListener(INotificationListener token, String[] keys) {
John Spurlocke6a7d932014-03-13 12:29:00 -04001269 final int callingUid = Binder.getCallingUid();
1270 final int callingPid = Binder.getCallingPid();
Adam Lesinski182f73f2013-12-05 16:48:06 -08001271 long identity = Binder.clearCallingIdentity();
1272 try {
Adam Lesinskie8240262014-03-26 16:01:00 -07001273 synchronized (mNotificationList) {
John Spurlock7340fc82014-04-24 18:50:12 -04001274 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
John Spurlocka4294292014-03-24 18:02:32 -04001275 if (keys != null) {
1276 final int N = keys.length;
1277 for (int i = 0; i < N; i++) {
1278 NotificationRecord r = mNotificationsByKey.get(keys[i]);
Kenny Guya263e4e2014-03-03 18:24:03 +00001279 final int userId = r.sbn.getUserId();
1280 if (userId != info.userid && userId != UserHandle.USER_ALL &&
John Spurlockb408e8e2014-04-23 21:12:45 -04001281 !mUserProfiles.isCurrentProfile(userId)) {
Kenny Guya263e4e2014-03-03 18:24:03 +00001282 throw new SecurityException("Disallowed call from listener: "
John Spurlock7340fc82014-04-24 18:50:12 -04001283 + info.service);
Kenny Guya263e4e2014-03-03 18:24:03 +00001284 }
John Spurlocka4294292014-03-24 18:02:32 -04001285 if (r != null) {
1286 cancelNotificationFromListenerLocked(info, callingUid, callingPid,
Kenny Guya263e4e2014-03-03 18:24:03 +00001287 r.sbn.getPackageName(), r.sbn.getTag(), r.sbn.getId(),
1288 userId);
John Spurlocka4294292014-03-24 18:02:32 -04001289 }
1290 }
1291 } else {
1292 cancelAllLocked(callingUid, callingPid, info.userid,
Kenny Guya263e4e2014-03-03 18:24:03 +00001293 REASON_LISTENER_CANCEL_ALL, info, info.supportsProfiles());
John Spurlocka4294292014-03-24 18:02:32 -04001294 }
Adam Lesinskie8240262014-03-26 16:01:00 -07001295 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001296 } finally {
1297 Binder.restoreCallingIdentity(identity);
1298 }
1299 }
1300
John Spurlock7340fc82014-04-24 18:50:12 -04001301 private void cancelNotificationFromListenerLocked(ManagedServiceInfo info,
Kenny Guya263e4e2014-03-03 18:24:03 +00001302 int callingUid, int callingPid, String pkg, String tag, int id, int userId) {
John Spurlocka4294292014-03-24 18:02:32 -04001303 cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
1304 Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
1305 true,
Kenny Guya263e4e2014-03-03 18:24:03 +00001306 userId, REASON_LISTENER_CANCEL, info);
John Spurlocka4294292014-03-24 18:02:32 -04001307 }
1308
Adam Lesinski182f73f2013-12-05 16:48:06 -08001309 /**
1310 * Allow an INotificationListener to simulate clearing (dismissing) a single notification.
1311 *
1312 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onNotificationClear}
1313 *
1314 * @param token The binder for the listener, to check that the caller is allowed
1315 */
1316 @Override
1317 public void cancelNotificationFromListener(INotificationListener token, String pkg,
1318 String tag, int id) {
John Spurlocke6a7d932014-03-13 12:29:00 -04001319 final int callingUid = Binder.getCallingUid();
1320 final int callingPid = Binder.getCallingPid();
Adam Lesinski182f73f2013-12-05 16:48:06 -08001321 long identity = Binder.clearCallingIdentity();
1322 try {
Adam Lesinskie8240262014-03-26 16:01:00 -07001323 synchronized (mNotificationList) {
John Spurlock7340fc82014-04-24 18:50:12 -04001324 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
Kenny Guya263e4e2014-03-03 18:24:03 +00001325 if (info.supportsProfiles()) {
1326 Log.e(TAG, "Ignoring deprecated cancelNotification(pkg, tag, id) "
1327 + "from " + info.component
1328 + " use cancelNotification(key) instead.");
1329 } else {
1330 cancelNotificationFromListenerLocked(info, callingUid, callingPid,
1331 pkg, tag, id, info.userid);
1332 }
Adam Lesinskie8240262014-03-26 16:01:00 -07001333 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001334 } finally {
1335 Binder.restoreCallingIdentity(identity);
1336 }
1337 }
1338
1339 /**
1340 * Allow an INotificationListener to request the list of outstanding notifications seen by
1341 * the current user. Useful when starting up, after which point the listener callbacks
1342 * should be used.
1343 *
1344 * @param token The binder for the listener, to check that the caller is allowed
Chris Wrenf9536642014-04-17 10:01:54 -04001345 * @param keys the notification keys to fetch, or null for all active notifications.
1346 * @returns The return value will contain the notifications specified in keys, in that
1347 * order, or if keys is null, all the notifications, in natural order.
Adam Lesinski182f73f2013-12-05 16:48:06 -08001348 */
1349 @Override
1350 public StatusBarNotification[] getActiveNotificationsFromListener(
John Spurlocka4294292014-03-24 18:02:32 -04001351 INotificationListener token, String[] keys) {
Adam Lesinski182f73f2013-12-05 16:48:06 -08001352 synchronized (mNotificationList) {
John Spurlock7340fc82014-04-24 18:50:12 -04001353 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
John Spurlocka4294292014-03-24 18:02:32 -04001354 final ArrayList<StatusBarNotification> list
1355 = new ArrayList<StatusBarNotification>();
1356 if (keys == null) {
1357 final int N = mNotificationList.size();
1358 for (int i=0; i<N; i++) {
1359 StatusBarNotification sbn = mNotificationList.get(i).sbn;
John Spurlock7340fc82014-04-24 18:50:12 -04001360 if (info.enabledAndUserMatches(sbn.getUserId())) {
John Spurlocka4294292014-03-24 18:02:32 -04001361 list.add(sbn);
1362 }
1363 }
1364 } else {
1365 final int N = keys.length;
1366 for (int i=0; i<N; i++) {
1367 NotificationRecord r = mNotificationsByKey.get(keys[i]);
John Spurlock7340fc82014-04-24 18:50:12 -04001368 if (r != null && info.enabledAndUserMatches(r.sbn.getUserId())) {
John Spurlocka4294292014-03-24 18:02:32 -04001369 list.add(r.sbn);
1370 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001371 }
1372 }
Adam Lesinski350159c2014-03-27 11:15:11 -07001373 return list.toArray(new StatusBarNotification[list.size()]);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001374 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001375 }
1376
1377 @Override
John Spurlocka4294292014-03-24 18:02:32 -04001378 public String[] getActiveNotificationKeysFromListener(INotificationListener token) {
Chris Wrenf9536642014-04-17 10:01:54 -04001379 return NotificationManagerService.this.getActiveNotificationKeys(token);
John Spurlocka4294292014-03-24 18:02:32 -04001380 }
1381
1382 @Override
John Spurlock056c5192014-04-20 21:52:01 -04001383 public ZenModeConfig getZenModeConfig() {
John Spurlock7340fc82014-04-24 18:50:12 -04001384 checkCallerIsSystem();
John Spurlock056c5192014-04-20 21:52:01 -04001385 return mZenModeHelper.getConfig();
1386 }
1387
1388 @Override
1389 public boolean setZenModeConfig(ZenModeConfig config) {
John Spurlock7340fc82014-04-24 18:50:12 -04001390 checkCallerIsSystem();
John Spurlock056c5192014-04-20 21:52:01 -04001391 return mZenModeHelper.setConfig(config);
1392 }
1393
1394 @Override
John Spurlocke77bb362014-04-26 10:24:59 -04001395 public void notifyConditions(String pkg, IConditionProvider provider,
1396 Condition[] conditions) {
1397 final ManagedServiceInfo info = mConditionProviders.checkServiceToken(provider);
1398 checkCallerIsSystemOrSameApp(pkg);
1399 final long identity = Binder.clearCallingIdentity();
1400 try {
1401 mConditionProviders.notifyConditions(pkg, info, conditions);
1402 } finally {
1403 Binder.restoreCallingIdentity(identity);
1404 }
1405 }
1406
1407 @Override
John Spurlock3b98b3f2014-05-01 09:08:48 -04001408 public void requestZenModeConditions(IConditionListener callback, int relevance) {
John Spurlocke77bb362014-04-26 10:24:59 -04001409 enforceSystemOrSystemUI("INotificationManager.requestZenModeConditions");
John Spurlock3b98b3f2014-05-01 09:08:48 -04001410 mConditionProviders.requestZenModeConditions(callback, relevance);
John Spurlocke77bb362014-04-26 10:24:59 -04001411 }
1412
1413 @Override
1414 public void setZenModeCondition(Uri conditionId) {
1415 enforceSystemOrSystemUI("INotificationManager.setZenModeCondition");
John Spurlockaf8d6c42014-05-07 17:49:08 -04001416 final long identity = Binder.clearCallingIdentity();
1417 try {
1418 mConditionProviders.setZenModeCondition(conditionId);
1419 } finally {
1420 Binder.restoreCallingIdentity(identity);
1421 }
John Spurlocke77bb362014-04-26 10:24:59 -04001422 }
1423
John Spurlock3b98b3f2014-05-01 09:08:48 -04001424 @Override
1425 public void setAutomaticZenModeConditions(Uri[] conditionIds) {
1426 enforceSystemOrSystemUI("INotificationManager.setAutomaticZenModeConditions");
1427 mConditionProviders.setAutomaticZenModeConditions(conditionIds);
1428 }
1429
1430 @Override
1431 public Condition[] getAutomaticZenModeConditions() {
1432 enforceSystemOrSystemUI("INotificationManager.getAutomaticZenModeConditions");
1433 return mConditionProviders.getAutomaticZenModeConditions();
1434 }
1435
John Spurlocke77bb362014-04-26 10:24:59 -04001436 private void enforceSystemOrSystemUI(String message) {
1437 if (isCallerSystem()) return;
1438 getContext().enforceCallingPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
1439 message);
John Spurlock7340fc82014-04-24 18:50:12 -04001440 }
1441
1442 @Override
Adam Lesinski182f73f2013-12-05 16:48:06 -08001443 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1444 if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
1445 != PackageManager.PERMISSION_GRANTED) {
1446 pw.println("Permission Denial: can't dump NotificationManager from from pid="
1447 + Binder.getCallingPid()
1448 + ", uid=" + Binder.getCallingUid());
1449 return;
1450 }
1451
1452 dumpImpl(pw);
1453 }
1454 };
1455
Chris Wrenf9536642014-04-17 10:01:54 -04001456 private String[] getActiveNotificationKeys(INotificationListener token) {
1457 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1458 final ArrayList<String> keys = new ArrayList<String>();
1459 if (info.isEnabledForCurrentProfiles()) {
1460 synchronized (mNotificationList) {
1461 final int N = mNotificationList.size();
1462 for (int i = 0; i < N; i++) {
1463 final StatusBarNotification sbn = mNotificationList.get(i).sbn;
1464 if (info.enabledAndUserMatches(sbn.getUserId())) {
1465 keys.add(sbn.getKey());
1466 }
John Spurlocka4294292014-03-24 18:02:32 -04001467 }
1468 }
John Spurlocka4294292014-03-24 18:02:32 -04001469 }
Chris Wrenf9536642014-04-17 10:01:54 -04001470 return keys.toArray(new String[keys.size()]);
John Spurlocka4294292014-03-24 18:02:32 -04001471 }
1472
Adam Lesinski182f73f2013-12-05 16:48:06 -08001473 void dumpImpl(PrintWriter pw) {
1474 pw.println("Current Notification Manager state:");
1475
Adam Lesinski182f73f2013-12-05 16:48:06 -08001476 int N;
1477
1478 synchronized (mToastQueue) {
1479 N = mToastQueue.size();
1480 if (N > 0) {
1481 pw.println(" Toast Queue:");
1482 for (int i=0; i<N; i++) {
1483 mToastQueue.get(i).dump(pw, " ");
1484 }
1485 pw.println(" ");
1486 }
1487
1488 }
1489
1490 synchronized (mNotificationList) {
1491 N = mNotificationList.size();
1492 if (N > 0) {
1493 pw.println(" Notification List:");
1494 for (int i=0; i<N; i++) {
1495 mNotificationList.get(i).dump(pw, " ", getContext());
1496 }
1497 pw.println(" ");
1498 }
1499
1500 N = mLights.size();
1501 if (N > 0) {
1502 pw.println(" Lights List:");
1503 for (int i=0; i<N; i++) {
1504 pw.println(" " + mLights.get(i));
1505 }
1506 pw.println(" ");
1507 }
1508
1509 pw.println(" mSoundNotification=" + mSoundNotification);
1510 pw.println(" mVibrateNotification=" + mVibrateNotification);
John Spurlocke677d712014-02-13 12:52:19 -05001511 pw.println(" mDisableNotificationAlerts=" + mDisableNotificationAlerts);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001512 pw.println(" mSystemReady=" + mSystemReady);
1513 pw.println(" mArchive=" + mArchive.toString());
1514 Iterator<StatusBarNotification> iter = mArchive.descendingIterator();
1515 int i=0;
1516 while (iter.hasNext()) {
1517 pw.println(" " + iter.next());
1518 if (++i >= 5) {
1519 if (iter.hasNext()) pw.println(" ...");
1520 break;
1521 }
1522 }
1523
Christoph Studer546bec82014-03-14 12:17:12 +01001524 pw.println("\n Usage Stats:");
1525 mUsageStats.dump(pw, " ");
1526
John Spurlock056c5192014-04-20 21:52:01 -04001527 pw.println("\n Zen Mode:");
1528 mZenModeHelper.dump(pw, " ");
John Spurlocke77bb362014-04-26 10:24:59 -04001529
1530 pw.println("\n Notification listeners:");
1531 mListeners.dump(pw);
1532
1533 pw.println("\n Condition providers:");
1534 mConditionProviders.dump(pw);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001535 }
1536 }
1537
Adam Lesinski182f73f2013-12-05 16:48:06 -08001538 /**
1539 * The private API only accessible to the system process.
1540 */
1541 private final NotificationManagerInternal mInternalService = new NotificationManagerInternal() {
1542 @Override
Christoph Studer8fd7f1e2014-04-11 17:35:05 -04001543 public void enqueueNotification(String pkg, String opPkg, int callingUid, int callingPid,
Adam Lesinski182f73f2013-12-05 16:48:06 -08001544 String tag, int id, Notification notification, int[] idReceived, int userId) {
Christoph Studer8fd7f1e2014-04-11 17:35:05 -04001545 enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, notification,
Adam Lesinski182f73f2013-12-05 16:48:06 -08001546 idReceived, userId);
1547 }
1548 };
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001549
Christoph Studer8fd7f1e2014-04-11 17:35:05 -04001550 void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,
Scott Greenwald9b05c612013-06-25 23:44:05 -04001551 final int callingPid, final String tag, final int id, final Notification notification,
Adam Lesinski182f73f2013-12-05 16:48:06 -08001552 int[] idOut, int incomingUserId) {
Daniel Sandler0da673f2012-04-11 12:33:16 -04001553 if (DBG) {
Adam Lesinski182f73f2013-12-05 16:48:06 -08001554 Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id
1555 + " notification=" + notification);
Daniel Sandler0da673f2012-04-11 12:33:16 -04001556 }
John Spurlock7340fc82014-04-24 18:50:12 -04001557 checkCallerIsSystemOrSameApp(pkg);
1558 final boolean isSystemNotification = isUidSystem(callingUid) || ("android".equals(pkg));
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001559
Scott Greenwald9b05c612013-06-25 23:44:05 -04001560 final int userId = ActivityManager.handleIncomingUser(callingPid,
1561 callingUid, incomingUserId, true, false, "enqueueNotification", pkg);
Jeff Sharkey65c4a2b2012-09-25 17:22:27 -07001562 final UserHandle user = new UserHandle(userId);
Dianne Hackborn41203752012-08-31 14:05:51 -07001563
Joe Onoratobd73d012010-06-04 11:44:54 -07001564 // Limit the number of notifications that any given package except the android
1565 // package can enqueue. Prevents DOS attacks and deals with leaks.
Daniel Sandler0da673f2012-04-11 12:33:16 -04001566 if (!isSystemNotification) {
Joe Onoratobd73d012010-06-04 11:44:54 -07001567 synchronized (mNotificationList) {
1568 int count = 0;
1569 final int N = mNotificationList.size();
1570 for (int i=0; i<N; i++) {
1571 final NotificationRecord r = mNotificationList.get(i);
Daniel Sandler4f91efd2013-04-25 16:38:41 -04001572 if (r.sbn.getPackageName().equals(pkg) && r.sbn.getUserId() == userId) {
Joe Onoratobd73d012010-06-04 11:44:54 -07001573 count++;
1574 if (count >= MAX_PACKAGE_NOTIFICATIONS) {
1575 Slog.e(TAG, "Package has already posted " + count
1576 + " notifications. Not showing more. package=" + pkg);
1577 return;
1578 }
1579 }
1580 }
1581 }
1582 }
1583
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001584 // This conditional is a dirty hack to limit the logging done on
1585 // behalf of the download manager without affecting other apps.
1586 if (!pkg.equals("com.android.providers.downloads")
1587 || Log.isLoggable("DownloadManager", Log.VERBOSE)) {
John Spurlocke6a7d932014-03-13 12:29:00 -04001588 EventLogTags.writeNotificationEnqueue(callingUid, callingPid,
1589 pkg, id, tag, userId, notification.toString());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001590 }
1591
1592 if (pkg == null || notification == null) {
1593 throw new IllegalArgumentException("null not allowed: pkg=" + pkg
1594 + " id=" + id + " notification=" + notification);
1595 }
1596 if (notification.icon != 0) {
1597 if (notification.contentView == null) {
1598 throw new IllegalArgumentException("contentView required: pkg=" + pkg
1599 + " id=" + id + " notification=" + notification);
1600 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001601 }
1602
Scott Greenwald9b05c612013-06-25 23:44:05 -04001603 mHandler.post(new Runnable() {
1604 @Override
1605 public void run() {
Daniel Sandler0da673f2012-04-11 12:33:16 -04001606
Scott Greenwald9b05c612013-06-25 23:44:05 -04001607 // === Scoring ===
Daniel Sandler0da673f2012-04-11 12:33:16 -04001608
Scott Greenwald9b05c612013-06-25 23:44:05 -04001609 // 0. Sanitize inputs
1610 notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN,
1611 Notification.PRIORITY_MAX);
1612 // Migrate notification flags to scores
1613 if (0 != (notification.flags & Notification.FLAG_HIGH_PRIORITY)) {
1614 if (notification.priority < Notification.PRIORITY_MAX) {
1615 notification.priority = Notification.PRIORITY_MAX;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001616 }
Scott Greenwald9b05c612013-06-25 23:44:05 -04001617 } else if (SCORE_ONGOING_HIGHER &&
1618 0 != (notification.flags & Notification.FLAG_ONGOING_EVENT)) {
1619 if (notification.priority < Notification.PRIORITY_HIGH) {
1620 notification.priority = Notification.PRIORITY_HIGH;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001621 }
1622 }
Daniel Sandlerfde19b12013-01-17 00:21:05 -05001623
Scott Greenwald9b05c612013-06-25 23:44:05 -04001624 // 1. initial score: buckets of 10, around the app
1625 int score = notification.priority * NOTIFICATION_PRIORITY_MULTIPLIER; //[-20..20]
1626
Chris Wrenf9536642014-04-17 10:01:54 -04001627 // 2. extract ranking signals from the notification data
1628 final StatusBarNotification n = new StatusBarNotification(
1629 pkg, opPkg, id, tag, callingUid, callingPid, score, notification,
1630 user);
1631 NotificationRecord r = new NotificationRecord(n);
1632 if (!mSignalExtractors.isEmpty()) {
1633 for (NotificationSignalExtractor extractor : mSignalExtractors) {
Scott Greenwald9a05b312013-06-28 00:37:54 -04001634 try {
Chris Wrenf9536642014-04-17 10:01:54 -04001635 RankingFuture future = extractor.process(r);
1636 scheduleRankingReconsideration(future);
Scott Greenwald9a05b312013-06-28 00:37:54 -04001637 } catch (Throwable t) {
Chris Wrenf9536642014-04-17 10:01:54 -04001638 Slog.w(TAG, "NotificationSignalExtractor failed.", t);
Scott Greenwald9a05b312013-06-28 00:37:54 -04001639 }
1640 }
Scott Greenwald9a05b312013-06-28 00:37:54 -04001641 }
1642
Chris Wrenf9536642014-04-17 10:01:54 -04001643 // 3. Apply local rules
Scott Greenwald9a05b312013-06-28 00:37:54 -04001644
Scott Greenwald9b05c612013-06-25 23:44:05 -04001645 // blocked apps
1646 if (ENABLE_BLOCKED_NOTIFICATIONS && !noteNotificationOp(pkg, callingUid)) {
1647 if (!isSystemNotification) {
1648 score = JUNK_SCORE;
1649 Slog.e(TAG, "Suppressing notification from package " + pkg
1650 + " by user request.");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001651 }
Daniel Sandlerd4d2de22012-11-14 11:25:46 -08001652 }
1653
Scott Greenwald9b05c612013-06-25 23:44:05 -04001654 if (score < SCORE_DISPLAY_THRESHOLD) {
1655 // Notification will be blocked because the score is too low.
1656 return;
1657 }
1658
John Spurlocke677d712014-02-13 12:52:19 -05001659 // Is this notification intercepted by zen mode?
John Spurlock056c5192014-04-20 21:52:01 -04001660 final boolean intercept = mZenModeHelper.shouldIntercept(pkg, notification);
John Spurlocke677d712014-02-13 12:52:19 -05001661 notification.extras.putBoolean(EXTRA_INTERCEPT, intercept);
Scott Greenwald9b05c612013-06-25 23:44:05 -04001662
John Spurlocke677d712014-02-13 12:52:19 -05001663 // Should this notification make noise, vibe, or use the LED?
1664 final boolean canInterrupt = (score >= SCORE_INTERRUPTION_THRESHOLD) && !intercept;
John Spurlockc8d7ab92014-05-05 09:22:57 -04001665 if (DBG || intercept) Slog.v(TAG,
1666 "pkg=" + pkg + " canInterrupt=" + canInterrupt + " intercept=" + intercept);
Scott Greenwald9b05c612013-06-25 23:44:05 -04001667 synchronized (mNotificationList) {
Scott Greenwald9b05c612013-06-25 23:44:05 -04001668 NotificationRecord old = null;
Scott Greenwald9b05c612013-06-25 23:44:05 -04001669 int index = indexOfNotificationLocked(pkg, tag, id, userId);
1670 if (index < 0) {
1671 mNotificationList.add(r);
Christoph Studer546bec82014-03-14 12:17:12 +01001672 mUsageStats.registerPostedByApp(r);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001673 } else {
Christoph Studerd89188d2014-04-03 00:02:39 +02001674 old = mNotificationList.get(index);
1675 mNotificationList.set(index, r);
Christoph Studer061dee22014-05-09 12:28:55 +02001676 mUsageStats.registerUpdatedByApp(r, old);
Scott Greenwald9b05c612013-06-25 23:44:05 -04001677 // Make sure we don't lose the foreground service state.
1678 if (old != null) {
1679 notification.flags |=
1680 old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;
1681 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001682 }
John Spurlocka4294292014-03-24 18:02:32 -04001683 if (old != null) {
1684 mNotificationsByKey.remove(old.sbn.getKey());
1685 }
1686 mNotificationsByKey.put(n.getKey(), r);
Scott Greenwald9b05c612013-06-25 23:44:05 -04001687
Chris Wrenf9536642014-04-17 10:01:54 -04001688 Collections.sort(mNotificationList, mRankingComparator);
1689
Scott Greenwald9b05c612013-06-25 23:44:05 -04001690 // Ensure if this is a foreground service that the proper additional
1691 // flags are set.
1692 if ((notification.flags&Notification.FLAG_FOREGROUND_SERVICE) != 0) {
1693 notification.flags |= Notification.FLAG_ONGOING_EVENT
1694 | Notification.FLAG_NO_CLEAR;
1695 }
1696
1697 final int currentUser;
1698 final long token = Binder.clearCallingIdentity();
1699 try {
1700 currentUser = ActivityManager.getCurrentUser();
1701 } finally {
1702 Binder.restoreCallingIdentity(token);
1703 }
1704
1705 if (notification.icon != 0) {
1706 if (old != null && old.statusBarKey != null) {
1707 r.statusBarKey = old.statusBarKey;
Adam Lesinski182f73f2013-12-05 16:48:06 -08001708 final long identity = Binder.clearCallingIdentity();
Scott Greenwald9b05c612013-06-25 23:44:05 -04001709 try {
1710 mStatusBar.updateNotification(r.statusBarKey, n);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001711 } finally {
Scott Greenwald9b05c612013-06-25 23:44:05 -04001712 Binder.restoreCallingIdentity(identity);
1713 }
1714 } else {
Adam Lesinski182f73f2013-12-05 16:48:06 -08001715 final long identity = Binder.clearCallingIdentity();
Scott Greenwald9b05c612013-06-25 23:44:05 -04001716 try {
1717 r.statusBarKey = mStatusBar.addNotification(n);
1718 if ((n.getNotification().flags & Notification.FLAG_SHOW_LIGHTS) != 0
1719 && canInterrupt) {
1720 mAttentionLight.pulse();
1721 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001722 } finally {
Scott Greenwald9b05c612013-06-25 23:44:05 -04001723 Binder.restoreCallingIdentity(identity);
1724 }
1725 }
1726 // Send accessibility events only for the current user.
1727 if (currentUser == userId) {
1728 sendAccessibilityEvent(notification, pkg);
1729 }
1730
John Spurlockb408e8e2014-04-23 21:12:45 -04001731 mListeners.notifyPostedLocked(r.sbn);
Scott Greenwald9b05c612013-06-25 23:44:05 -04001732 } else {
1733 Slog.e(TAG, "Not posting notification with icon==0: " + notification);
1734 if (old != null && old.statusBarKey != null) {
Adam Lesinski182f73f2013-12-05 16:48:06 -08001735 final long identity = Binder.clearCallingIdentity();
Scott Greenwald9b05c612013-06-25 23:44:05 -04001736 try {
1737 mStatusBar.removeNotification(old.statusBarKey);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001738 } finally {
Scott Greenwald9b05c612013-06-25 23:44:05 -04001739 Binder.restoreCallingIdentity(identity);
1740 }
1741
John Spurlockb408e8e2014-04-23 21:12:45 -04001742 mListeners.notifyRemovedLocked(r.sbn);
Scott Greenwald9b05c612013-06-25 23:44:05 -04001743 }
1744 // ATTENTION: in a future release we will bail out here
Adam Lesinski182f73f2013-12-05 16:48:06 -08001745 // so that we do not play sounds, show lights, etc. for invalid
1746 // notifications
Scott Greenwald9b05c612013-06-25 23:44:05 -04001747 Slog.e(TAG, "WARNING: In a future release this will crash the app: "
1748 + n.getPackageName());
1749 }
1750
1751 // If we're not supposed to beep, vibrate, etc. then don't.
John Spurlocke677d712014-02-13 12:52:19 -05001752 if (!mDisableNotificationAlerts
Scott Greenwald9b05c612013-06-25 23:44:05 -04001753 && (!(old != null
1754 && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 ))
1755 && (r.getUserId() == UserHandle.USER_ALL ||
Kenny Guya263e4e2014-03-03 18:24:03 +00001756 (r.getUserId() == userId && r.getUserId() == currentUser) ||
John Spurlockb408e8e2014-04-23 21:12:45 -04001757 mUserProfiles.isCurrentProfile(r.getUserId()))
Scott Greenwald9b05c612013-06-25 23:44:05 -04001758 && canInterrupt
Adam Lesinski182f73f2013-12-05 16:48:06 -08001759 && mSystemReady
1760 && mAudioManager != null) {
John Spurlocke677d712014-02-13 12:52:19 -05001761 if (DBG) Slog.v(TAG, "Interrupting!");
Scott Greenwald9b05c612013-06-25 23:44:05 -04001762 // sound
1763
1764 // should we use the default notification sound? (indicated either by
1765 // DEFAULT_SOUND or because notification.sound is pointing at
1766 // Settings.System.NOTIFICATION_SOUND)
1767 final boolean useDefaultSound =
1768 (notification.defaults & Notification.DEFAULT_SOUND) != 0 ||
1769 Settings.System.DEFAULT_NOTIFICATION_URI
1770 .equals(notification.sound);
1771
1772 Uri soundUri = null;
1773 boolean hasValidSound = false;
1774
1775 if (useDefaultSound) {
1776 soundUri = Settings.System.DEFAULT_NOTIFICATION_URI;
1777
1778 // check to see if the default notification sound is silent
Adam Lesinski182f73f2013-12-05 16:48:06 -08001779 ContentResolver resolver = getContext().getContentResolver();
Scott Greenwald9b05c612013-06-25 23:44:05 -04001780 hasValidSound = Settings.System.getString(resolver,
1781 Settings.System.NOTIFICATION_SOUND) != null;
1782 } else if (notification.sound != null) {
1783 soundUri = notification.sound;
1784 hasValidSound = (soundUri != null);
1785 }
1786
1787 if (hasValidSound) {
Adam Lesinski182f73f2013-12-05 16:48:06 -08001788 boolean looping =
1789 (notification.flags & Notification.FLAG_INSISTENT) != 0;
Scott Greenwald9b05c612013-06-25 23:44:05 -04001790 int audioStreamType;
1791 if (notification.audioStreamType >= 0) {
1792 audioStreamType = notification.audioStreamType;
1793 } else {
1794 audioStreamType = DEFAULT_STREAM_TYPE;
1795 }
1796 mSoundNotification = r;
1797 // do not play notifications if stream volume is 0 (typically because
Jean-Michel Trivi23805662013-07-31 14:19:18 -07001798 // ringer mode is silent) or if there is a user of exclusive audio focus
Adam Lesinski182f73f2013-12-05 16:48:06 -08001799 if ((mAudioManager.getStreamVolume(audioStreamType) != 0)
1800 && !mAudioManager.isAudioFocusExclusive()) {
Scott Greenwald9b05c612013-06-25 23:44:05 -04001801 final long identity = Binder.clearCallingIdentity();
1802 try {
Adam Lesinski182f73f2013-12-05 16:48:06 -08001803 final IRingtonePlayer player =
1804 mAudioManager.getRingtonePlayer();
Scott Greenwald9b05c612013-06-25 23:44:05 -04001805 if (player != null) {
John Spurlocke677d712014-02-13 12:52:19 -05001806 if (DBG) Slog.v(TAG, "Playing sound " + soundUri
1807 + " on stream " + audioStreamType);
Scott Greenwald9b05c612013-06-25 23:44:05 -04001808 player.playAsync(soundUri, user, looping, audioStreamType);
1809 }
1810 } catch (RemoteException e) {
1811 } finally {
1812 Binder.restoreCallingIdentity(identity);
1813 }
1814 }
1815 }
1816
1817 // vibrate
1818 // Does the notification want to specify its own vibration?
1819 final boolean hasCustomVibrate = notification.vibrate != null;
1820
1821 // new in 4.2: if there was supposed to be a sound and we're in vibrate
1822 // mode, and no other vibration is specified, we fall back to vibration
1823 final boolean convertSoundToVibration =
1824 !hasCustomVibrate
1825 && hasValidSound
Adam Lesinski182f73f2013-12-05 16:48:06 -08001826 && (mAudioManager.getRingerMode()
Scott Greenwald9b05c612013-06-25 23:44:05 -04001827 == AudioManager.RINGER_MODE_VIBRATE);
1828
1829 // The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback.
1830 final boolean useDefaultVibrate =
1831 (notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
1832
1833 if ((useDefaultVibrate || convertSoundToVibration || hasCustomVibrate)
Adam Lesinski182f73f2013-12-05 16:48:06 -08001834 && !(mAudioManager.getRingerMode()
Scott Greenwald9b05c612013-06-25 23:44:05 -04001835 == AudioManager.RINGER_MODE_SILENT)) {
1836 mVibrateNotification = r;
1837
1838 if (useDefaultVibrate || convertSoundToVibration) {
1839 // Escalate privileges so we can use the vibrator even if the
1840 // notifying app does not have the VIBRATE permission.
1841 long identity = Binder.clearCallingIdentity();
1842 try {
Christoph Studer8fd7f1e2014-04-11 17:35:05 -04001843 mVibrator.vibrate(r.sbn.getUid(), r.sbn.getOpPkg(),
Scott Greenwald9b05c612013-06-25 23:44:05 -04001844 useDefaultVibrate ? mDefaultVibrationPattern
1845 : mFallbackVibrationPattern,
1846 ((notification.flags & Notification.FLAG_INSISTENT) != 0)
John Spurlock1af30c72014-03-10 08:33:35 -04001847 ? 0: -1, notification.audioStreamType);
Scott Greenwald9b05c612013-06-25 23:44:05 -04001848 } finally {
1849 Binder.restoreCallingIdentity(identity);
1850 }
1851 } else if (notification.vibrate.length > 1) {
1852 // If you want your own vibration pattern, you need the VIBRATE
1853 // permission
Christoph Studer8fd7f1e2014-04-11 17:35:05 -04001854 mVibrator.vibrate(r.sbn.getUid(), r.sbn.getOpPkg(),
Scott Greenwald9b05c612013-06-25 23:44:05 -04001855 notification.vibrate,
1856 ((notification.flags & Notification.FLAG_INSISTENT) != 0)
John Spurlock1af30c72014-03-10 08:33:35 -04001857 ? 0: -1, notification.audioStreamType);
Scott Greenwald9b05c612013-06-25 23:44:05 -04001858 }
1859 }
1860 }
1861
1862 // light
1863 // the most recent thing gets the light
1864 mLights.remove(old);
1865 if (mLedNotification == old) {
1866 mLedNotification = null;
1867 }
1868 //Slog.i(TAG, "notification.lights="
1869 // + ((old.notification.lights.flags & Notification.FLAG_SHOW_LIGHTS)
1870 // != 0));
1871 if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0
1872 && canInterrupt) {
1873 mLights.add(r);
1874 updateLightsLocked();
1875 } else {
1876 if (old != null
1877 && ((old.getFlags() & Notification.FLAG_SHOW_LIGHTS) != 0)) {
1878 updateLightsLocked();
Eric Laurent524dc042009-11-27 05:07:55 -08001879 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001880 }
1881 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001882 }
Scott Greenwald9b05c612013-06-25 23:44:05 -04001883 });
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001884
1885 idOut[0] = id;
1886 }
1887
Adam Lesinski182f73f2013-12-05 16:48:06 -08001888 void showNextToastLocked() {
1889 ToastRecord record = mToastQueue.get(0);
1890 while (record != null) {
1891 if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback);
1892 try {
1893 record.callback.show();
1894 scheduleTimeoutLocked(record);
1895 return;
1896 } catch (RemoteException e) {
1897 Slog.w(TAG, "Object died trying to show notification " + record.callback
1898 + " in package " + record.pkg);
1899 // remove it from the list and let the process die
1900 int index = mToastQueue.indexOf(record);
1901 if (index >= 0) {
1902 mToastQueue.remove(index);
1903 }
1904 keepProcessAliveLocked(record.pid);
1905 if (mToastQueue.size() > 0) {
1906 record = mToastQueue.get(0);
1907 } else {
1908 record = null;
1909 }
1910 }
1911 }
1912 }
1913
1914 void cancelToastLocked(int index) {
1915 ToastRecord record = mToastQueue.get(index);
1916 try {
1917 record.callback.hide();
1918 } catch (RemoteException e) {
1919 Slog.w(TAG, "Object died trying to hide notification " + record.callback
1920 + " in package " + record.pkg);
1921 // don't worry about this, we're about to remove it from
1922 // the list anyway
1923 }
1924 mToastQueue.remove(index);
1925 keepProcessAliveLocked(record.pid);
1926 if (mToastQueue.size() > 0) {
1927 // Show the next one. If the callback fails, this will remove
1928 // it from the list, so don't assume that the list hasn't changed
1929 // after this point.
1930 showNextToastLocked();
1931 }
1932 }
1933
1934 private void scheduleTimeoutLocked(ToastRecord r)
1935 {
1936 mHandler.removeCallbacksAndMessages(r);
1937 Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r);
1938 long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY;
1939 mHandler.sendMessageDelayed(m, delay);
1940 }
1941
1942 private void handleTimeout(ToastRecord record)
1943 {
1944 if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback);
1945 synchronized (mToastQueue) {
1946 int index = indexOfToastLocked(record.pkg, record.callback);
1947 if (index >= 0) {
1948 cancelToastLocked(index);
1949 }
1950 }
1951 }
1952
1953 // lock on mToastQueue
1954 int indexOfToastLocked(String pkg, ITransientNotification callback)
1955 {
1956 IBinder cbak = callback.asBinder();
1957 ArrayList<ToastRecord> list = mToastQueue;
1958 int len = list.size();
1959 for (int i=0; i<len; i++) {
1960 ToastRecord r = list.get(i);
1961 if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) {
1962 return i;
1963 }
1964 }
1965 return -1;
1966 }
1967
1968 // lock on mToastQueue
1969 void keepProcessAliveLocked(int pid)
1970 {
1971 int toastCount = 0; // toasts from this pid
1972 ArrayList<ToastRecord> list = mToastQueue;
1973 int N = list.size();
1974 for (int i=0; i<N; i++) {
1975 ToastRecord r = list.get(i);
1976 if (r.pid == pid) {
1977 toastCount++;
1978 }
1979 }
1980 try {
1981 mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0);
1982 } catch (RemoteException e) {
1983 // Shouldn't happen.
1984 }
1985 }
1986
Chris Wrenf9536642014-04-17 10:01:54 -04001987 private void scheduleRankingReconsideration(RankingFuture future) {
1988 if (future != null) {
1989 Message m = Message.obtain(mRankingHandler, MESSAGE_RECONSIDER_RANKING, future);
1990 long delay = future.getDelay(TimeUnit.MILLISECONDS);
1991 mRankingHandler.sendMessageDelayed(m, delay);
1992 }
1993 }
1994
1995 private void handleRankingReconsideration(Message message) {
1996 if (!(message.obj instanceof RankingFuture)) return;
1997
1998 RankingFuture future = (RankingFuture) message.obj;
1999 future.run();
2000 try {
2001 NotificationRecord record = future.get();
2002 synchronized (mNotificationList) {
2003 int before = mNotificationList.indexOf(record);
2004 if (before != -1) {
2005 Collections.sort(mNotificationList, mRankingComparator);
2006 int after = mNotificationList.indexOf(record);
2007
2008 if (before != after) {
2009 scheduleSendRankingUpdate();
2010 }
2011 }
2012 }
2013 } catch (InterruptedException e) {
2014 // we're running the future explicitly, so this should never happen
2015 } catch (ExecutionException e) {
2016 // we're running the future explicitly, so this should never happen
2017 }
2018 }
2019
2020 private void scheduleSendRankingUpdate() {
2021 mHandler.removeMessages(MESSAGE_SEND_RANKING_UPDATE);
2022 Message m = Message.obtain(mHandler, MESSAGE_SEND_RANKING_UPDATE);
2023 mHandler.sendMessage(m);
2024 }
2025
2026 private void handleSendRankingUpdate() {
2027 synchronized (mNotificationList) {
2028 final int N = mNotificationList.size();
2029 ArrayList<StatusBarNotification> sbns =
2030 new ArrayList<StatusBarNotification>(N);
2031 for (int i = 0; i < N; i++ ) {
2032 sbns.add(mNotificationList.get(i).sbn);
2033 }
2034 mListeners.notifyOrderUpdateLocked(sbns);
2035 }
2036 }
2037
Adam Lesinski182f73f2013-12-05 16:48:06 -08002038 private final class WorkerHandler extends Handler
2039 {
2040 @Override
2041 public void handleMessage(Message msg)
2042 {
2043 switch (msg.what)
2044 {
2045 case MESSAGE_TIMEOUT:
2046 handleTimeout((ToastRecord)msg.obj);
2047 break;
John Spurlock056c5192014-04-20 21:52:01 -04002048 case MESSAGE_SAVE_POLICY_FILE:
2049 handleSavePolicyFile();
2050 break;
Chris Wrenf9536642014-04-17 10:01:54 -04002051 case MESSAGE_SEND_RANKING_UPDATE:
2052 handleSendRankingUpdate();
2053 break;
2054 }
2055 }
2056
2057 }
2058
2059 private final class RankingWorkerHandler extends Handler
2060 {
2061 public RankingWorkerHandler(Looper looper) {
2062 super(looper);
2063 }
2064
2065 @Override
2066 public void handleMessage(Message msg) {
2067 switch (msg.what) {
2068 case MESSAGE_RECONSIDER_RANKING:
2069 handleRankingReconsideration(msg);
2070 break;
Adam Lesinski182f73f2013-12-05 16:48:06 -08002071 }
2072 }
2073 }
2074
Adam Lesinski182f73f2013-12-05 16:48:06 -08002075 // Notifications
2076 // ============================================================================
2077 static int clamp(int x, int low, int high) {
2078 return (x < low) ? low : ((x > high) ? high : x);
2079 }
2080
2081 void sendAccessibilityEvent(Notification notification, CharSequence packageName) {
2082 AccessibilityManager manager = AccessibilityManager.getInstance(getContext());
svetoslavganov75986cf2009-05-14 22:28:01 -07002083 if (!manager.isEnabled()) {
2084 return;
2085 }
2086
2087 AccessibilityEvent event =
2088 AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
2089 event.setPackageName(packageName);
2090 event.setClassName(Notification.class.getName());
2091 event.setParcelableData(notification);
2092 CharSequence tickerText = notification.tickerText;
2093 if (!TextUtils.isEmpty(tickerText)) {
2094 event.getText().add(tickerText);
2095 }
2096
2097 manager.sendAccessibilityEvent(event);
2098 }
2099
Christoph Studer546bec82014-03-14 12:17:12 +01002100 private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason) {
Joe Onorato46439ce2010-11-19 13:56:21 -08002101 // tell the app
2102 if (sendDelete) {
Daniel Sandlerfde19b12013-01-17 00:21:05 -05002103 if (r.getNotification().deleteIntent != null) {
Joe Onorato46439ce2010-11-19 13:56:21 -08002104 try {
Daniel Sandlerfde19b12013-01-17 00:21:05 -05002105 r.getNotification().deleteIntent.send();
Joe Onorato46439ce2010-11-19 13:56:21 -08002106 } catch (PendingIntent.CanceledException ex) {
2107 // do nothing - there's no relevant way to recover, and
2108 // no reason to let this propagate
Daniel Sandler4f91efd2013-04-25 16:38:41 -04002109 Slog.w(TAG, "canceled PendingIntent for " + r.sbn.getPackageName(), ex);
Joe Onorato46439ce2010-11-19 13:56:21 -08002110 }
2111 }
2112 }
2113
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002114 // status bar
Daniel Sandlerfde19b12013-01-17 00:21:05 -05002115 if (r.getNotification().icon != 0) {
Adam Lesinski182f73f2013-12-05 16:48:06 -08002116 final long identity = Binder.clearCallingIdentity();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002117 try {
Joe Onorato0cbda992010-05-02 16:28:15 -07002118 mStatusBar.removeNotification(r.statusBarKey);
Adam Lesinski182f73f2013-12-05 16:48:06 -08002119 } finally {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002120 Binder.restoreCallingIdentity(identity);
2121 }
2122 r.statusBarKey = null;
John Spurlockb408e8e2014-04-23 21:12:45 -04002123 mListeners.notifyRemovedLocked(r.sbn);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002124 }
2125
2126 // sound
2127 if (mSoundNotification == r) {
2128 mSoundNotification = null;
Jeff Sharkey098d5802012-04-26 17:30:34 -07002129 final long identity = Binder.clearCallingIdentity();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002130 try {
Adam Lesinski182f73f2013-12-05 16:48:06 -08002131 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
Jeff Sharkey098d5802012-04-26 17:30:34 -07002132 if (player != null) {
2133 player.stopAsync();
2134 }
2135 } catch (RemoteException e) {
2136 } finally {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002137 Binder.restoreCallingIdentity(identity);
2138 }
2139 }
2140
2141 // vibrate
2142 if (mVibrateNotification == r) {
2143 mVibrateNotification = null;
2144 long identity = Binder.clearCallingIdentity();
2145 try {
2146 mVibrator.cancel();
2147 }
2148 finally {
2149 Binder.restoreCallingIdentity(identity);
2150 }
2151 }
2152
2153 // light
2154 mLights.remove(r);
2155 if (mLedNotification == r) {
2156 mLedNotification = null;
2157 }
Daniel Sandler23d7c702013-03-07 16:32:06 -05002158
Christoph Studer546bec82014-03-14 12:17:12 +01002159 // Record usage stats
2160 switch (reason) {
2161 case REASON_DELEGATE_CANCEL:
2162 case REASON_DELEGATE_CANCEL_ALL:
2163 case REASON_LISTENER_CANCEL:
2164 case REASON_LISTENER_CANCEL_ALL:
2165 mUsageStats.registerDismissedByUser(r);
2166 break;
2167 case REASON_NOMAN_CANCEL:
2168 case REASON_NOMAN_CANCEL_ALL:
2169 mUsageStats.registerRemovedByApp(r);
2170 break;
2171 case REASON_DELEGATE_CLICK:
2172 mUsageStats.registerCancelDueToClick(r);
2173 break;
2174 default:
2175 mUsageStats.registerCancelUnknown(r);
2176 break;
2177 }
2178
Daniel Sandler23d7c702013-03-07 16:32:06 -05002179 // Save it for users of getHistoricalNotifications()
2180 mArchive.record(r.sbn);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002181 }
2182
2183 /**
Dianne Hackbornd8a43f62009-08-17 23:33:56 -07002184 * Cancels a notification ONLY if it has all of the {@code mustHaveFlags}
Doug Zongkerab5c49c2009-12-04 10:31:43 -08002185 * and none of the {@code mustNotHaveFlags}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002186 */
John Spurlocke6a7d932014-03-13 12:29:00 -04002187 void cancelNotification(final int callingUid, final int callingPid,
2188 final String pkg, final String tag, final int id,
Svetoslav Ganov835835e2013-08-04 20:17:52 -07002189 final int mustHaveFlags, final int mustNotHaveFlags, final boolean sendDelete,
John Spurlock7340fc82014-04-24 18:50:12 -04002190 final int userId, final int reason, final ManagedServiceInfo listener) {
Svetoslav Ganov835835e2013-08-04 20:17:52 -07002191 // In enqueueNotificationInternal notifications are added by scheduling the
2192 // work on the worker handler. Hence, we also schedule the cancel on this
2193 // handler to avoid a scenario where an add notification call followed by a
2194 // remove notification call ends up in not removing the notification.
2195 mHandler.post(new Runnable() {
2196 @Override
2197 public void run() {
John Spurlocke6a7d932014-03-13 12:29:00 -04002198 EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, id, tag, userId,
2199 mustHaveFlags, mustNotHaveFlags, reason,
2200 listener == null ? null : listener.component.toShortString());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002201
Svetoslav Ganov835835e2013-08-04 20:17:52 -07002202 synchronized (mNotificationList) {
2203 int index = indexOfNotificationLocked(pkg, tag, id, userId);
2204 if (index >= 0) {
2205 NotificationRecord r = mNotificationList.get(index);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08002206
Christoph Studer546bec82014-03-14 12:17:12 +01002207 // Ideally we'd do this in the caller of this method. However, that would
2208 // require the caller to also find the notification.
2209 if (reason == REASON_DELEGATE_CLICK) {
2210 mUsageStats.registerClickedByUser(r);
2211 }
2212
Svetoslav Ganov835835e2013-08-04 20:17:52 -07002213 if ((r.getNotification().flags & mustHaveFlags) != mustHaveFlags) {
2214 return;
2215 }
2216 if ((r.getNotification().flags & mustNotHaveFlags) != 0) {
2217 return;
2218 }
2219
2220 mNotificationList.remove(index);
John Spurlocka4294292014-03-24 18:02:32 -04002221 mNotificationsByKey.remove(r.sbn.getKey());
Svetoslav Ganov835835e2013-08-04 20:17:52 -07002222
Christoph Studer546bec82014-03-14 12:17:12 +01002223 cancelNotificationLocked(r, sendDelete, reason);
Svetoslav Ganov835835e2013-08-04 20:17:52 -07002224 updateLightsLocked();
2225 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002226 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002227 }
Svetoslav Ganov835835e2013-08-04 20:17:52 -07002228 });
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002229 }
2230
2231 /**
Daniel Sandler321e9c52012-10-12 10:59:26 -07002232 * Determine whether the userId applies to the notification in question, either because
2233 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard).
2234 */
2235 private boolean notificationMatchesUserId(NotificationRecord r, int userId) {
2236 return
2237 // looking for USER_ALL notifications? match everything
2238 userId == UserHandle.USER_ALL
2239 // a notification sent to USER_ALL matches any query
Daniel Sandlerfde19b12013-01-17 00:21:05 -05002240 || r.getUserId() == UserHandle.USER_ALL
Daniel Sandler321e9c52012-10-12 10:59:26 -07002241 // an exact user match
Daniel Sandlerfde19b12013-01-17 00:21:05 -05002242 || r.getUserId() == userId;
Daniel Sandler321e9c52012-10-12 10:59:26 -07002243 }
2244
2245 /**
Kenny Guy3a7c4a52014-03-03 18:24:03 +00002246 * Determine whether the userId applies to the notification in question, either because
2247 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard) or
Kenny Guy2a764942014-04-02 13:29:20 +01002248 * because it matches one of the users profiles.
Kenny Guy3a7c4a52014-03-03 18:24:03 +00002249 */
Kenny Guy2a764942014-04-02 13:29:20 +01002250 private boolean notificationMatchesCurrentProfiles(NotificationRecord r, int userId) {
Kenny Guya263e4e2014-03-03 18:24:03 +00002251 return notificationMatchesUserId(r, userId)
John Spurlockb408e8e2014-04-23 21:12:45 -04002252 || mUserProfiles.isCurrentProfile(r.getUserId());
Kenny Guy3a7c4a52014-03-03 18:24:03 +00002253 }
2254
2255 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002256 * Cancels all notifications from a given package that have all of the
2257 * {@code mustHaveFlags}.
2258 */
John Spurlocke6a7d932014-03-13 12:29:00 -04002259 boolean cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, int mustHaveFlags,
2260 int mustNotHaveFlags, boolean doit, int userId, int reason,
John Spurlock7340fc82014-04-24 18:50:12 -04002261 ManagedServiceInfo listener) {
John Spurlocke6a7d932014-03-13 12:29:00 -04002262 EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
2263 pkg, userId, mustHaveFlags, mustNotHaveFlags, reason,
2264 listener == null ? null : listener.component.toShortString());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002265
2266 synchronized (mNotificationList) {
2267 final int N = mNotificationList.size();
2268 boolean canceledSomething = false;
2269 for (int i = N-1; i >= 0; --i) {
2270 NotificationRecord r = mNotificationList.get(i);
Daniel Sandler321e9c52012-10-12 10:59:26 -07002271 if (!notificationMatchesUserId(r, userId)) {
Dianne Hackborn41203752012-08-31 14:05:51 -07002272 continue;
2273 }
Amith Yamasani5ec00e92012-11-07 16:58:30 -08002274 // Don't remove notifications to all, if there's no package name specified
Daniel Sandlerfde19b12013-01-17 00:21:05 -05002275 if (r.getUserId() == UserHandle.USER_ALL && pkg == null) {
Amith Yamasani5ec00e92012-11-07 16:58:30 -08002276 continue;
2277 }
Daniel Sandlerfde19b12013-01-17 00:21:05 -05002278 if ((r.getFlags() & mustHaveFlags) != mustHaveFlags) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002279 continue;
2280 }
Daniel Sandlerfde19b12013-01-17 00:21:05 -05002281 if ((r.getFlags() & mustNotHaveFlags) != 0) {
Dianne Hackbornd8a43f62009-08-17 23:33:56 -07002282 continue;
2283 }
Daniel Sandler4f91efd2013-04-25 16:38:41 -04002284 if (pkg != null && !r.sbn.getPackageName().equals(pkg)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002285 continue;
2286 }
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08002287 canceledSomething = true;
2288 if (!doit) {
2289 return true;
2290 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002291 mNotificationList.remove(i);
John Spurlocka4294292014-03-24 18:02:32 -04002292 mNotificationsByKey.remove(r.sbn.getKey());
Christoph Studer546bec82014-03-14 12:17:12 +01002293 cancelNotificationLocked(r, false, reason);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002294 }
2295 if (canceledSomething) {
2296 updateLightsLocked();
2297 }
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08002298 return canceledSomething;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002299 }
2300 }
2301
Adam Lesinski350159c2014-03-27 11:15:11 -07002302 void cancelAllLocked(int callingUid, int callingPid, int userId, int reason,
John Spurlock7340fc82014-04-24 18:50:12 -04002303 ManagedServiceInfo listener, boolean includeCurrentProfiles) {
John Spurlocke6a7d932014-03-13 12:29:00 -04002304 EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
2305 null, userId, 0, 0, reason,
2306 listener == null ? null : listener.component.toShortString());
Christoph Studer546bec82014-03-14 12:17:12 +01002307
Adam Lesinskie8240262014-03-26 16:01:00 -07002308 final int N = mNotificationList.size();
2309 for (int i=N-1; i>=0; i--) {
2310 NotificationRecord r = mNotificationList.get(i);
Kenny Guya263e4e2014-03-03 18:24:03 +00002311 if (includeCurrentProfiles) {
2312 if (!notificationMatchesCurrentProfiles(r, userId)) {
2313 continue;
2314 }
2315 } else {
2316 if (!notificationMatchesUserId(r, userId)) {
2317 continue;
2318 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002319 }
2320
Adam Lesinskie8240262014-03-26 16:01:00 -07002321 if ((r.getFlags() & (Notification.FLAG_ONGOING_EVENT
2322 | Notification.FLAG_NO_CLEAR)) == 0) {
2323 mNotificationList.remove(i);
John Spurlocka4294292014-03-24 18:02:32 -04002324 mNotificationsByKey.remove(r.sbn.getKey());
Christoph Studer546bec82014-03-14 12:17:12 +01002325 cancelNotificationLocked(r, true, reason);
Adam Lesinskie8240262014-03-26 16:01:00 -07002326 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002327 }
Adam Lesinskie8240262014-03-26 16:01:00 -07002328 updateLightsLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002329 }
2330
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002331 // lock on mNotificationList
Adam Lesinski182f73f2013-12-05 16:48:06 -08002332 void updateLightsLocked()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002333 {
The Android Open Source Project10592532009-03-18 17:39:46 -07002334 // handle notification lights
2335 if (mLedNotification == null) {
2336 // get next notification, if any
2337 int n = mLights.size();
2338 if (n > 0) {
2339 mLedNotification = mLights.get(n-1);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002340 }
2341 }
Mike Lockwoodc22404a2009-12-02 11:15:02 -05002342
Mike Lockwood63b5ad92011-08-30 09:55:30 -04002343 // Don't flash while we are in a call or screen is on
2344 if (mLedNotification == null || mInCall || mScreenOn) {
Mike Lockwood3cb67a32009-11-27 14:25:58 -05002345 mNotificationLight.turnOff();
The Android Open Source Project10592532009-03-18 17:39:46 -07002346 } else {
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -04002347 final Notification ledno = mLedNotification.sbn.getNotification();
Daniel Sandlerfde19b12013-01-17 00:21:05 -05002348 int ledARGB = ledno.ledARGB;
2349 int ledOnMS = ledno.ledOnMS;
2350 int ledOffMS = ledno.ledOffMS;
2351 if ((ledno.defaults & Notification.DEFAULT_LIGHTS) != 0) {
Mike Lockwood670f9322010-01-20 12:13:36 -05002352 ledARGB = mDefaultNotificationColor;
2353 ledOnMS = mDefaultNotificationLedOn;
2354 ledOffMS = mDefaultNotificationLedOff;
2355 }
2356 if (mNotificationPulseEnabled) {
2357 // pulse repeatedly
Adam Lesinski182f73f2013-12-05 16:48:06 -08002358 mNotificationLight.setFlashing(ledARGB, Light.LIGHT_FLASH_TIMED,
Mike Lockwood670f9322010-01-20 12:13:36 -05002359 ledOnMS, ledOffMS);
Mike Lockwood670f9322010-01-20 12:13:36 -05002360 }
The Android Open Source Project10592532009-03-18 17:39:46 -07002361 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002362 }
2363
2364 // lock on mNotificationList
Adam Lesinski182f73f2013-12-05 16:48:06 -08002365 int indexOfNotificationLocked(String pkg, String tag, int id, int userId)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002366 {
2367 ArrayList<NotificationRecord> list = mNotificationList;
2368 final int len = list.size();
2369 for (int i=0; i<len; i++) {
2370 NotificationRecord r = list.get(i);
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -04002371 if (!notificationMatchesUserId(r, userId) || r.sbn.getId() != id) {
Dianne Hackborn41203752012-08-31 14:05:51 -07002372 continue;
2373 }
Fred Quintana6ecaff12009-09-25 14:23:13 -07002374 if (tag == null) {
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -04002375 if (r.sbn.getTag() != null) {
Fred Quintana6ecaff12009-09-25 14:23:13 -07002376 continue;
2377 }
2378 } else {
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -04002379 if (!tag.equals(r.sbn.getTag())) {
Fred Quintana6ecaff12009-09-25 14:23:13 -07002380 continue;
2381 }
2382 }
Daniel Sandler4f91efd2013-04-25 16:38:41 -04002383 if (r.sbn.getPackageName().equals(pkg)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002384 return i;
2385 }
2386 }
2387 return -1;
2388 }
2389
Mike Lockwoodc22404a2009-12-02 11:15:02 -05002390 private void updateNotificationPulse() {
2391 synchronized (mNotificationList) {
2392 updateLightsLocked();
2393 }
2394 }
John Spurlocke677d712014-02-13 12:52:19 -05002395
John Spurlock7340fc82014-04-24 18:50:12 -04002396 private static boolean isUidSystem(int uid) {
2397 final int appid = UserHandle.getAppId(uid);
2398 return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0);
2399 }
John Spurlockb408e8e2014-04-23 21:12:45 -04002400
John Spurlock7340fc82014-04-24 18:50:12 -04002401 private static boolean isCallerSystem() {
2402 return isUidSystem(Binder.getCallingUid());
2403 }
2404
2405 private static void checkCallerIsSystem() {
2406 if (isCallerSystem()) {
2407 return;
2408 }
2409 throw new SecurityException("Disallowed call for uid " + Binder.getCallingUid());
2410 }
2411
2412 private static void checkCallerIsSystemOrSameApp(String pkg) {
2413 if (isCallerSystem()) {
2414 return;
2415 }
2416 final int uid = Binder.getCallingUid();
2417 try {
2418 ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo(
2419 pkg, 0, UserHandle.getCallingUserId());
2420 if (!UserHandle.isSameApp(ai.uid, uid)) {
2421 throw new SecurityException("Calling uid " + uid + " gave package"
2422 + pkg + " which is owned by uid " + ai.uid);
2423 }
2424 } catch (RemoteException re) {
2425 throw new SecurityException("Unknown package " + pkg + "\n" + re);
2426 }
2427 }
2428
2429 public class NotificationListeners extends ManagedServices {
2430
2431 public NotificationListeners() {
2432 super(getContext(), mHandler, mNotificationList, mUserProfiles);
2433 }
2434
2435 @Override
2436 protected Config getConfig() {
2437 Config c = new Config();
2438 c.caption = "notification listener";
2439 c.serviceInterface = NotificationListenerService.SERVICE_INTERFACE;
2440 c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_LISTENERS;
2441 c.bindPermission = android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE;
2442 c.settingsAction = Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS;
2443 c.clientLabel = R.string.notification_listener_binding_label;
2444 return c;
2445 }
2446
2447 @Override
2448 protected IInterface asInterface(IBinder binder) {
2449 return INotificationListener.Stub.asInterface(binder);
2450 }
2451
2452 @Override
John Spurlock3b98b3f2014-05-01 09:08:48 -04002453 public void onServiceAdded(ManagedServiceInfo info) {
2454 final INotificationListener listener = (INotificationListener) info.service;
Chris Wrenf9536642014-04-17 10:01:54 -04002455 final String[] keys = getActiveNotificationKeys(listener);
John Spurlock7340fc82014-04-24 18:50:12 -04002456 try {
Chris Wrenf9536642014-04-17 10:01:54 -04002457 listener.onListenerConnected(new NotificationOrderUpdate(keys));
John Spurlock7340fc82014-04-24 18:50:12 -04002458 } catch (RemoteException e) {
2459 // we tried
2460 }
2461 }
2462
2463 /**
2464 * asynchronously notify all listeners about a new notification
2465 */
2466 public void notifyPostedLocked(StatusBarNotification sbn) {
2467 // make a copy in case changes are made to the underlying Notification object
2468 final StatusBarNotification sbnClone = sbn.clone();
2469 for (final ManagedServiceInfo info : mServices) {
Chris Wrenf9536642014-04-17 10:01:54 -04002470 if (info.isEnabledForCurrentProfiles()) {
2471 final INotificationListener listener = (INotificationListener) info.service;
2472 final String[] keys = getActiveNotificationKeys(listener);
2473 if (keys.length > 0) {
2474 mHandler.post(new Runnable() {
2475 @Override
2476 public void run() {
2477 notifyPostedIfUserMatch(info, sbnClone, keys);
2478 }
2479 });
John Spurlockb408e8e2014-04-23 21:12:45 -04002480 }
Chris Wrenf9536642014-04-17 10:01:54 -04002481 }
Kenny Guy3a7c4a52014-03-03 18:24:03 +00002482 }
2483 }
Kenny Guy3a7c4a52014-03-03 18:24:03 +00002484
John Spurlock7340fc82014-04-24 18:50:12 -04002485 /**
2486 * asynchronously notify all listeners about a removed notification
2487 */
2488 public void notifyRemovedLocked(StatusBarNotification sbn) {
2489 // make a copy in case changes are made to the underlying Notification object
2490 // NOTE: this copy is lightweight: it doesn't include heavyweight parts of the
2491 // notification
2492 final StatusBarNotification sbnLight = sbn.cloneLight();
Chris Wrenf9536642014-04-17 10:01:54 -04002493 for (final ManagedServiceInfo info : mServices) {
2494 if (info.isEnabledForCurrentProfiles()) {
2495 final INotificationListener listener = (INotificationListener) info.service;
2496 final String[] keys = getActiveNotificationKeys(listener);
2497 mHandler.post(new Runnable() {
2498 @Override
2499 public void run() {
2500 notifyRemovedIfUserMatch(info, sbnLight, keys);
2501 }
2502 });
2503 }
2504 }
2505 }
2506
2507 /**
2508 * asynchronously notify all listeners about a reordering of notifications
2509 * @param sbns an array of {@link StatusBarNotification}s to consider. This code
2510 * must not rely on mutable members of these objects, such as the
2511 * {@link Notification}.
2512 */
2513 public void notifyOrderUpdateLocked(final ArrayList<StatusBarNotification> sbns) {
2514 for (final ManagedServiceInfo serviceInfo : mServices) {
John Spurlock7340fc82014-04-24 18:50:12 -04002515 mHandler.post(new Runnable() {
2516 @Override
2517 public void run() {
Chris Wrenf9536642014-04-17 10:01:54 -04002518 notifyOrderUpdateIfUserMatch(serviceInfo, sbns);
John Spurlock7340fc82014-04-24 18:50:12 -04002519 }
2520 });
Kenny Guya263e4e2014-03-03 18:24:03 +00002521 }
Kenny Guya263e4e2014-03-03 18:24:03 +00002522 }
Kenny Guya263e4e2014-03-03 18:24:03 +00002523
Chris Wrenf9536642014-04-17 10:01:54 -04002524 private void notifyPostedIfUserMatch(final ManagedServiceInfo info,
2525 final StatusBarNotification sbn, String[] keys) {
John Spurlock7340fc82014-04-24 18:50:12 -04002526 if (!info.enabledAndUserMatches(sbn.getUserId())) {
2527 return;
2528 }
2529 final INotificationListener listener = (INotificationListener)info.service;
2530 try {
Chris Wrenf9536642014-04-17 10:01:54 -04002531 listener.onNotificationPosted(sbn, new NotificationOrderUpdate(keys));
John Spurlock7340fc82014-04-24 18:50:12 -04002532 } catch (RemoteException ex) {
2533 Log.e(TAG, "unable to notify listener (posted): " + listener, ex);
2534 }
2535 }
2536
Chris Wrenf9536642014-04-17 10:01:54 -04002537 private void notifyRemovedIfUserMatch(ManagedServiceInfo info, StatusBarNotification sbn,
2538 String[] keys) {
John Spurlock7340fc82014-04-24 18:50:12 -04002539 if (!info.enabledAndUserMatches(sbn.getUserId())) {
2540 return;
2541 }
2542 final INotificationListener listener = (INotificationListener)info.service;
2543 try {
Chris Wrenf9536642014-04-17 10:01:54 -04002544 listener.onNotificationRemoved(sbn, new NotificationOrderUpdate(keys));
John Spurlock7340fc82014-04-24 18:50:12 -04002545 } catch (RemoteException ex) {
2546 Log.e(TAG, "unable to notify listener (removed): " + listener, ex);
John Spurlockb408e8e2014-04-23 21:12:45 -04002547 }
Kenny Guya263e4e2014-03-03 18:24:03 +00002548 }
Chris Wrenf9536642014-04-17 10:01:54 -04002549
2550 /**
2551 * @param sbns an array of {@link StatusBarNotification}s to consider. This code
2552 * must not rely on mutable members of these objects, such as the
2553 * {@link Notification}.
2554 */
2555 public void notifyOrderUpdateIfUserMatch(ManagedServiceInfo info,
2556 ArrayList<StatusBarNotification> sbns) {
2557 ArrayList<String> keys = new ArrayList<String>(sbns.size());
2558 for (StatusBarNotification sbn: sbns) {
2559 if (info.enabledAndUserMatches(sbn.getUserId())) {
2560 keys.add(sbn.getKey());
2561 }
2562 }
2563 final INotificationListener listener = (INotificationListener)info.service;
2564 try {
2565 listener.onNotificationOrderUpdate(
2566 new NotificationOrderUpdate(keys.toArray(new String[keys.size()])));
2567 } catch (RemoteException ex) {
2568 Log.e(TAG, "unable to notify listener (ranking update): " + listener, ex);
2569 }
2570 }
Kenny Guya263e4e2014-03-03 18:24:03 +00002571 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002572}