blob: f7cb346fa5f4baebea5b9ed8624bf2a74caca720 [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;
Christoph Studercee44ba2014-05-20 18:36:43 +020043import android.content.pm.ParceledListSlice;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080044import android.content.res.Resources;
Dianne Hackborn1dac2772009-06-26 18:16:48 -070045import android.database.ContentObserver;
John Spurlock7b414672014-07-18 13:02:39 -040046import android.media.AudioAttributes;
svetoslavganov75986cf2009-05-14 22:28:01 -070047import android.media.AudioManager;
Jeff Sharkey098d5802012-04-26 17:30:34 -070048import android.media.IRingtonePlayer;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080049import android.net.Uri;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080050import android.os.Binder;
John Spurlock056c5192014-04-20 21:52:01 -040051import android.os.Environment;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080052import android.os.Handler;
Chris Wrenf9536642014-04-17 10:01:54 -040053import android.os.HandlerThread;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080054import android.os.IBinder;
John Spurlock7340fc82014-04-24 18:50:12 -040055import android.os.IInterface;
Chris Wrenf9536642014-04-17 10:01:54 -040056import android.os.Looper;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080057import android.os.Message;
Dianne Hackbornd8a43f62009-08-17 23:33:56 -070058import android.os.Process;
svetoslavganov75986cf2009-05-14 22:28:01 -070059import android.os.RemoteException;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -070060import android.os.UserHandle;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080061import android.os.Vibrator;
62import android.provider.Settings;
Chris Wren333a61c2014-05-28 16:40:57 -040063import android.service.notification.Condition;
John Spurlocke77bb362014-04-26 10:24:59 -040064import android.service.notification.IConditionListener;
John Spurlock7340fc82014-04-24 18:50:12 -040065import android.service.notification.IConditionProvider;
Chris Wren333a61c2014-05-28 16:40:57 -040066import android.service.notification.INotificationListener;
John Spurlock7340fc82014-04-24 18:50:12 -040067import android.service.notification.NotificationListenerService;
Christoph Studer05ad4822014-05-16 14:16:03 +020068import android.service.notification.NotificationRankingUpdate;
Daniel Sandler5feceeb2013-03-22 18:29:23 -070069import android.service.notification.StatusBarNotification;
John Spurlock056c5192014-04-20 21:52:01 -040070import android.service.notification.ZenModeConfig;
Daniel Sandlere96ffb12010-03-11 13:38:06 -050071import android.telephony.TelephonyManager;
svetoslavganov75986cf2009-05-14 22:28:01 -070072import android.text.TextUtils;
John Spurlocka4294292014-03-24 18:02:32 -040073import android.util.ArrayMap;
Dianne Hackborn39606a02012-07-31 17:54:35 -070074import android.util.AtomicFile;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080075import android.util.Log;
Andy Stadler110988c2010-12-03 14:29:16 -080076import android.util.Slog;
Daniel Sandler0da673f2012-04-11 12:33:16 -040077import android.util.Xml;
svetoslavganov75986cf2009-05-14 22:28:01 -070078import android.view.accessibility.AccessibilityEvent;
79import android.view.accessibility.AccessibilityManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080080import android.widget.Toast;
81
Scott Greenwald9a05b312013-06-28 00:37:54 -040082import com.android.internal.R;
John Spurlock056c5192014-04-20 21:52:01 -040083import com.android.internal.util.FastXmlSerializer;
Adam Lesinski182f73f2013-12-05 16:48:06 -080084import com.android.server.EventLogTags;
Adam Lesinski182f73f2013-12-05 16:48:06 -080085import com.android.server.SystemService;
86import com.android.server.lights.Light;
87import com.android.server.lights.LightsManager;
John Spurlock7340fc82014-04-24 18:50:12 -040088import com.android.server.notification.ManagedServices.ManagedServiceInfo;
89import com.android.server.notification.ManagedServices.UserProfiles;
John Spurlockb408e8e2014-04-23 21:12:45 -040090import 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 Sandlerfde19b12013-01-17 00:21:05 -0500105import java.util.ArrayDeque;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800106import java.util.ArrayList;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400107import java.util.HashSet;
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500108import java.util.Iterator;
109import java.util.NoSuchElementException;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400110
Daniel Sandlerd0a2f862010-08-03 15:29:31 -0400111/** {@hide} */
Adam Lesinski182f73f2013-12-05 16:48:06 -0800112public class NotificationManagerService extends SystemService {
113 static final String TAG = "NotificationService";
114 static final boolean DBG = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800115
Adam Lesinski182f73f2013-12-05 16:48:06 -0800116 static final int MAX_PACKAGE_NOTIFICATIONS = 50;
Joe Onoratobd73d012010-06-04 11:44:54 -0700117
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800118 // message codes
Adam Lesinski182f73f2013-12-05 16:48:06 -0800119 static final int MESSAGE_TIMEOUT = 2;
John Spurlock056c5192014-04-20 21:52:01 -0400120 static final int MESSAGE_SAVE_POLICY_FILE = 3;
Chris Wrenf9536642014-04-17 10:01:54 -0400121 static final int MESSAGE_RECONSIDER_RANKING = 4;
Chris Wren54bbef42014-07-09 18:37:56 -0400122 static final int MESSAGE_RANKING_CONFIG_CHANGE = 5;
123 static final int MESSAGE_SEND_RANKING_UPDATE = 6;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800124
Adam Lesinski182f73f2013-12-05 16:48:06 -0800125 static final int LONG_DELAY = 3500; // 3.5 seconds
126 static final int SHORT_DELAY = 2000; // 2 seconds
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800127
Adam Lesinski182f73f2013-12-05 16:48:06 -0800128 static final long[] DEFAULT_VIBRATE_PATTERN = {0, 250, 250, 250};
129 static final int VIBRATE_PATTERN_MAXLEN = 8 * 2 + 1; // up to eight bumps
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800130
Adam Lesinski182f73f2013-12-05 16:48:06 -0800131 static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_NOTIFICATION;
132 static final boolean SCORE_ONGOING_HIGHER = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800133
Adam Lesinski182f73f2013-12-05 16:48:06 -0800134 static final int JUNK_SCORE = -1000;
135 static final int NOTIFICATION_PRIORITY_MULTIPLIER = 10;
136 static final int SCORE_DISPLAY_THRESHOLD = Notification.PRIORITY_MIN * NOTIFICATION_PRIORITY_MULTIPLIER;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400137
Daniel Sandler526fa0e2012-12-04 14:51:50 -0500138 // Notifications with scores below this will not interrupt the user, either via LED or
139 // sound or vibration
Adam Lesinski182f73f2013-12-05 16:48:06 -0800140 static final int SCORE_INTERRUPTION_THRESHOLD =
Daniel Sandler526fa0e2012-12-04 14:51:50 -0500141 Notification.PRIORITY_LOW * NOTIFICATION_PRIORITY_MULTIPLIER;
142
Adam Lesinski182f73f2013-12-05 16:48:06 -0800143 static final boolean ENABLE_BLOCKED_NOTIFICATIONS = true;
144 static final boolean ENABLE_BLOCKED_TOASTS = true;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400145
Adam Lesinski182f73f2013-12-05 16:48:06 -0800146 private IActivityManager mAm;
147 AudioManager mAudioManager;
148 StatusBarManagerInternal mStatusBar;
149 Vibrator mVibrator;
150
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800151 final IBinder mForegroundToken = new Binder();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800152 private WorkerHandler mHandler;
Chris Wrenf9536642014-04-17 10:01:54 -0400153 private final HandlerThread mRankingThread = new HandlerThread("ranker",
154 Process.THREAD_PRIORITY_BACKGROUND);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800155
Adam Lesinski182f73f2013-12-05 16:48:06 -0800156 private Light mNotificationLight;
157 Light mAttentionLight;
Mike Lockwood670f9322010-01-20 12:13:36 -0500158 private int mDefaultNotificationColor;
159 private int mDefaultNotificationLedOn;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800160
Mike Lockwood670f9322010-01-20 12:13:36 -0500161 private int mDefaultNotificationLedOff;
Daniel Sandleredbb3802012-11-13 20:49:47 -0800162 private long[] mDefaultVibrationPattern;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800163
Daniel Sandleredbb3802012-11-13 20:49:47 -0800164 private long[] mFallbackVibrationPattern;
Chris Wren5116a822014-06-04 15:59:50 -0400165 private boolean mUseAttentionLight;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800166 boolean mSystemReady;
Daniel Sandleredbb3802012-11-13 20:49:47 -0800167
John Spurlocke677d712014-02-13 12:52:19 -0500168 private boolean mDisableNotificationAlerts;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800169 NotificationRecord mSoundNotification;
170 NotificationRecord mVibrateNotification;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800171
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500172 // for enabling and disabling notification pulse behavior
Mike Lockwood63b5ad92011-08-30 09:55:30 -0400173 private boolean mScreenOn = true;
Daniel Sandlere96ffb12010-03-11 13:38:06 -0500174 private boolean mInCall = false;
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500175 private boolean mNotificationPulseEnabled;
176
Daniel Sandler09a247e2013-02-14 10:24:17 -0500177 // used as a mutex for access to all active notifications & listeners
Adam Lesinski182f73f2013-12-05 16:48:06 -0800178 final ArrayList<NotificationRecord> mNotificationList =
Fred Quintana6ecaff12009-09-25 14:23:13 -0700179 new ArrayList<NotificationRecord>();
John Spurlocka4294292014-03-24 18:02:32 -0400180 final ArrayMap<String, NotificationRecord> mNotificationsByKey =
181 new ArrayMap<String, NotificationRecord>();
Adam Lesinski182f73f2013-12-05 16:48:06 -0800182 final ArrayList<ToastRecord> mToastQueue = new ArrayList<ToastRecord>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800183
Chris Wrena3446562014-06-03 18:11:47 -0400184 ArrayList<String> mLights = new ArrayList<String>();
Adam Lesinski182f73f2013-12-05 16:48:06 -0800185 NotificationRecord mLedNotification;
svetoslavganov75986cf2009-05-14 22:28:01 -0700186
Adam Lesinski182f73f2013-12-05 16:48:06 -0800187 private AppOpsManager mAppOps;
Daniel Sandler4a900ac2013-01-30 14:04:10 -0500188
Griff Hazen9f637d12014-06-10 11:13:51 -0700189 private Archive mArchive;
190
Daniel Sandler0da673f2012-04-11 12:33:16 -0400191 // Notification control database. For now just contains disabled packages.
192 private AtomicFile mPolicyFile;
193 private HashSet<String> mBlockedPackages = new HashSet<String>();
194
195 private static final int DB_VERSION = 1;
196
197 private static final String TAG_BODY = "notification-policy";
198 private static final String ATTR_VERSION = "version";
199
200 private static final String TAG_BLOCKED_PKGS = "blocked-packages";
201 private static final String TAG_PACKAGE = "package";
202 private static final String ATTR_NAME = "name";
203
Chris Wren54bbef42014-07-09 18:37:56 -0400204 private RankingHelper mRankingHelper;
Scott Greenwald9a05b312013-06-28 00:37:54 -0400205
John Spurlockb408e8e2014-04-23 21:12:45 -0400206 private final UserProfiles mUserProfiles = new UserProfiles();
John Spurlock7340fc82014-04-24 18:50:12 -0400207 private NotificationListeners mListeners;
208 private ConditionProviders mConditionProviders;
Christoph Studer1c3f81f2014-04-16 15:05:56 +0200209 private NotificationUsageStats mUsageStats;
Christoph Studer546bec82014-03-14 12:17:12 +0100210
John Spurlocke6a7d932014-03-13 12:29:00 -0400211 private static final int MY_UID = Process.myUid();
212 private static final int MY_PID = Process.myPid();
213 private static final int REASON_DELEGATE_CLICK = 1;
214 private static final int REASON_DELEGATE_CANCEL = 2;
215 private static final int REASON_DELEGATE_CANCEL_ALL = 3;
216 private static final int REASON_DELEGATE_ERROR = 4;
217 private static final int REASON_PACKAGE_CHANGED = 5;
218 private static final int REASON_USER_STOPPED = 6;
219 private static final int REASON_PACKAGE_BANNED = 7;
220 private static final int REASON_NOMAN_CANCEL = 8;
221 private static final int REASON_NOMAN_CANCEL_ALL = 9;
222 private static final int REASON_LISTENER_CANCEL = 10;
223 private static final int REASON_LISTENER_CANCEL_ALL = 11;
Christoph Studere4ef156b2014-07-04 18:41:57 +0200224 private static final int REASON_GROUP_SUMMARY_CANCELED = 12;
John Spurlocke6a7d932014-03-13 12:29:00 -0400225
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500226 private static class Archive {
Griff Hazen9f637d12014-06-10 11:13:51 -0700227 final int mBufferSize;
228 final ArrayDeque<StatusBarNotification> mBuffer;
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500229
Griff Hazen9f637d12014-06-10 11:13:51 -0700230 public Archive(int size) {
231 mBufferSize = size;
232 mBuffer = new ArrayDeque<StatusBarNotification>(mBufferSize);
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500233 }
Jeff Sharkey0c1baf92013-04-03 13:08:52 -0700234
Daniel Sandler5e62e3a2013-04-15 20:57:02 -0400235 public String toString() {
236 final StringBuilder sb = new StringBuilder();
237 final int N = mBuffer.size();
238 sb.append("Archive (");
239 sb.append(N);
240 sb.append(" notification");
241 sb.append((N==1)?")":"s)");
242 return sb.toString();
243 }
244
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500245 public void record(StatusBarNotification nr) {
Griff Hazen9f637d12014-06-10 11:13:51 -0700246 if (mBuffer.size() == mBufferSize) {
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500247 mBuffer.removeFirst();
248 }
Daniel Sandler26b81d52013-05-20 20:56:43 -0400249
250 // We don't want to store the heavy bits of the notification in the archive,
251 // but other clients in the system process might be using the object, so we
252 // store a (lightened) copy.
253 mBuffer.addLast(nr.cloneLight());
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500254 }
255
256 public void clear() {
257 mBuffer.clear();
258 }
259
260 public Iterator<StatusBarNotification> descendingIterator() {
261 return mBuffer.descendingIterator();
262 }
263 public Iterator<StatusBarNotification> ascendingIterator() {
264 return mBuffer.iterator();
265 }
266 public Iterator<StatusBarNotification> filter(
267 final Iterator<StatusBarNotification> iter, final String pkg, final int userId) {
268 return new Iterator<StatusBarNotification>() {
269 StatusBarNotification mNext = findNext();
270
271 private StatusBarNotification findNext() {
272 while (iter.hasNext()) {
273 StatusBarNotification nr = iter.next();
Daniel Sandler4f91efd2013-04-25 16:38:41 -0400274 if ((pkg == null || nr.getPackageName() == pkg)
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500275 && (userId == UserHandle.USER_ALL || nr.getUserId() == userId)) {
276 return nr;
277 }
278 }
279 return null;
280 }
281
282 @Override
283 public boolean hasNext() {
284 return mNext == null;
285 }
286
287 @Override
288 public StatusBarNotification next() {
289 StatusBarNotification next = mNext;
290 if (next == null) {
291 throw new NoSuchElementException();
292 }
293 mNext = findNext();
294 return next;
295 }
296
297 @Override
298 public void remove() {
299 iter.remove();
300 }
301 };
302 }
Daniel Sandler78d0d252013-02-12 08:14:52 -0500303
304 public StatusBarNotification[] getArray(int count) {
Griff Hazen9f637d12014-06-10 11:13:51 -0700305 if (count == 0) count = mBufferSize;
Daniel Sandler78d0d252013-02-12 08:14:52 -0500306 final StatusBarNotification[] a
307 = new StatusBarNotification[Math.min(count, mBuffer.size())];
308 Iterator<StatusBarNotification> iter = descendingIterator();
309 int i=0;
310 while (iter.hasNext() && i < count) {
311 a[i++] = iter.next();
312 }
313 return a;
314 }
315
316 public StatusBarNotification[] getArray(int count, String pkg, int userId) {
Griff Hazen9f637d12014-06-10 11:13:51 -0700317 if (count == 0) count = mBufferSize;
Daniel Sandler78d0d252013-02-12 08:14:52 -0500318 final StatusBarNotification[] a
319 = new StatusBarNotification[Math.min(count, mBuffer.size())];
320 Iterator<StatusBarNotification> iter = filter(descendingIterator(), pkg, userId);
321 int i=0;
322 while (iter.hasNext() && i < count) {
323 a[i++] = iter.next();
324 }
325 return a;
326 }
327
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500328 }
329
John Spurlock056c5192014-04-20 21:52:01 -0400330 private void loadPolicyFile() {
331 synchronized(mPolicyFile) {
332 mBlockedPackages.clear();
Daniel Sandler0da673f2012-04-11 12:33:16 -0400333
John Spurlock056c5192014-04-20 21:52:01 -0400334 FileInputStream infile = null;
335 try {
336 infile = mPolicyFile.openRead();
337 final XmlPullParser parser = Xml.newPullParser();
338 parser.setInput(infile, null);
Daniel Sandler0da673f2012-04-11 12:33:16 -0400339
John Spurlock056c5192014-04-20 21:52:01 -0400340 int type;
341 String tag;
342 int version = DB_VERSION;
343 while ((type = parser.next()) != END_DOCUMENT) {
344 tag = parser.getName();
345 if (type == START_TAG) {
346 if (TAG_BODY.equals(tag)) {
347 version = Integer.parseInt(
348 parser.getAttributeValue(null, ATTR_VERSION));
349 } else if (TAG_BLOCKED_PKGS.equals(tag)) {
350 while ((type = parser.next()) != END_DOCUMENT) {
351 tag = parser.getName();
352 if (TAG_PACKAGE.equals(tag)) {
353 mBlockedPackages.add(
354 parser.getAttributeValue(null, ATTR_NAME));
355 } else if (TAG_BLOCKED_PKGS.equals(tag) && type == END_TAG) {
356 break;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400357 }
358 }
359 }
360 }
John Spurlock056c5192014-04-20 21:52:01 -0400361 mZenModeHelper.readXml(parser);
Chris Wren54bbef42014-07-09 18:37:56 -0400362 mRankingHelper.readXml(parser);
Daniel Sandler0da673f2012-04-11 12:33:16 -0400363 }
John Spurlock056c5192014-04-20 21:52:01 -0400364 } catch (FileNotFoundException e) {
365 // No data yet
366 } catch (IOException e) {
367 Log.wtf(TAG, "Unable to read notification policy", e);
368 } catch (NumberFormatException e) {
369 Log.wtf(TAG, "Unable to parse notification policy", e);
370 } catch (XmlPullParserException e) {
371 Log.wtf(TAG, "Unable to parse notification policy", e);
372 } finally {
373 IoUtils.closeQuietly(infile);
374 }
375 }
376 }
377
378 public void savePolicyFile() {
379 mHandler.removeMessages(MESSAGE_SAVE_POLICY_FILE);
380 mHandler.sendEmptyMessage(MESSAGE_SAVE_POLICY_FILE);
381 }
382
383 private void handleSavePolicyFile() {
384 Slog.d(TAG, "handleSavePolicyFile");
385 synchronized (mPolicyFile) {
386 final FileOutputStream stream;
387 try {
388 stream = mPolicyFile.startWrite();
389 } catch (IOException e) {
390 Slog.w(TAG, "Failed to save policy file", e);
391 return;
392 }
393
394 try {
395 final XmlSerializer out = new FastXmlSerializer();
396 out.setOutput(stream, "utf-8");
397 out.startDocument(null, true);
398 out.startTag(null, TAG_BODY);
399 out.attribute(null, ATTR_VERSION, Integer.toString(DB_VERSION));
400 mZenModeHelper.writeXml(out);
Chris Wren54bbef42014-07-09 18:37:56 -0400401 mRankingHelper.writeXml(out);
John Spurlock056c5192014-04-20 21:52:01 -0400402 out.endTag(null, TAG_BODY);
403 out.endDocument();
404 mPolicyFile.finishWrite(stream);
405 } catch (IOException e) {
406 Slog.w(TAG, "Failed to save policy file, restoring backup", e);
407 mPolicyFile.failWrite(stream);
Daniel Sandler0da673f2012-04-11 12:33:16 -0400408 }
409 }
410 }
411
Daniel Sandler4a900ac2013-01-30 14:04:10 -0500412 /** Use this when you actually want to post a notification or toast.
413 *
414 * Unchecked. Not exposed via Binder, but can be called in the course of enqueue*().
415 */
416 private boolean noteNotificationOp(String pkg, int uid) {
417 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
418 != AppOpsManager.MODE_ALLOWED) {
419 Slog.v(TAG, "notifications are disabled by AppOps for " + pkg);
420 return false;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400421 }
Daniel Sandler4a900ac2013-01-30 14:04:10 -0500422 return true;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400423 }
424
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800425 private static final class ToastRecord
426 {
427 final int pid;
428 final String pkg;
429 final ITransientNotification callback;
430 int duration;
431
432 ToastRecord(int pid, String pkg, ITransientNotification callback, int duration)
433 {
434 this.pid = pid;
435 this.pkg = pkg;
436 this.callback = callback;
437 this.duration = duration;
438 }
439
440 void update(int duration) {
441 this.duration = duration;
442 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800443
John Spurlock25e2d242014-06-27 13:58:23 -0400444 void dump(PrintWriter pw, String prefix, DumpFilter filter) {
445 if (filter != null && !filter.matches(pkg)) return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800446 pw.println(prefix + this);
447 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800448
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800449 @Override
450 public final String toString()
451 {
452 return "ToastRecord{"
453 + Integer.toHexString(System.identityHashCode(this))
454 + " pkg=" + pkg
455 + " callback=" + callback
456 + " duration=" + duration;
457 }
458 }
459
Adam Lesinski182f73f2013-12-05 16:48:06 -0800460 private final NotificationDelegate mNotificationDelegate = new NotificationDelegate() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800461
Adam Lesinski182f73f2013-12-05 16:48:06 -0800462 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800463 public void onSetDisabled(int status) {
464 synchronized (mNotificationList) {
John Spurlocke677d712014-02-13 12:52:19 -0500465 mDisableNotificationAlerts = (status & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0;
466 if (mDisableNotificationAlerts) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800467 // cancel whatever's going on
468 long identity = Binder.clearCallingIdentity();
469 try {
Adam Lesinski182f73f2013-12-05 16:48:06 -0800470 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
Jeff Sharkey098d5802012-04-26 17:30:34 -0700471 if (player != null) {
472 player.stopAsync();
473 }
474 } catch (RemoteException e) {
475 } finally {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800476 Binder.restoreCallingIdentity(identity);
477 }
478
479 identity = Binder.clearCallingIdentity();
480 try {
481 mVibrator.cancel();
Jeff Sharkey098d5802012-04-26 17:30:34 -0700482 } finally {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800483 Binder.restoreCallingIdentity(identity);
484 }
485 }
486 }
487 }
488
Adam Lesinski182f73f2013-12-05 16:48:06 -0800489 @Override
John Spurlocke6a7d932014-03-13 12:29:00 -0400490 public void onClearAll(int callingUid, int callingPid, int userId) {
Adam Lesinskie8240262014-03-26 16:01:00 -0700491 synchronized (mNotificationList) {
Kenny Guya263e4e2014-03-03 18:24:03 +0000492 cancelAllLocked(callingUid, callingPid, userId, REASON_DELEGATE_CANCEL_ALL, null,
493 /*includeCurrentProfiles*/ true);
Adam Lesinskie8240262014-03-26 16:01:00 -0700494 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800495 }
496
Adam Lesinski182f73f2013-12-05 16:48:06 -0800497 @Override
Christoph Studer03b87a22014-04-30 17:33:27 +0200498 public void onNotificationClick(int callingUid, int callingPid, String key) {
499 synchronized (mNotificationList) {
500 EventLogTags.writeNotificationClicked(key);
501 NotificationRecord r = mNotificationsByKey.get(key);
502 if (r == null) {
503 Log.w(TAG, "No notification with key: " + key);
504 return;
505 }
506 StatusBarNotification sbn = r.sbn;
507 cancelNotification(callingUid, callingPid, sbn.getPackageName(), sbn.getTag(),
508 sbn.getId(), Notification.FLAG_AUTO_CANCEL,
509 Notification.FLAG_FOREGROUND_SERVICE, false, r.getUserId(),
510 REASON_DELEGATE_CLICK, null);
511 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800512 }
513
Adam Lesinski182f73f2013-12-05 16:48:06 -0800514 @Override
John Spurlocke6a7d932014-03-13 12:29:00 -0400515 public void onNotificationClear(int callingUid, int callingPid,
516 String pkg, String tag, int id, int userId) {
517 cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
Kenny Guy3a7c4a52014-03-03 18:24:03 +0000518 Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
John Spurlocke6a7d932014-03-13 12:29:00 -0400519 true, userId, REASON_DELEGATE_CANCEL, null);
Daniel Sandler0f0b11c2010-08-04 15:54:58 -0400520 }
521
Adam Lesinski182f73f2013-12-05 16:48:06 -0800522 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800523 public void onPanelRevealed() {
Christoph Studer760ea552014-03-21 13:10:21 +0100524 EventLogTags.writeNotificationPanelRevealed();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800525 synchronized (mNotificationList) {
526 // sound
527 mSoundNotification = null;
Jeff Sharkey098d5802012-04-26 17:30:34 -0700528
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800529 long identity = Binder.clearCallingIdentity();
530 try {
Adam Lesinski182f73f2013-12-05 16:48:06 -0800531 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
Jeff Sharkey098d5802012-04-26 17:30:34 -0700532 if (player != null) {
533 player.stopAsync();
534 }
535 } catch (RemoteException e) {
536 } finally {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800537 Binder.restoreCallingIdentity(identity);
538 }
539
540 // vibrate
541 mVibrateNotification = null;
542 identity = Binder.clearCallingIdentity();
543 try {
544 mVibrator.cancel();
Jeff Sharkey098d5802012-04-26 17:30:34 -0700545 } finally {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800546 Binder.restoreCallingIdentity(identity);
547 }
548
549 // light
550 mLights.clear();
551 mLedNotification = null;
552 updateLightsLocked();
553 }
554 }
Joe Onorato005847b2010-06-04 16:08:02 -0400555
Adam Lesinski182f73f2013-12-05 16:48:06 -0800556 @Override
Christoph Studer760ea552014-03-21 13:10:21 +0100557 public void onPanelHidden() {
558 EventLogTags.writeNotificationPanelHidden();
559 }
560
561 @Override
John Spurlocke6a7d932014-03-13 12:29:00 -0400562 public void onNotificationError(int callingUid, int callingPid, String pkg, String tag, int id,
Kenny Guy3a7c4a52014-03-03 18:24:03 +0000563 int uid, int initialPid, String message, int userId) {
Daniel Sandlerd0a2f862010-08-03 15:29:31 -0400564 Slog.d(TAG, "onNotification error pkg=" + pkg + " tag=" + tag + " id=" + id
565 + "; will crashApplication(uid=" + uid + ", pid=" + initialPid + ")");
John Spurlocke6a7d932014-03-13 12:29:00 -0400566 cancelNotification(callingUid, callingPid, pkg, tag, id, 0, 0, false, userId,
567 REASON_DELEGATE_ERROR, null);
Dianne Hackborn9d39d0c2010-06-24 15:57:42 -0700568 long ident = Binder.clearCallingIdentity();
569 try {
570 ActivityManagerNative.getDefault().crashApplication(uid, initialPid, pkg,
571 "Bad notification posted from package " + pkg
572 + ": " + message);
573 } catch (RemoteException e) {
574 }
575 Binder.restoreCallingIdentity(ident);
Joe Onorato005847b2010-06-04 16:08:02 -0400576 }
John Spurlocke677d712014-02-13 12:52:19 -0500577
578 @Override
579 public boolean allowDisable(int what, IBinder token, String pkg) {
John Spurlock056c5192014-04-20 21:52:01 -0400580 return mZenModeHelper.allowDisable(what, token, pkg);
John Spurlocke677d712014-02-13 12:52:19 -0500581 }
Christoph Studer92b389d2014-04-01 18:44:40 +0200582
583 @Override
584 public void onNotificationVisibilityChanged(
585 String[] newlyVisibleKeys, String[] noLongerVisibleKeys) {
586 // Using ';' as separator since eventlogs uses ',' to separate
587 // args.
588 EventLogTags.writeNotificationVisibilityChanged(
589 TextUtils.join(";", newlyVisibleKeys),
590 TextUtils.join(";", noLongerVisibleKeys));
Christoph Studerffeb0c32014-05-07 22:23:56 +0200591 synchronized (mNotificationList) {
592 for (String key : newlyVisibleKeys) {
593 NotificationRecord r = mNotificationsByKey.get(key);
594 if (r == null) continue;
595 r.stats.onVisibilityChanged(true);
596 }
597 // Note that we might receive this event after notifications
598 // have already left the system, e.g. after dismissing from the
599 // shade. Hence not finding notifications in
600 // mNotificationsByKey is not an exceptional condition.
601 for (String key : noLongerVisibleKeys) {
602 NotificationRecord r = mNotificationsByKey.get(key);
603 if (r == null) continue;
604 r.stats.onVisibilityChanged(false);
605 }
606 }
Christoph Studer92b389d2014-04-01 18:44:40 +0200607 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800608 };
609
610 private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
611 @Override
612 public void onReceive(Context context, Intent intent) {
613 String action = intent.getAction();
614
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800615 boolean queryRestart = false;
Chris Wrenae9bb572013-05-15 14:50:28 -0400616 boolean queryRemove = false;
Daniel Sandler26ece572012-06-01 15:38:46 -0400617 boolean packageChanged = false;
John Spurlock79f78922013-05-16 09:10:05 -0400618 boolean cancelNotifications = true;
Chris Wrenf9536642014-04-17 10:01:54 -0400619
Chris Wren3da73022013-05-10 14:41:21 -0400620 if (action.equals(Intent.ACTION_PACKAGE_ADDED)
Chris Wrenae9bb572013-05-15 14:50:28 -0400621 || (queryRemove=action.equals(Intent.ACTION_PACKAGE_REMOVED))
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800622 || action.equals(Intent.ACTION_PACKAGE_RESTARTED)
Daniel Sandler26ece572012-06-01 15:38:46 -0400623 || (packageChanged=action.equals(Intent.ACTION_PACKAGE_CHANGED))
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800624 || (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART))
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800625 || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800626 String pkgList[] = null;
Chris Wrenae9bb572013-05-15 14:50:28 -0400627 boolean queryReplace = queryRemove &&
628 intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
John Spurlocke77bb362014-04-26 10:24:59 -0400629 if (DBG) Slog.i(TAG, "action=" + action + " queryReplace=" + queryReplace);
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800630 if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800631 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800632 } else if (queryRestart) {
633 pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800634 } else {
635 Uri uri = intent.getData();
636 if (uri == null) {
637 return;
638 }
639 String pkgName = uri.getSchemeSpecificPart();
640 if (pkgName == null) {
641 return;
642 }
Daniel Sandler26ece572012-06-01 15:38:46 -0400643 if (packageChanged) {
644 // We cancel notifications for packages which have just been disabled
Christopher Tate06e5fed2013-10-09 14:39:15 -0700645 try {
Adam Lesinski182f73f2013-12-05 16:48:06 -0800646 final int enabled = getContext().getPackageManager()
Christopher Tate06e5fed2013-10-09 14:39:15 -0700647 .getApplicationEnabledSetting(pkgName);
648 if (enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED
649 || enabled == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
650 cancelNotifications = false;
651 }
652 } catch (IllegalArgumentException e) {
653 // Package doesn't exist; probably racing with uninstall.
654 // cancelNotifications is already true, so nothing to do here.
655 if (DBG) {
656 Slog.i(TAG, "Exception trying to look up app enabled setting", e);
657 }
Daniel Sandler26ece572012-06-01 15:38:46 -0400658 }
659 }
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800660 pkgList = new String[]{pkgName};
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800661 }
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700662
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800663 if (pkgList != null && (pkgList.length > 0)) {
664 for (String pkgName : pkgList) {
John Spurlock79f78922013-05-16 09:10:05 -0400665 if (cancelNotifications) {
John Spurlocke6a7d932014-03-13 12:29:00 -0400666 cancelAllNotificationsInt(MY_UID, MY_PID, pkgName, 0, 0, !queryRestart,
667 UserHandle.USER_ALL, REASON_PACKAGE_CHANGED, null);
John Spurlock79f78922013-05-16 09:10:05 -0400668 }
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800669 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800670 }
John Spurlockb408e8e2014-04-23 21:12:45 -0400671 mListeners.onPackagesChanged(queryReplace, pkgList);
John Spurlock7340fc82014-04-24 18:50:12 -0400672 mConditionProviders.onPackagesChanged(queryReplace, pkgList);
Mike Lockwood63b5ad92011-08-30 09:55:30 -0400673 } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
674 // Keep track of screen on/off state, but do not turn off the notification light
675 // until user passes through the lock screen or views the notification.
676 mScreenOn = true;
677 } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
678 mScreenOn = false;
Daniel Sandlere96ffb12010-03-11 13:38:06 -0500679 } else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) {
John Spurlock5d2eeb12014-01-16 10:46:36 -0500680 mInCall = TelephonyManager.EXTRA_STATE_OFFHOOK
681 .equals(intent.getStringExtra(TelephonyManager.EXTRA_STATE));
Daniel Sandlere96ffb12010-03-11 13:38:06 -0500682 updateNotificationPulse();
Dianne Hackborn80a4af22012-08-27 19:18:31 -0700683 } else if (action.equals(Intent.ACTION_USER_STOPPED)) {
684 int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
685 if (userHandle >= 0) {
John Spurlocke6a7d932014-03-13 12:29:00 -0400686 cancelAllNotificationsInt(MY_UID, MY_PID, null, 0, 0, true, userHandle,
687 REASON_USER_STOPPED, null);
Dianne Hackborn80a4af22012-08-27 19:18:31 -0700688 }
Mike Lockwood63b5ad92011-08-30 09:55:30 -0400689 } else if (action.equals(Intent.ACTION_USER_PRESENT)) {
690 // turn off LED when user passes through lock screen
691 mNotificationLight.turnOff();
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400692 } else if (action.equals(Intent.ACTION_USER_SWITCHED)) {
693 // reload per-user settings
694 mSettingsObserver.update(null);
John Spurlockb408e8e2014-04-23 21:12:45 -0400695 mUserProfiles.updateCache(context);
Kenny Guy3a7c4a52014-03-03 18:24:03 +0000696 } else if (action.equals(Intent.ACTION_USER_ADDED)) {
John Spurlockb408e8e2014-04-23 21:12:45 -0400697 mUserProfiles.updateCache(context);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800698 }
699 }
700 };
701
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700702 class SettingsObserver extends ContentObserver {
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400703 private final Uri NOTIFICATION_LIGHT_PULSE_URI
704 = Settings.System.getUriFor(Settings.System.NOTIFICATION_LIGHT_PULSE);
705
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700706 SettingsObserver(Handler handler) {
707 super(handler);
708 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800709
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700710 void observe() {
Adam Lesinski182f73f2013-12-05 16:48:06 -0800711 ContentResolver resolver = getContext().getContentResolver();
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400712 resolver.registerContentObserver(NOTIFICATION_LIGHT_PULSE_URI,
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700713 false, this, UserHandle.USER_ALL);
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400714 update(null);
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700715 }
716
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400717 @Override public void onChange(boolean selfChange, Uri uri) {
718 update(uri);
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700719 }
720
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400721 public void update(Uri uri) {
Adam Lesinski182f73f2013-12-05 16:48:06 -0800722 ContentResolver resolver = getContext().getContentResolver();
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400723 if (uri == null || NOTIFICATION_LIGHT_PULSE_URI.equals(uri)) {
724 boolean pulseEnabled = Settings.System.getInt(resolver,
725 Settings.System.NOTIFICATION_LIGHT_PULSE, 0) != 0;
726 if (mNotificationPulseEnabled != pulseEnabled) {
727 mNotificationPulseEnabled = pulseEnabled;
728 updateNotificationPulse();
729 }
730 }
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700731 }
732 }
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500733
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400734 private SettingsObserver mSettingsObserver;
John Spurlock056c5192014-04-20 21:52:01 -0400735 private ZenModeHelper mZenModeHelper;
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400736
Daniel Sandleredbb3802012-11-13 20:49:47 -0800737 static long[] getLongArray(Resources r, int resid, int maxlen, long[] def) {
738 int[] ar = r.getIntArray(resid);
739 if (ar == null) {
740 return def;
741 }
742 final int len = ar.length > maxlen ? maxlen : ar.length;
743 long[] out = new long[len];
744 for (int i=0; i<len; i++) {
745 out[i] = ar[i];
746 }
747 return out;
748 }
749
Jeff Brownb880d882014-02-10 19:47:07 -0800750 public NotificationManagerService(Context context) {
751 super(context);
752 }
753
Adam Lesinski182f73f2013-12-05 16:48:06 -0800754 @Override
755 public void onStart() {
Chris Wren54bbef42014-07-09 18:37:56 -0400756 Resources resources = getContext().getResources();
757
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800758 mAm = ActivityManagerNative.getDefault();
Adam Lesinski182f73f2013-12-05 16:48:06 -0800759 mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
760 mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
San Mehat3ee13172010-02-04 20:54:43 -0800761
Adam Lesinski182f73f2013-12-05 16:48:06 -0800762 mHandler = new WorkerHandler();
Chris Wrenf9536642014-04-17 10:01:54 -0400763 mRankingThread.start();
Chris Wren54bbef42014-07-09 18:37:56 -0400764 String[] extractorNames;
765 try {
766 extractorNames = resources.getStringArray(R.array.config_notificationSignalExtractors);
767 } catch (Resources.NotFoundException e) {
768 extractorNames = new String[0];
769 }
770 mRankingHelper = new RankingHelper(getContext(),
771 new RankingWorkerHandler(mRankingThread.getLooper()),
772 extractorNames);
John Spurlock056c5192014-04-20 21:52:01 -0400773 mZenModeHelper = new ZenModeHelper(getContext(), mHandler);
John Spurlock1c923a32014-04-27 16:42:29 -0400774 mZenModeHelper.addCallback(new ZenModeHelper.Callback() {
John Spurlock056c5192014-04-20 21:52:01 -0400775 @Override
776 public void onConfigChanged() {
777 savePolicyFile();
778 }
779 });
780 final File systemDir = new File(Environment.getDataDirectory(), "system");
781 mPolicyFile = new AtomicFile(new File(systemDir, "notification_policy.xml"));
Christoph Studer1c3f81f2014-04-16 15:05:56 +0200782 mUsageStats = new NotificationUsageStats(getContext());
Daniel Sandler4a900ac2013-01-30 14:04:10 -0500783
784 importOldBlockDb();
Daniel Sandler0da673f2012-04-11 12:33:16 -0400785
John Spurlock7340fc82014-04-24 18:50:12 -0400786 mListeners = new NotificationListeners();
787 mConditionProviders = new ConditionProviders(getContext(),
John Spurlocke77bb362014-04-26 10:24:59 -0400788 mHandler, mUserProfiles, mZenModeHelper);
Adam Lesinski182f73f2013-12-05 16:48:06 -0800789 mStatusBar = getLocalService(StatusBarManagerInternal.class);
790 mStatusBar.setNotificationDelegate(mNotificationDelegate);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800791
Adam Lesinski182f73f2013-12-05 16:48:06 -0800792 final LightsManager lights = getLocalService(LightsManager.class);
793 mNotificationLight = lights.getLight(LightsManager.LIGHT_ID_NOTIFICATIONS);
794 mAttentionLight = lights.getLight(LightsManager.LIGHT_ID_ATTENTION);
Mike Lockwood3cb67a32009-11-27 14:25:58 -0500795
Mike Lockwood670f9322010-01-20 12:13:36 -0500796 mDefaultNotificationColor = resources.getColor(
Scott Greenwald9a05b312013-06-28 00:37:54 -0400797 R.color.config_defaultNotificationColor);
Mike Lockwood670f9322010-01-20 12:13:36 -0500798 mDefaultNotificationLedOn = resources.getInteger(
Scott Greenwald9a05b312013-06-28 00:37:54 -0400799 R.integer.config_defaultNotificationLedOn);
Mike Lockwood670f9322010-01-20 12:13:36 -0500800 mDefaultNotificationLedOff = resources.getInteger(
Scott Greenwald9a05b312013-06-28 00:37:54 -0400801 R.integer.config_defaultNotificationLedOff);
Mike Lockwood670f9322010-01-20 12:13:36 -0500802
Daniel Sandleredbb3802012-11-13 20:49:47 -0800803 mDefaultVibrationPattern = getLongArray(resources,
Scott Greenwald9a05b312013-06-28 00:37:54 -0400804 R.array.config_defaultNotificationVibePattern,
Daniel Sandleredbb3802012-11-13 20:49:47 -0800805 VIBRATE_PATTERN_MAXLEN,
806 DEFAULT_VIBRATE_PATTERN);
807
808 mFallbackVibrationPattern = getLongArray(resources,
Scott Greenwald9a05b312013-06-28 00:37:54 -0400809 R.array.config_notificationFallbackVibePattern,
Daniel Sandleredbb3802012-11-13 20:49:47 -0800810 VIBRATE_PATTERN_MAXLEN,
811 DEFAULT_VIBRATE_PATTERN);
812
Chris Wren5116a822014-06-04 15:59:50 -0400813 mUseAttentionLight = resources.getBoolean(R.bool.config_useAttentionLight);
814
Joe Onorato39f5b6a2009-07-23 12:29:19 -0400815 // Don't start allowing notifications until the setup wizard has run once.
816 // After that, including subsequent boots, init with notifications turned on.
817 // This works on the first boot because the setup wizard will toggle this
818 // flag at least once and we'll go back to 0 after that.
Adam Lesinski182f73f2013-12-05 16:48:06 -0800819 if (0 == Settings.Global.getInt(getContext().getContentResolver(),
Jeff Brownbf6f6f92012-09-25 15:03:20 -0700820 Settings.Global.DEVICE_PROVISIONED, 0)) {
John Spurlocke677d712014-02-13 12:52:19 -0500821 mDisableNotificationAlerts = true;
Joe Onorato39f5b6a2009-07-23 12:29:19 -0400822 }
John Spurlock056c5192014-04-20 21:52:01 -0400823 mZenModeHelper.updateZenMode();
Joe Onorato39f5b6a2009-07-23 12:29:19 -0400824
John Spurlockb408e8e2014-04-23 21:12:45 -0400825 mUserProfiles.updateCache(getContext());
Kenny Guya263e4e2014-03-03 18:24:03 +0000826
Mike Lockwood35e16bf2010-11-30 19:53:36 -0500827 // register for various Intents
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800828 IntentFilter filter = new IntentFilter();
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500829 filter.addAction(Intent.ACTION_SCREEN_ON);
830 filter.addAction(Intent.ACTION_SCREEN_OFF);
Daniel Sandlere96ffb12010-03-11 13:38:06 -0500831 filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
Mike Lockwood63b5ad92011-08-30 09:55:30 -0400832 filter.addAction(Intent.ACTION_USER_PRESENT);
Dianne Hackborn80a4af22012-08-27 19:18:31 -0700833 filter.addAction(Intent.ACTION_USER_STOPPED);
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400834 filter.addAction(Intent.ACTION_USER_SWITCHED);
Kenny Guy3a7c4a52014-03-03 18:24:03 +0000835 filter.addAction(Intent.ACTION_USER_ADDED);
Adam Lesinski182f73f2013-12-05 16:48:06 -0800836 getContext().registerReceiver(mIntentReceiver, filter);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800837 IntentFilter pkgFilter = new IntentFilter();
Chris Wren3da73022013-05-10 14:41:21 -0400838 pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800839 pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
Daniel Sandleraac0eb02011-08-06 22:51:56 -0400840 pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800841 pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
842 pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
843 pkgFilter.addDataScheme("package");
Adam Lesinski182f73f2013-12-05 16:48:06 -0800844 getContext().registerReceiver(mIntentReceiver, pkgFilter);
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800845 IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
Adam Lesinski182f73f2013-12-05 16:48:06 -0800846 getContext().registerReceiver(mIntentReceiver, sdFilter);
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800847
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400848 mSettingsObserver = new SettingsObserver(mHandler);
Scott Greenwald9a05b312013-06-28 00:37:54 -0400849
Griff Hazen9f637d12014-06-10 11:13:51 -0700850 mArchive = new Archive(resources.getInteger(
851 R.integer.config_notificationServiceArchiveSize));
852
Adam Lesinski182f73f2013-12-05 16:48:06 -0800853 publishBinderService(Context.NOTIFICATION_SERVICE, mService);
854 publishLocalService(NotificationManagerInternal.class, mInternalService);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800855 }
856
Daniel Sandler4a900ac2013-01-30 14:04:10 -0500857 /**
858 * Read the old XML-based app block database and import those blockages into the AppOps system.
859 */
860 private void importOldBlockDb() {
John Spurlock056c5192014-04-20 21:52:01 -0400861 loadPolicyFile();
Daniel Sandler4a900ac2013-01-30 14:04:10 -0500862
Adam Lesinski182f73f2013-12-05 16:48:06 -0800863 PackageManager pm = getContext().getPackageManager();
Daniel Sandler4a900ac2013-01-30 14:04:10 -0500864 for (String pkg : mBlockedPackages) {
865 PackageInfo info = null;
866 try {
867 info = pm.getPackageInfo(pkg, 0);
Adam Lesinski182f73f2013-12-05 16:48:06 -0800868 setNotificationsEnabledForPackageImpl(pkg, info.applicationInfo.uid, false);
Daniel Sandler4a900ac2013-01-30 14:04:10 -0500869 } catch (NameNotFoundException e) {
870 // forget you
871 }
872 }
873 mBlockedPackages.clear();
Daniel Sandler4a900ac2013-01-30 14:04:10 -0500874 }
875
Adam Lesinski182f73f2013-12-05 16:48:06 -0800876 @Override
877 public void onBootPhase(int phase) {
878 if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
879 // no beeping until we're basically done booting
880 mSystemReady = true;
Jeff Sharkey098d5802012-04-26 17:30:34 -0700881
Adam Lesinski182f73f2013-12-05 16:48:06 -0800882 // Grab our optional AudioService
883 mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
John Spurlockae641c92014-06-30 18:11:40 -0400884 mZenModeHelper.setAudioManager(mAudioManager);
Adam Lesinskia6db4ab2014-03-24 12:31:45 -0700885 } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
886 // This observer will force an update when observe is called, causing us to
887 // bind to listener services.
888 mSettingsObserver.observe();
John Spurlockb408e8e2014-04-23 21:12:45 -0400889 mListeners.onBootPhaseAppsCanStart();
John Spurlock7340fc82014-04-24 18:50:12 -0400890 mConditionProviders.onBootPhaseAppsCanStart();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800891 }
892 }
893
Adam Lesinski182f73f2013-12-05 16:48:06 -0800894 void setNotificationsEnabledForPackageImpl(String pkg, int uid, boolean enabled) {
895 Slog.v(TAG, (enabled?"en":"dis") + "abling notifications for " + pkg);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800896
Adam Lesinski182f73f2013-12-05 16:48:06 -0800897 mAppOps.setMode(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg,
898 enabled ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800899
Adam Lesinski182f73f2013-12-05 16:48:06 -0800900 // Now, cancel any outstanding notifications that are part of a just-disabled app
901 if (ENABLE_BLOCKED_NOTIFICATIONS && !enabled) {
John Spurlocke6a7d932014-03-13 12:29:00 -0400902 cancelAllNotificationsInt(MY_UID, MY_PID, pkg, 0, 0, true, UserHandle.getUserId(uid),
903 REASON_PACKAGE_BANNED, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800904 }
905 }
906
Adam Lesinski182f73f2013-12-05 16:48:06 -0800907 private final IBinder mService = new INotificationManager.Stub() {
908 // Toasts
909 // ============================================================================
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800910
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800911 @Override
Adam Lesinski182f73f2013-12-05 16:48:06 -0800912 public void enqueueToast(String pkg, ITransientNotification callback, int duration)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800913 {
Adam Lesinski182f73f2013-12-05 16:48:06 -0800914 if (DBG) {
915 Slog.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback
916 + " duration=" + duration);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800917 }
Adam Lesinski182f73f2013-12-05 16:48:06 -0800918
919 if (pkg == null || callback == null) {
920 Slog.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback);
921 return ;
922 }
923
John Spurlock7340fc82014-04-24 18:50:12 -0400924 final boolean isSystemToast = isCallerSystem() || ("android".equals(pkg));
Adam Lesinski182f73f2013-12-05 16:48:06 -0800925
926 if (ENABLE_BLOCKED_TOASTS && !noteNotificationOp(pkg, Binder.getCallingUid())) {
927 if (!isSystemToast) {
928 Slog.e(TAG, "Suppressing toast from package " + pkg + " by user request.");
929 return;
930 }
931 }
932
933 synchronized (mToastQueue) {
934 int callingPid = Binder.getCallingPid();
935 long callingId = Binder.clearCallingIdentity();
936 try {
937 ToastRecord record;
938 int index = indexOfToastLocked(pkg, callback);
939 // If it's already in the queue, we update it in place, we don't
940 // move it to the end of the queue.
941 if (index >= 0) {
942 record = mToastQueue.get(index);
943 record.update(duration);
944 } else {
945 // Limit the number of toasts that any given package except the android
946 // package can enqueue. Prevents DOS attacks and deals with leaks.
947 if (!isSystemToast) {
948 int count = 0;
949 final int N = mToastQueue.size();
950 for (int i=0; i<N; i++) {
951 final ToastRecord r = mToastQueue.get(i);
952 if (r.pkg.equals(pkg)) {
953 count++;
954 if (count >= MAX_PACKAGE_NOTIFICATIONS) {
955 Slog.e(TAG, "Package has already posted " + count
956 + " toasts. Not showing more. Package=" + pkg);
957 return;
958 }
959 }
960 }
961 }
962
963 record = new ToastRecord(callingPid, pkg, callback, duration);
964 mToastQueue.add(record);
965 index = mToastQueue.size() - 1;
966 keepProcessAliveLocked(callingPid);
967 }
968 // If it's at index 0, it's the current toast. It doesn't matter if it's
969 // new or just been updated. Call back and tell it to show itself.
970 // If the callback fails, this will remove it from the list, so don't
971 // assume that it's valid after this.
972 if (index == 0) {
973 showNextToastLocked();
974 }
975 } finally {
976 Binder.restoreCallingIdentity(callingId);
977 }
978 }
979 }
980
981 @Override
982 public void cancelToast(String pkg, ITransientNotification callback) {
983 Slog.i(TAG, "cancelToast pkg=" + pkg + " callback=" + callback);
984
985 if (pkg == null || callback == null) {
986 Slog.e(TAG, "Not cancelling notification. pkg=" + pkg + " callback=" + callback);
987 return ;
988 }
989
990 synchronized (mToastQueue) {
991 long callingId = Binder.clearCallingIdentity();
992 try {
993 int index = indexOfToastLocked(pkg, callback);
994 if (index >= 0) {
995 cancelToastLocked(index);
996 } else {
997 Slog.w(TAG, "Toast already cancelled. pkg=" + pkg
998 + " callback=" + callback);
999 }
1000 } finally {
1001 Binder.restoreCallingIdentity(callingId);
1002 }
1003 }
1004 }
1005
1006 @Override
Christoph Studer8fd7f1e2014-04-11 17:35:05 -04001007 public void enqueueNotificationWithTag(String pkg, String opPkg, String tag, int id,
Adam Lesinski182f73f2013-12-05 16:48:06 -08001008 Notification notification, int[] idOut, int userId) throws RemoteException {
Christoph Studer8fd7f1e2014-04-11 17:35:05 -04001009 enqueueNotificationInternal(pkg, opPkg, Binder.getCallingUid(),
Adam Lesinski182f73f2013-12-05 16:48:06 -08001010 Binder.getCallingPid(), tag, id, notification, idOut, userId);
1011 }
1012
1013 @Override
1014 public void cancelNotificationWithTag(String pkg, String tag, int id, int userId) {
John Spurlock7340fc82014-04-24 18:50:12 -04001015 checkCallerIsSystemOrSameApp(pkg);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001016 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1017 Binder.getCallingUid(), userId, true, false, "cancelNotificationWithTag", pkg);
1018 // Don't allow client applications to cancel foreground service notis.
John Spurlocke6a7d932014-03-13 12:29:00 -04001019 cancelNotification(Binder.getCallingUid(), Binder.getCallingPid(), pkg, tag, id, 0,
Adam Lesinski182f73f2013-12-05 16:48:06 -08001020 Binder.getCallingUid() == Process.SYSTEM_UID
John Spurlocke6a7d932014-03-13 12:29:00 -04001021 ? 0 : Notification.FLAG_FOREGROUND_SERVICE, false, userId, REASON_NOMAN_CANCEL,
1022 null);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001023 }
1024
1025 @Override
1026 public void cancelAllNotifications(String pkg, int userId) {
John Spurlock7340fc82014-04-24 18:50:12 -04001027 checkCallerIsSystemOrSameApp(pkg);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001028
1029 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1030 Binder.getCallingUid(), userId, true, false, "cancelAllNotifications", pkg);
1031
1032 // Calling from user space, don't allow the canceling of actively
1033 // running foreground services.
John Spurlocke6a7d932014-03-13 12:29:00 -04001034 cancelAllNotificationsInt(Binder.getCallingUid(), Binder.getCallingPid(),
1035 pkg, 0, Notification.FLAG_FOREGROUND_SERVICE, true, userId,
1036 REASON_NOMAN_CANCEL_ALL, null);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001037 }
1038
1039 @Override
1040 public void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) {
John Spurlock7340fc82014-04-24 18:50:12 -04001041 checkCallerIsSystem();
Adam Lesinski182f73f2013-12-05 16:48:06 -08001042
1043 setNotificationsEnabledForPackageImpl(pkg, uid, enabled);
1044 }
1045
1046 /**
1047 * Use this when you just want to know if notifications are OK for this package.
1048 */
1049 @Override
1050 public boolean areNotificationsEnabledForPackage(String pkg, int uid) {
John Spurlock7340fc82014-04-24 18:50:12 -04001051 checkCallerIsSystem();
Adam Lesinski182f73f2013-12-05 16:48:06 -08001052 return (mAppOps.checkOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
1053 == AppOpsManager.MODE_ALLOWED);
1054 }
1055
Chris Wren54bbef42014-07-09 18:37:56 -04001056 @Override
1057 public void setPackagePriority(String pkg, int uid, int priority) {
1058 checkCallerIsSystem();
1059 mRankingHelper.setPackagePriority(pkg, uid, priority);
1060 savePolicyFile();
1061 }
1062
1063 @Override
1064 public int getPackagePriority(String pkg, int uid) {
1065 checkCallerIsSystem();
1066 return mRankingHelper.getPackagePriority(pkg, uid);
1067 }
1068
Adam Lesinski182f73f2013-12-05 16:48:06 -08001069 /**
1070 * System-only API for getting a list of current (i.e. not cleared) notifications.
1071 *
1072 * Requires ACCESS_NOTIFICATIONS which is signature|system.
Chris Wrenf9536642014-04-17 10:01:54 -04001073 * @returns A list of all the notifications, in natural order.
Adam Lesinski182f73f2013-12-05 16:48:06 -08001074 */
1075 @Override
1076 public StatusBarNotification[] getActiveNotifications(String callingPkg) {
1077 // enforce() will ensure the calling uid has the correct permission
1078 getContext().enforceCallingOrSelfPermission(
1079 android.Manifest.permission.ACCESS_NOTIFICATIONS,
1080 "NotificationManagerService.getActiveNotifications");
1081
1082 StatusBarNotification[] tmp = null;
1083 int uid = Binder.getCallingUid();
1084
1085 // noteOp will check to make sure the callingPkg matches the uid
1086 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
1087 == AppOpsManager.MODE_ALLOWED) {
1088 synchronized (mNotificationList) {
1089 tmp = new StatusBarNotification[mNotificationList.size()];
1090 final int N = mNotificationList.size();
1091 for (int i=0; i<N; i++) {
1092 tmp[i] = mNotificationList.get(i).sbn;
1093 }
1094 }
1095 }
1096 return tmp;
1097 }
1098
1099 /**
1100 * System-only API for getting a list of recent (cleared, no longer shown) notifications.
1101 *
1102 * Requires ACCESS_NOTIFICATIONS which is signature|system.
1103 */
1104 @Override
1105 public StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count) {
1106 // enforce() will ensure the calling uid has the correct permission
1107 getContext().enforceCallingOrSelfPermission(
1108 android.Manifest.permission.ACCESS_NOTIFICATIONS,
1109 "NotificationManagerService.getHistoricalNotifications");
1110
1111 StatusBarNotification[] tmp = null;
1112 int uid = Binder.getCallingUid();
1113
1114 // noteOp will check to make sure the callingPkg matches the uid
1115 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
1116 == AppOpsManager.MODE_ALLOWED) {
1117 synchronized (mArchive) {
1118 tmp = mArchive.getArray(count);
1119 }
1120 }
1121 return tmp;
1122 }
1123
1124 /**
1125 * Register a listener binder directly with the notification manager.
1126 *
1127 * Only works with system callers. Apps should extend
1128 * {@link android.service.notification.NotificationListenerService}.
1129 */
1130 @Override
1131 public void registerListener(final INotificationListener listener,
1132 final ComponentName component, final int userid) {
Christoph Studer3e144d32014-05-22 16:48:40 +02001133 enforceSystemOrSystemUI("INotificationManager.registerListener");
John Spurlock7340fc82014-04-24 18:50:12 -04001134 mListeners.registerService(listener, component, userid);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001135 }
1136
1137 /**
1138 * Remove a listener binder directly
1139 */
1140 @Override
1141 public void unregisterListener(INotificationListener listener, int userid) {
John Spurlock7340fc82014-04-24 18:50:12 -04001142 mListeners.unregisterService(listener, userid);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001143 }
1144
1145 /**
1146 * Allow an INotificationListener to simulate a "clear all" operation.
1147 *
1148 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onClearAllNotifications}
1149 *
1150 * @param token The binder for the listener, to check that the caller is allowed
1151 */
1152 @Override
John Spurlocka4294292014-03-24 18:02:32 -04001153 public void cancelNotificationsFromListener(INotificationListener token, String[] keys) {
John Spurlocke6a7d932014-03-13 12:29:00 -04001154 final int callingUid = Binder.getCallingUid();
1155 final int callingPid = Binder.getCallingPid();
Adam Lesinski182f73f2013-12-05 16:48:06 -08001156 long identity = Binder.clearCallingIdentity();
1157 try {
Adam Lesinskie8240262014-03-26 16:01:00 -07001158 synchronized (mNotificationList) {
John Spurlock7340fc82014-04-24 18:50:12 -04001159 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
John Spurlocka4294292014-03-24 18:02:32 -04001160 if (keys != null) {
1161 final int N = keys.length;
1162 for (int i = 0; i < N; i++) {
1163 NotificationRecord r = mNotificationsByKey.get(keys[i]);
Kenny Guya263e4e2014-03-03 18:24:03 +00001164 final int userId = r.sbn.getUserId();
1165 if (userId != info.userid && userId != UserHandle.USER_ALL &&
John Spurlockb408e8e2014-04-23 21:12:45 -04001166 !mUserProfiles.isCurrentProfile(userId)) {
Kenny Guya263e4e2014-03-03 18:24:03 +00001167 throw new SecurityException("Disallowed call from listener: "
John Spurlock7340fc82014-04-24 18:50:12 -04001168 + info.service);
Kenny Guya263e4e2014-03-03 18:24:03 +00001169 }
John Spurlocka4294292014-03-24 18:02:32 -04001170 if (r != null) {
1171 cancelNotificationFromListenerLocked(info, callingUid, callingPid,
Kenny Guya263e4e2014-03-03 18:24:03 +00001172 r.sbn.getPackageName(), r.sbn.getTag(), r.sbn.getId(),
1173 userId);
John Spurlocka4294292014-03-24 18:02:32 -04001174 }
1175 }
1176 } else {
1177 cancelAllLocked(callingUid, callingPid, info.userid,
Kenny Guya263e4e2014-03-03 18:24:03 +00001178 REASON_LISTENER_CANCEL_ALL, info, info.supportsProfiles());
John Spurlocka4294292014-03-24 18:02:32 -04001179 }
Adam Lesinskie8240262014-03-26 16:01:00 -07001180 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001181 } finally {
1182 Binder.restoreCallingIdentity(identity);
1183 }
1184 }
1185
John Spurlock7340fc82014-04-24 18:50:12 -04001186 private void cancelNotificationFromListenerLocked(ManagedServiceInfo info,
Kenny Guya263e4e2014-03-03 18:24:03 +00001187 int callingUid, int callingPid, String pkg, String tag, int id, int userId) {
John Spurlocka4294292014-03-24 18:02:32 -04001188 cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
1189 Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
1190 true,
Kenny Guya263e4e2014-03-03 18:24:03 +00001191 userId, REASON_LISTENER_CANCEL, info);
John Spurlocka4294292014-03-24 18:02:32 -04001192 }
1193
Adam Lesinski182f73f2013-12-05 16:48:06 -08001194 /**
1195 * Allow an INotificationListener to simulate clearing (dismissing) a single notification.
1196 *
1197 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onNotificationClear}
1198 *
1199 * @param token The binder for the listener, to check that the caller is allowed
1200 */
1201 @Override
1202 public void cancelNotificationFromListener(INotificationListener token, String pkg,
1203 String tag, int id) {
John Spurlocke6a7d932014-03-13 12:29:00 -04001204 final int callingUid = Binder.getCallingUid();
1205 final int callingPid = Binder.getCallingPid();
Adam Lesinski182f73f2013-12-05 16:48:06 -08001206 long identity = Binder.clearCallingIdentity();
1207 try {
Adam Lesinskie8240262014-03-26 16:01:00 -07001208 synchronized (mNotificationList) {
John Spurlock7340fc82014-04-24 18:50:12 -04001209 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
Kenny Guya263e4e2014-03-03 18:24:03 +00001210 if (info.supportsProfiles()) {
1211 Log.e(TAG, "Ignoring deprecated cancelNotification(pkg, tag, id) "
1212 + "from " + info.component
1213 + " use cancelNotification(key) instead.");
1214 } else {
1215 cancelNotificationFromListenerLocked(info, callingUid, callingPid,
1216 pkg, tag, id, info.userid);
1217 }
Adam Lesinskie8240262014-03-26 16:01:00 -07001218 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001219 } finally {
1220 Binder.restoreCallingIdentity(identity);
1221 }
1222 }
1223
1224 /**
1225 * Allow an INotificationListener to request the list of outstanding notifications seen by
1226 * the current user. Useful when starting up, after which point the listener callbacks
1227 * should be used.
1228 *
1229 * @param token The binder for the listener, to check that the caller is allowed
Chris Wrenf9536642014-04-17 10:01:54 -04001230 * @returns The return value will contain the notifications specified in keys, in that
1231 * order, or if keys is null, all the notifications, in natural order.
Adam Lesinski182f73f2013-12-05 16:48:06 -08001232 */
1233 @Override
Christoph Studercee44ba2014-05-20 18:36:43 +02001234 public ParceledListSlice<StatusBarNotification> getActiveNotificationsFromListener(
1235 INotificationListener token) {
Adam Lesinski182f73f2013-12-05 16:48:06 -08001236 synchronized (mNotificationList) {
John Spurlock7340fc82014-04-24 18:50:12 -04001237 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
John Spurlocka4294292014-03-24 18:02:32 -04001238 final ArrayList<StatusBarNotification> list
1239 = new ArrayList<StatusBarNotification>();
Christoph Studercee44ba2014-05-20 18:36:43 +02001240 final int N = mNotificationList.size();
1241 for (int i=0; i<N; i++) {
1242 StatusBarNotification sbn = mNotificationList.get(i).sbn;
1243 if (info.enabledAndUserMatches(sbn.getUserId())) {
1244 list.add(sbn);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001245 }
1246 }
Christoph Studercee44ba2014-05-20 18:36:43 +02001247 return new ParceledListSlice<StatusBarNotification>(list);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001248 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001249 }
1250
1251 @Override
John Spurlock056c5192014-04-20 21:52:01 -04001252 public ZenModeConfig getZenModeConfig() {
John Spurlock856edeb2014-06-01 20:36:47 -04001253 enforceSystemOrSystemUI("INotificationManager.getZenModeConfig");
John Spurlock056c5192014-04-20 21:52:01 -04001254 return mZenModeHelper.getConfig();
1255 }
1256
1257 @Override
1258 public boolean setZenModeConfig(ZenModeConfig config) {
John Spurlock7340fc82014-04-24 18:50:12 -04001259 checkCallerIsSystem();
John Spurlock056c5192014-04-20 21:52:01 -04001260 return mZenModeHelper.setConfig(config);
1261 }
1262
1263 @Override
John Spurlocke77bb362014-04-26 10:24:59 -04001264 public void notifyConditions(String pkg, IConditionProvider provider,
1265 Condition[] conditions) {
1266 final ManagedServiceInfo info = mConditionProviders.checkServiceToken(provider);
1267 checkCallerIsSystemOrSameApp(pkg);
1268 final long identity = Binder.clearCallingIdentity();
1269 try {
1270 mConditionProviders.notifyConditions(pkg, info, conditions);
1271 } finally {
1272 Binder.restoreCallingIdentity(identity);
1273 }
1274 }
1275
1276 @Override
John Spurlock3b98b3f2014-05-01 09:08:48 -04001277 public void requestZenModeConditions(IConditionListener callback, int relevance) {
John Spurlocke77bb362014-04-26 10:24:59 -04001278 enforceSystemOrSystemUI("INotificationManager.requestZenModeConditions");
John Spurlock3b98b3f2014-05-01 09:08:48 -04001279 mConditionProviders.requestZenModeConditions(callback, relevance);
John Spurlocke77bb362014-04-26 10:24:59 -04001280 }
1281
1282 @Override
1283 public void setZenModeCondition(Uri conditionId) {
1284 enforceSystemOrSystemUI("INotificationManager.setZenModeCondition");
John Spurlockaf8d6c42014-05-07 17:49:08 -04001285 final long identity = Binder.clearCallingIdentity();
1286 try {
John Spurlock6ae82a72014-07-16 16:23:01 -04001287 mConditionProviders.setZenModeCondition(conditionId, "binderCall");
John Spurlockaf8d6c42014-05-07 17:49:08 -04001288 } finally {
1289 Binder.restoreCallingIdentity(identity);
1290 }
John Spurlocke77bb362014-04-26 10:24:59 -04001291 }
1292
John Spurlock3b98b3f2014-05-01 09:08:48 -04001293 @Override
1294 public void setAutomaticZenModeConditions(Uri[] conditionIds) {
1295 enforceSystemOrSystemUI("INotificationManager.setAutomaticZenModeConditions");
1296 mConditionProviders.setAutomaticZenModeConditions(conditionIds);
1297 }
1298
1299 @Override
1300 public Condition[] getAutomaticZenModeConditions() {
1301 enforceSystemOrSystemUI("INotificationManager.getAutomaticZenModeConditions");
1302 return mConditionProviders.getAutomaticZenModeConditions();
1303 }
1304
John Spurlocke77bb362014-04-26 10:24:59 -04001305 private void enforceSystemOrSystemUI(String message) {
1306 if (isCallerSystem()) return;
1307 getContext().enforceCallingPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
1308 message);
John Spurlock7340fc82014-04-24 18:50:12 -04001309 }
1310
1311 @Override
Adam Lesinski182f73f2013-12-05 16:48:06 -08001312 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1313 if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
1314 != PackageManager.PERMISSION_GRANTED) {
1315 pw.println("Permission Denial: can't dump NotificationManager from from pid="
1316 + Binder.getCallingPid()
1317 + ", uid=" + Binder.getCallingUid());
1318 return;
1319 }
1320
John Spurlock25e2d242014-06-27 13:58:23 -04001321 dumpImpl(pw, DumpFilter.parseFromArguments(args));
Adam Lesinski182f73f2013-12-05 16:48:06 -08001322 }
1323 };
1324
Chris Wrenf9536642014-04-17 10:01:54 -04001325 private String[] getActiveNotificationKeys(INotificationListener token) {
1326 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1327 final ArrayList<String> keys = new ArrayList<String>();
1328 if (info.isEnabledForCurrentProfiles()) {
1329 synchronized (mNotificationList) {
1330 final int N = mNotificationList.size();
1331 for (int i = 0; i < N; i++) {
1332 final StatusBarNotification sbn = mNotificationList.get(i).sbn;
1333 if (info.enabledAndUserMatches(sbn.getUserId())) {
1334 keys.add(sbn.getKey());
1335 }
John Spurlocka4294292014-03-24 18:02:32 -04001336 }
1337 }
John Spurlocka4294292014-03-24 18:02:32 -04001338 }
Chris Wrenf9536642014-04-17 10:01:54 -04001339 return keys.toArray(new String[keys.size()]);
John Spurlocka4294292014-03-24 18:02:32 -04001340 }
1341
John Spurlock25e2d242014-06-27 13:58:23 -04001342 void dumpImpl(PrintWriter pw, DumpFilter filter) {
1343 pw.print("Current Notification Manager state");
1344 if (filter != null) {
John Spurlock50806fc2014-07-15 10:22:02 -04001345 pw.print(" (filtered to "); pw.print(filter); pw.print(")");
John Spurlock25e2d242014-06-27 13:58:23 -04001346 }
1347 pw.println(':');
Adam Lesinski182f73f2013-12-05 16:48:06 -08001348 int N;
John Spurlock50806fc2014-07-15 10:22:02 -04001349 final boolean zenOnly = filter != null && filter.zen;
Adam Lesinski182f73f2013-12-05 16:48:06 -08001350
John Spurlock50806fc2014-07-15 10:22:02 -04001351 if (!zenOnly) {
1352 synchronized (mToastQueue) {
1353 N = mToastQueue.size();
1354 if (N > 0) {
1355 pw.println(" Toast Queue:");
1356 for (int i=0; i<N; i++) {
1357 mToastQueue.get(i).dump(pw, " ", filter);
1358 }
1359 pw.println(" ");
Adam Lesinski182f73f2013-12-05 16:48:06 -08001360 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001361 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001362 }
1363
1364 synchronized (mNotificationList) {
John Spurlock50806fc2014-07-15 10:22:02 -04001365 if (!zenOnly) {
1366 N = mNotificationList.size();
John Spurlock25e2d242014-06-27 13:58:23 -04001367 if (N > 0) {
John Spurlock50806fc2014-07-15 10:22:02 -04001368 pw.println(" Notification List:");
John Spurlock25e2d242014-06-27 13:58:23 -04001369 for (int i=0; i<N; i++) {
John Spurlock50806fc2014-07-15 10:22:02 -04001370 final NotificationRecord nr = mNotificationList.get(i);
1371 if (filter != null && !filter.matches(nr.sbn)) continue;
1372 nr.dump(pw, " ", getContext());
John Spurlock25e2d242014-06-27 13:58:23 -04001373 }
1374 pw.println(" ");
Adam Lesinski182f73f2013-12-05 16:48:06 -08001375 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001376
John Spurlock50806fc2014-07-15 10:22:02 -04001377 if (filter == null) {
1378 N = mLights.size();
1379 if (N > 0) {
1380 pw.println(" Lights List:");
1381 for (int i=0; i<N; i++) {
1382 pw.println(" " + mLights.get(i));
1383 }
1384 pw.println(" ");
1385 }
1386
1387 pw.println(" mSoundNotification=" + mSoundNotification);
1388 pw.println(" mVibrateNotification=" + mVibrateNotification);
1389 pw.println(" mDisableNotificationAlerts=" + mDisableNotificationAlerts);
1390 pw.println(" mSystemReady=" + mSystemReady);
1391 }
1392 pw.println(" mArchive=" + mArchive.toString());
1393 Iterator<StatusBarNotification> iter = mArchive.descendingIterator();
1394 int i=0;
1395 while (iter.hasNext()) {
1396 final StatusBarNotification sbn = iter.next();
1397 if (filter != null && !filter.matches(sbn)) continue;
1398 pw.println(" " + sbn);
1399 if (++i >= 5) {
1400 if (iter.hasNext()) pw.println(" ...");
1401 break;
1402 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001403 }
1404 }
1405
John Spurlock50806fc2014-07-15 10:22:02 -04001406 if (!zenOnly) {
1407 pw.println("\n Usage Stats:");
1408 mUsageStats.dump(pw, " ", filter);
1409 }
Christoph Studer546bec82014-03-14 12:17:12 +01001410
John Spurlock50806fc2014-07-15 10:22:02 -04001411 if (filter == null || zenOnly) {
John Spurlock25e2d242014-06-27 13:58:23 -04001412 pw.println("\n Zen Mode:");
1413 mZenModeHelper.dump(pw, " ");
John Spurlock6ae82a72014-07-16 16:23:01 -04001414
1415 pw.println("\n Zen Log:");
1416 ZenLog.dump(pw, " ");
John Spurlock25e2d242014-06-27 13:58:23 -04001417 }
John Spurlocke77bb362014-04-26 10:24:59 -04001418
John Spurlock50806fc2014-07-15 10:22:02 -04001419 if (!zenOnly) {
1420 pw.println("\n Ranking Config:");
1421 mRankingHelper.dump(pw, " ", filter);
Chris Wren54bbef42014-07-09 18:37:56 -04001422
John Spurlock50806fc2014-07-15 10:22:02 -04001423 pw.println("\n Notification listeners:");
1424 mListeners.dump(pw, filter);
1425 }
John Spurlocke77bb362014-04-26 10:24:59 -04001426
1427 pw.println("\n Condition providers:");
John Spurlock25e2d242014-06-27 13:58:23 -04001428 mConditionProviders.dump(pw, filter);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001429 }
1430 }
1431
Adam Lesinski182f73f2013-12-05 16:48:06 -08001432 /**
1433 * The private API only accessible to the system process.
1434 */
1435 private final NotificationManagerInternal mInternalService = new NotificationManagerInternal() {
1436 @Override
Christoph Studer8fd7f1e2014-04-11 17:35:05 -04001437 public void enqueueNotification(String pkg, String opPkg, int callingUid, int callingPid,
Adam Lesinski182f73f2013-12-05 16:48:06 -08001438 String tag, int id, Notification notification, int[] idReceived, int userId) {
Christoph Studer8fd7f1e2014-04-11 17:35:05 -04001439 enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, notification,
Adam Lesinski182f73f2013-12-05 16:48:06 -08001440 idReceived, userId);
1441 }
1442 };
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001443
Christoph Studer8fd7f1e2014-04-11 17:35:05 -04001444 void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,
Scott Greenwald9b05c612013-06-25 23:44:05 -04001445 final int callingPid, final String tag, final int id, final Notification notification,
Adam Lesinski182f73f2013-12-05 16:48:06 -08001446 int[] idOut, int incomingUserId) {
Daniel Sandler0da673f2012-04-11 12:33:16 -04001447 if (DBG) {
Adam Lesinski182f73f2013-12-05 16:48:06 -08001448 Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id
1449 + " notification=" + notification);
Daniel Sandler0da673f2012-04-11 12:33:16 -04001450 }
John Spurlock7340fc82014-04-24 18:50:12 -04001451 checkCallerIsSystemOrSameApp(pkg);
1452 final boolean isSystemNotification = isUidSystem(callingUid) || ("android".equals(pkg));
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001453
Scott Greenwald9b05c612013-06-25 23:44:05 -04001454 final int userId = ActivityManager.handleIncomingUser(callingPid,
1455 callingUid, incomingUserId, true, false, "enqueueNotification", pkg);
Jeff Sharkey65c4a2b2012-09-25 17:22:27 -07001456 final UserHandle user = new UserHandle(userId);
Dianne Hackborn41203752012-08-31 14:05:51 -07001457
Joe Onoratobd73d012010-06-04 11:44:54 -07001458 // Limit the number of notifications that any given package except the android
Justin Koh500a6b02014-06-05 19:02:47 +00001459 // package can enqueue. Prevents DOS attacks and deals with leaks.
1460 if (!isSystemNotification) {
Joe Onoratobd73d012010-06-04 11:44:54 -07001461 synchronized (mNotificationList) {
1462 int count = 0;
1463 final int N = mNotificationList.size();
1464 for (int i=0; i<N; i++) {
1465 final NotificationRecord r = mNotificationList.get(i);
Daniel Sandler4f91efd2013-04-25 16:38:41 -04001466 if (r.sbn.getPackageName().equals(pkg) && r.sbn.getUserId() == userId) {
Joe Onoratobd73d012010-06-04 11:44:54 -07001467 count++;
1468 if (count >= MAX_PACKAGE_NOTIFICATIONS) {
1469 Slog.e(TAG, "Package has already posted " + count
1470 + " notifications. Not showing more. package=" + pkg);
1471 return;
1472 }
1473 }
1474 }
1475 }
1476 }
1477
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001478 // This conditional is a dirty hack to limit the logging done on
1479 // behalf of the download manager without affecting other apps.
1480 if (!pkg.equals("com.android.providers.downloads")
1481 || Log.isLoggable("DownloadManager", Log.VERBOSE)) {
John Spurlocke6a7d932014-03-13 12:29:00 -04001482 EventLogTags.writeNotificationEnqueue(callingUid, callingPid,
1483 pkg, id, tag, userId, notification.toString());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001484 }
1485
1486 if (pkg == null || notification == null) {
1487 throw new IllegalArgumentException("null not allowed: pkg=" + pkg
1488 + " id=" + id + " notification=" + notification);
1489 }
1490 if (notification.icon != 0) {
1491 if (notification.contentView == null) {
1492 throw new IllegalArgumentException("contentView required: pkg=" + pkg
1493 + " id=" + id + " notification=" + notification);
1494 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001495 }
1496
Scott Greenwald9b05c612013-06-25 23:44:05 -04001497 mHandler.post(new Runnable() {
1498 @Override
1499 public void run() {
Daniel Sandler0da673f2012-04-11 12:33:16 -04001500
Scott Greenwald9b05c612013-06-25 23:44:05 -04001501 // === Scoring ===
Daniel Sandler0da673f2012-04-11 12:33:16 -04001502
Scott Greenwald9b05c612013-06-25 23:44:05 -04001503 // 0. Sanitize inputs
1504 notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN,
1505 Notification.PRIORITY_MAX);
1506 // Migrate notification flags to scores
1507 if (0 != (notification.flags & Notification.FLAG_HIGH_PRIORITY)) {
1508 if (notification.priority < Notification.PRIORITY_MAX) {
1509 notification.priority = Notification.PRIORITY_MAX;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001510 }
Scott Greenwald9b05c612013-06-25 23:44:05 -04001511 } else if (SCORE_ONGOING_HIGHER &&
1512 0 != (notification.flags & Notification.FLAG_ONGOING_EVENT)) {
1513 if (notification.priority < Notification.PRIORITY_HIGH) {
1514 notification.priority = Notification.PRIORITY_HIGH;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001515 }
1516 }
Daniel Sandlerfde19b12013-01-17 00:21:05 -05001517
Chris Wrena3446562014-06-03 18:11:47 -04001518 // 1. initial score: buckets of 10, around the app [-20..20]
1519 final int score = notification.priority * NOTIFICATION_PRIORITY_MULTIPLIER;
Scott Greenwald9b05c612013-06-25 23:44:05 -04001520
Chris Wrenf9536642014-04-17 10:01:54 -04001521 // 2. extract ranking signals from the notification data
1522 final StatusBarNotification n = new StatusBarNotification(
1523 pkg, opPkg, id, tag, callingUid, callingPid, score, notification,
1524 user);
Chris Wrena3446562014-06-03 18:11:47 -04001525 NotificationRecord r = new NotificationRecord(n, score);
Chris Wren52eba542014-06-02 15:40:32 -04001526 NotificationRecord old = mNotificationsByKey.get(n.getKey());
1527 if (old != null) {
1528 // Retain ranking information from previous record
1529 r.copyRankingInformation(old);
1530 }
Chris Wren54bbef42014-07-09 18:37:56 -04001531 mRankingHelper.extractSignals(r);
Scott Greenwald9a05b312013-06-28 00:37:54 -04001532
Chris Wrenf9536642014-04-17 10:01:54 -04001533 // 3. Apply local rules
Scott Greenwald9a05b312013-06-28 00:37:54 -04001534
Scott Greenwald9b05c612013-06-25 23:44:05 -04001535 // blocked apps
1536 if (ENABLE_BLOCKED_NOTIFICATIONS && !noteNotificationOp(pkg, callingUid)) {
1537 if (!isSystemNotification) {
Chris Wrena3446562014-06-03 18:11:47 -04001538 r.score = JUNK_SCORE;
Scott Greenwald9b05c612013-06-25 23:44:05 -04001539 Slog.e(TAG, "Suppressing notification from package " + pkg
1540 + " by user request.");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001541 }
Daniel Sandlerd4d2de22012-11-14 11:25:46 -08001542 }
1543
Chris Wrena3446562014-06-03 18:11:47 -04001544 if (r.score < SCORE_DISPLAY_THRESHOLD) {
Scott Greenwald9b05c612013-06-25 23:44:05 -04001545 // Notification will be blocked because the score is too low.
1546 return;
1547 }
1548
Scott Greenwald9b05c612013-06-25 23:44:05 -04001549 synchronized (mNotificationList) {
Christoph Studer71f18fd2014-05-20 17:02:04 +02001550 int index = indexOfNotificationLocked(n.getKey());
Scott Greenwald9b05c612013-06-25 23:44:05 -04001551 if (index < 0) {
1552 mNotificationList.add(r);
Christoph Studer546bec82014-03-14 12:17:12 +01001553 mUsageStats.registerPostedByApp(r);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001554 } else {
Christoph Studerd89188d2014-04-03 00:02:39 +02001555 old = mNotificationList.get(index);
1556 mNotificationList.set(index, r);
Christoph Studer061dee22014-05-09 12:28:55 +02001557 mUsageStats.registerUpdatedByApp(r, old);
Scott Greenwald9b05c612013-06-25 23:44:05 -04001558 // Make sure we don't lose the foreground service state.
Christoph Studer71f18fd2014-05-20 17:02:04 +02001559 notification.flags |=
Chris Wrena3446562014-06-03 18:11:47 -04001560 old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;
John Spurlocka4294292014-03-24 18:02:32 -04001561 mNotificationsByKey.remove(old.sbn.getKey());
Chris Wrena3446562014-06-03 18:11:47 -04001562 r.isUpdate = true;
John Spurlocka4294292014-03-24 18:02:32 -04001563 }
Christoph Studer1cd5add2014-07-03 00:23:05 +02001564
John Spurlocka4294292014-03-24 18:02:32 -04001565 mNotificationsByKey.put(n.getKey(), r);
Scott Greenwald9b05c612013-06-25 23:44:05 -04001566
Christoph Studer1cd5add2014-07-03 00:23:05 +02001567 // Ensure if this is a foreground service that the proper additional
1568 // flags are set.
1569 if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
1570 notification.flags |= Notification.FLAG_ONGOING_EVENT
1571 | Notification.FLAG_NO_CLEAR;
1572 }
1573
Chris Wren52eba542014-06-02 15:40:32 -04001574 applyZenModeLocked(r);
Chris Wren52eba542014-06-02 15:40:32 -04001575
Chris Wren54bbef42014-07-09 18:37:56 -04001576 mRankingHelper.sort(mNotificationList);
Chris Wrenf9536642014-04-17 10:01:54 -04001577
Scott Greenwald9b05c612013-06-25 23:44:05 -04001578 if (notification.icon != 0) {
Chris Wrena3446562014-06-03 18:11:47 -04001579 mListeners.notifyPostedLocked(n);
Scott Greenwald9b05c612013-06-25 23:44:05 -04001580 } else {
1581 Slog.e(TAG, "Not posting notification with icon==0: " + notification);
Christoph Studer71f18fd2014-05-20 17:02:04 +02001582 if (old != null && !old.isCanceled) {
Chris Wrena3446562014-06-03 18:11:47 -04001583 mListeners.notifyRemovedLocked(n);
Scott Greenwald9b05c612013-06-25 23:44:05 -04001584 }
1585 // ATTENTION: in a future release we will bail out here
Adam Lesinski182f73f2013-12-05 16:48:06 -08001586 // so that we do not play sounds, show lights, etc. for invalid
1587 // notifications
Scott Greenwald9b05c612013-06-25 23:44:05 -04001588 Slog.e(TAG, "WARNING: In a future release this will crash the app: "
1589 + n.getPackageName());
1590 }
1591
Chris Wrena3446562014-06-03 18:11:47 -04001592 buzzBeepBlinkLocked(r);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001593 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001594 }
Scott Greenwald9b05c612013-06-25 23:44:05 -04001595 });
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001596
1597 idOut[0] = id;
1598 }
1599
Chris Wrena3446562014-06-03 18:11:47 -04001600 private void buzzBeepBlinkLocked(NotificationRecord record) {
1601 final Notification notification = record.sbn.getNotification();
1602
1603 // Should this notification make noise, vibe, or use the LED?
1604 final boolean canInterrupt = (record.score >= SCORE_INTERRUPTION_THRESHOLD) &&
1605 !record.isIntercepted();
1606 if (DBG || record.isIntercepted())
1607 Slog.v(TAG,
1608 "pkg=" + record.sbn.getPackageName() + " canInterrupt=" + canInterrupt +
1609 " intercept=" + record.isIntercepted()
1610 );
1611
1612 final int currentUser;
1613 final long token = Binder.clearCallingIdentity();
1614 try {
1615 currentUser = ActivityManager.getCurrentUser();
1616 } finally {
1617 Binder.restoreCallingIdentity(token);
1618 }
1619
1620 // If we're not supposed to beep, vibrate, etc. then don't.
1621 if (!mDisableNotificationAlerts
1622 && (!(record.isUpdate
1623 && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 ))
1624 && (record.getUserId() == UserHandle.USER_ALL ||
1625 record.getUserId() == currentUser ||
1626 mUserProfiles.isCurrentProfile(record.getUserId()))
1627 && canInterrupt
1628 && mSystemReady
1629 && mAudioManager != null) {
1630 if (DBG) Slog.v(TAG, "Interrupting!");
1631
1632 sendAccessibilityEvent(notification, record.sbn.getPackageName());
1633
1634 // sound
1635
1636 // should we use the default notification sound? (indicated either by
1637 // DEFAULT_SOUND or because notification.sound is pointing at
1638 // Settings.System.NOTIFICATION_SOUND)
1639 final boolean useDefaultSound =
1640 (notification.defaults & Notification.DEFAULT_SOUND) != 0 ||
1641 Settings.System.DEFAULT_NOTIFICATION_URI
1642 .equals(notification.sound);
1643
1644 Uri soundUri = null;
1645 boolean hasValidSound = false;
1646
1647 if (useDefaultSound) {
1648 soundUri = Settings.System.DEFAULT_NOTIFICATION_URI;
1649
1650 // check to see if the default notification sound is silent
1651 ContentResolver resolver = getContext().getContentResolver();
1652 hasValidSound = Settings.System.getString(resolver,
1653 Settings.System.NOTIFICATION_SOUND) != null;
1654 } else if (notification.sound != null) {
1655 soundUri = notification.sound;
1656 hasValidSound = (soundUri != null);
1657 }
1658
1659 if (hasValidSound) {
1660 boolean looping =
1661 (notification.flags & Notification.FLAG_INSISTENT) != 0;
1662 int audioStreamType;
1663 if (notification.audioStreamType >= 0) {
1664 audioStreamType = notification.audioStreamType;
1665 } else {
1666 audioStreamType = DEFAULT_STREAM_TYPE;
1667 }
1668 mSoundNotification = record;
1669 // do not play notifications if stream volume is 0 (typically because
1670 // ringer mode is silent) or if there is a user of exclusive audio focus
1671 if ((mAudioManager.getStreamVolume(audioStreamType) != 0)
1672 && !mAudioManager.isAudioFocusExclusive()) {
1673 final long identity = Binder.clearCallingIdentity();
1674 try {
1675 final IRingtonePlayer player =
1676 mAudioManager.getRingtonePlayer();
1677 if (player != null) {
1678 if (DBG) Slog.v(TAG, "Playing sound " + soundUri
1679 + " on stream " + audioStreamType);
1680 player.playAsync(soundUri, record.sbn.getUser(), looping,
1681 audioStreamType);
1682 }
1683 } catch (RemoteException e) {
1684 } finally {
1685 Binder.restoreCallingIdentity(identity);
1686 }
1687 }
1688 }
1689
1690 // vibrate
1691 // Does the notification want to specify its own vibration?
1692 final boolean hasCustomVibrate = notification.vibrate != null;
1693
1694 // new in 4.2: if there was supposed to be a sound and we're in vibrate
1695 // mode, and no other vibration is specified, we fall back to vibration
1696 final boolean convertSoundToVibration =
1697 !hasCustomVibrate
1698 && hasValidSound
1699 && (mAudioManager.getRingerMode()
1700 == AudioManager.RINGER_MODE_VIBRATE);
1701
1702 // The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback.
1703 final boolean useDefaultVibrate =
1704 (notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
1705
1706 if ((useDefaultVibrate || convertSoundToVibration || hasCustomVibrate)
1707 && !(mAudioManager.getRingerMode()
1708 == AudioManager.RINGER_MODE_SILENT)) {
1709 mVibrateNotification = record;
1710
1711 if (useDefaultVibrate || convertSoundToVibration) {
1712 // Escalate privileges so we can use the vibrator even if the
1713 // notifying app does not have the VIBRATE permission.
1714 long identity = Binder.clearCallingIdentity();
1715 try {
1716 mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
1717 useDefaultVibrate ? mDefaultVibrationPattern
1718 : mFallbackVibrationPattern,
1719 ((notification.flags & Notification.FLAG_INSISTENT) != 0)
John Spurlock7b414672014-07-18 13:02:39 -04001720 ? 0: -1, audioAttributesForNotification(notification));
Chris Wrena3446562014-06-03 18:11:47 -04001721 } finally {
1722 Binder.restoreCallingIdentity(identity);
1723 }
1724 } else if (notification.vibrate.length > 1) {
1725 // If you want your own vibration pattern, you need the VIBRATE
1726 // permission
1727 mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
1728 notification.vibrate,
1729 ((notification.flags & Notification.FLAG_INSISTENT) != 0)
John Spurlock7b414672014-07-18 13:02:39 -04001730 ? 0: -1, audioAttributesForNotification(notification));
Chris Wrena3446562014-06-03 18:11:47 -04001731 }
1732 }
1733 }
1734
1735 // light
1736 // release the light
1737 boolean wasShowLights = mLights.remove(record.getKey());
1738 if (mLedNotification != null && record.getKey().equals(mLedNotification.getKey())) {
1739 mLedNotification = null;
1740 }
1741 if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0 && canInterrupt) {
1742 mLights.add(record.getKey());
1743 updateLightsLocked();
Chris Wren5116a822014-06-04 15:59:50 -04001744 if (mUseAttentionLight) {
1745 mAttentionLight.pulse();
1746 }
Chris Wrena3446562014-06-03 18:11:47 -04001747 } else if (wasShowLights) {
1748 updateLightsLocked();
1749 }
1750 }
1751
John Spurlock7b414672014-07-18 13:02:39 -04001752 private static AudioAttributes audioAttributesForNotification(Notification n) {
1753 return new AudioAttributes.Builder().setLegacyStreamType(n.audioStreamType).build();
1754 }
1755
Adam Lesinski182f73f2013-12-05 16:48:06 -08001756 void showNextToastLocked() {
1757 ToastRecord record = mToastQueue.get(0);
1758 while (record != null) {
1759 if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback);
1760 try {
1761 record.callback.show();
1762 scheduleTimeoutLocked(record);
1763 return;
1764 } catch (RemoteException e) {
1765 Slog.w(TAG, "Object died trying to show notification " + record.callback
1766 + " in package " + record.pkg);
1767 // remove it from the list and let the process die
1768 int index = mToastQueue.indexOf(record);
1769 if (index >= 0) {
1770 mToastQueue.remove(index);
1771 }
1772 keepProcessAliveLocked(record.pid);
1773 if (mToastQueue.size() > 0) {
1774 record = mToastQueue.get(0);
1775 } else {
1776 record = null;
1777 }
1778 }
1779 }
1780 }
1781
1782 void cancelToastLocked(int index) {
1783 ToastRecord record = mToastQueue.get(index);
1784 try {
1785 record.callback.hide();
1786 } catch (RemoteException e) {
1787 Slog.w(TAG, "Object died trying to hide notification " + record.callback
1788 + " in package " + record.pkg);
1789 // don't worry about this, we're about to remove it from
1790 // the list anyway
1791 }
1792 mToastQueue.remove(index);
1793 keepProcessAliveLocked(record.pid);
1794 if (mToastQueue.size() > 0) {
1795 // Show the next one. If the callback fails, this will remove
1796 // it from the list, so don't assume that the list hasn't changed
1797 // after this point.
1798 showNextToastLocked();
1799 }
1800 }
1801
1802 private void scheduleTimeoutLocked(ToastRecord r)
1803 {
1804 mHandler.removeCallbacksAndMessages(r);
1805 Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r);
1806 long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY;
1807 mHandler.sendMessageDelayed(m, delay);
1808 }
1809
1810 private void handleTimeout(ToastRecord record)
1811 {
1812 if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback);
1813 synchronized (mToastQueue) {
1814 int index = indexOfToastLocked(record.pkg, record.callback);
1815 if (index >= 0) {
1816 cancelToastLocked(index);
1817 }
1818 }
1819 }
1820
1821 // lock on mToastQueue
1822 int indexOfToastLocked(String pkg, ITransientNotification callback)
1823 {
1824 IBinder cbak = callback.asBinder();
1825 ArrayList<ToastRecord> list = mToastQueue;
1826 int len = list.size();
1827 for (int i=0; i<len; i++) {
1828 ToastRecord r = list.get(i);
1829 if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) {
1830 return i;
1831 }
1832 }
1833 return -1;
1834 }
1835
1836 // lock on mToastQueue
1837 void keepProcessAliveLocked(int pid)
1838 {
1839 int toastCount = 0; // toasts from this pid
1840 ArrayList<ToastRecord> list = mToastQueue;
1841 int N = list.size();
1842 for (int i=0; i<N; i++) {
1843 ToastRecord r = list.get(i);
1844 if (r.pid == pid) {
1845 toastCount++;
1846 }
1847 }
1848 try {
1849 mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0);
1850 } catch (RemoteException e) {
1851 // Shouldn't happen.
1852 }
1853 }
1854
Chris Wrenf9536642014-04-17 10:01:54 -04001855 private void handleRankingReconsideration(Message message) {
Chris Wren470c1ac2014-05-21 15:28:10 -04001856 if (!(message.obj instanceof RankingReconsideration)) return;
1857 RankingReconsideration recon = (RankingReconsideration) message.obj;
1858 recon.run();
Chris Wren333a61c2014-05-28 16:40:57 -04001859 boolean changed;
Chris Wren470c1ac2014-05-21 15:28:10 -04001860 synchronized (mNotificationList) {
1861 final NotificationRecord record = mNotificationsByKey.get(recon.getKey());
1862 if (record == null) {
1863 return;
Chris Wrenf9536642014-04-17 10:01:54 -04001864 }
Chris Wren333a61c2014-05-28 16:40:57 -04001865 int indexBefore = findNotificationRecordIndexLocked(record);
1866 boolean interceptBefore = record.isIntercepted();
Chris Wren470c1ac2014-05-21 15:28:10 -04001867 recon.applyChangesLocked(record);
Chris Wren333a61c2014-05-28 16:40:57 -04001868 applyZenModeLocked(record);
Chris Wren54bbef42014-07-09 18:37:56 -04001869 mRankingHelper.sort(mNotificationList);
Chris Wren333a61c2014-05-28 16:40:57 -04001870 int indexAfter = findNotificationRecordIndexLocked(record);
1871 boolean interceptAfter = record.isIntercepted();
1872 changed = indexBefore != indexAfter || interceptBefore != interceptAfter;
Chris Wrena3446562014-06-03 18:11:47 -04001873 if (interceptBefore && !interceptAfter) {
1874 buzzBeepBlinkLocked(record);
1875 }
Chris Wrenf9536642014-04-17 10:01:54 -04001876 }
Chris Wren333a61c2014-05-28 16:40:57 -04001877 if (changed) {
Chris Wren470c1ac2014-05-21 15:28:10 -04001878 scheduleSendRankingUpdate();
1879 }
1880 }
1881
Chris Wren54bbef42014-07-09 18:37:56 -04001882 private void handleRankingConfigChange() {
1883 synchronized (mNotificationList) {
1884 final int N = mNotificationList.size();
1885 ArrayList<String> orderBefore = new ArrayList<String>(N);
1886 for (int i = 0; i < N; i++) {
1887 final NotificationRecord r = mNotificationList.get(i);
1888 orderBefore.add(r.getKey());
1889 mRankingHelper.extractSignals(r);
1890 }
1891 mRankingHelper.sort(mNotificationList);
1892 for (int i = 0; i < N; i++) {
1893 if (!orderBefore.get(i).equals(mNotificationList.get(i).getKey())) {
1894 scheduleSendRankingUpdate();
1895 return;
1896 }
1897 }
1898 }
1899 }
1900
Christoph Studerd5092bc2014-07-03 17:47:58 +02001901 // let zen mode evaluate this record
Chris Wren333a61c2014-05-28 16:40:57 -04001902 private void applyZenModeLocked(NotificationRecord record) {
Christoph Studerd5092bc2014-07-03 17:47:58 +02001903 record.setIntercepted(mZenModeHelper.shouldIntercept(record));
Chris Wren333a61c2014-05-28 16:40:57 -04001904 }
1905
Chris Wren470c1ac2014-05-21 15:28:10 -04001906 // lock on mNotificationList
1907 private int findNotificationRecordIndexLocked(NotificationRecord target) {
Chris Wren54bbef42014-07-09 18:37:56 -04001908 return mRankingHelper.indexOf(mNotificationList, target);
Chris Wrenf9536642014-04-17 10:01:54 -04001909 }
1910
1911 private void scheduleSendRankingUpdate() {
1912 mHandler.removeMessages(MESSAGE_SEND_RANKING_UPDATE);
1913 Message m = Message.obtain(mHandler, MESSAGE_SEND_RANKING_UPDATE);
1914 mHandler.sendMessage(m);
1915 }
1916
1917 private void handleSendRankingUpdate() {
1918 synchronized (mNotificationList) {
Chris Wren333a61c2014-05-28 16:40:57 -04001919 mListeners.notifyRankingUpdateLocked();
Chris Wrenf9536642014-04-17 10:01:54 -04001920 }
1921 }
1922
Adam Lesinski182f73f2013-12-05 16:48:06 -08001923 private final class WorkerHandler extends Handler
1924 {
1925 @Override
1926 public void handleMessage(Message msg)
1927 {
1928 switch (msg.what)
1929 {
1930 case MESSAGE_TIMEOUT:
1931 handleTimeout((ToastRecord)msg.obj);
1932 break;
John Spurlock056c5192014-04-20 21:52:01 -04001933 case MESSAGE_SAVE_POLICY_FILE:
1934 handleSavePolicyFile();
1935 break;
Chris Wrenf9536642014-04-17 10:01:54 -04001936 case MESSAGE_SEND_RANKING_UPDATE:
1937 handleSendRankingUpdate();
1938 break;
1939 }
1940 }
1941
1942 }
1943
1944 private final class RankingWorkerHandler extends Handler
1945 {
1946 public RankingWorkerHandler(Looper looper) {
1947 super(looper);
1948 }
1949
1950 @Override
1951 public void handleMessage(Message msg) {
1952 switch (msg.what) {
1953 case MESSAGE_RECONSIDER_RANKING:
1954 handleRankingReconsideration(msg);
1955 break;
Chris Wren54bbef42014-07-09 18:37:56 -04001956 case MESSAGE_RANKING_CONFIG_CHANGE:
1957 handleRankingConfigChange();
1958 break;
Adam Lesinski182f73f2013-12-05 16:48:06 -08001959 }
1960 }
1961 }
1962
Adam Lesinski182f73f2013-12-05 16:48:06 -08001963 // Notifications
1964 // ============================================================================
1965 static int clamp(int x, int low, int high) {
1966 return (x < low) ? low : ((x > high) ? high : x);
1967 }
1968
1969 void sendAccessibilityEvent(Notification notification, CharSequence packageName) {
1970 AccessibilityManager manager = AccessibilityManager.getInstance(getContext());
svetoslavganov75986cf2009-05-14 22:28:01 -07001971 if (!manager.isEnabled()) {
1972 return;
1973 }
1974
1975 AccessibilityEvent event =
1976 AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
1977 event.setPackageName(packageName);
1978 event.setClassName(Notification.class.getName());
1979 event.setParcelableData(notification);
1980 CharSequence tickerText = notification.tickerText;
1981 if (!TextUtils.isEmpty(tickerText)) {
1982 event.getText().add(tickerText);
1983 }
1984
1985 manager.sendAccessibilityEvent(event);
1986 }
1987
Christoph Studer546bec82014-03-14 12:17:12 +01001988 private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason) {
Joe Onorato46439ce2010-11-19 13:56:21 -08001989 // tell the app
1990 if (sendDelete) {
Daniel Sandlerfde19b12013-01-17 00:21:05 -05001991 if (r.getNotification().deleteIntent != null) {
Joe Onorato46439ce2010-11-19 13:56:21 -08001992 try {
Daniel Sandlerfde19b12013-01-17 00:21:05 -05001993 r.getNotification().deleteIntent.send();
Joe Onorato46439ce2010-11-19 13:56:21 -08001994 } catch (PendingIntent.CanceledException ex) {
1995 // do nothing - there's no relevant way to recover, and
1996 // no reason to let this propagate
Daniel Sandler4f91efd2013-04-25 16:38:41 -04001997 Slog.w(TAG, "canceled PendingIntent for " + r.sbn.getPackageName(), ex);
Joe Onorato46439ce2010-11-19 13:56:21 -08001998 }
1999 }
2000 }
2001
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002002 // status bar
Daniel Sandlerfde19b12013-01-17 00:21:05 -05002003 if (r.getNotification().icon != 0) {
Christoph Studer71f18fd2014-05-20 17:02:04 +02002004 r.isCanceled = true;
Chris Wren333a61c2014-05-28 16:40:57 -04002005 mListeners.notifyRemovedLocked(r.sbn);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002006 }
2007
2008 // sound
2009 if (mSoundNotification == r) {
2010 mSoundNotification = null;
Jeff Sharkey098d5802012-04-26 17:30:34 -07002011 final long identity = Binder.clearCallingIdentity();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002012 try {
Adam Lesinski182f73f2013-12-05 16:48:06 -08002013 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
Jeff Sharkey098d5802012-04-26 17:30:34 -07002014 if (player != null) {
2015 player.stopAsync();
2016 }
2017 } catch (RemoteException e) {
2018 } finally {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002019 Binder.restoreCallingIdentity(identity);
2020 }
2021 }
2022
2023 // vibrate
2024 if (mVibrateNotification == r) {
2025 mVibrateNotification = null;
2026 long identity = Binder.clearCallingIdentity();
2027 try {
2028 mVibrator.cancel();
2029 }
2030 finally {
2031 Binder.restoreCallingIdentity(identity);
2032 }
2033 }
2034
2035 // light
Chris Wrena3446562014-06-03 18:11:47 -04002036 mLights.remove(r.getKey());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002037 if (mLedNotification == r) {
2038 mLedNotification = null;
2039 }
Daniel Sandler23d7c702013-03-07 16:32:06 -05002040
Christoph Studer546bec82014-03-14 12:17:12 +01002041 // Record usage stats
2042 switch (reason) {
2043 case REASON_DELEGATE_CANCEL:
2044 case REASON_DELEGATE_CANCEL_ALL:
2045 case REASON_LISTENER_CANCEL:
2046 case REASON_LISTENER_CANCEL_ALL:
2047 mUsageStats.registerDismissedByUser(r);
2048 break;
2049 case REASON_NOMAN_CANCEL:
2050 case REASON_NOMAN_CANCEL_ALL:
2051 mUsageStats.registerRemovedByApp(r);
2052 break;
2053 case REASON_DELEGATE_CLICK:
2054 mUsageStats.registerCancelDueToClick(r);
2055 break;
2056 default:
2057 mUsageStats.registerCancelUnknown(r);
2058 break;
2059 }
2060
Daniel Sandler23d7c702013-03-07 16:32:06 -05002061 // Save it for users of getHistoricalNotifications()
2062 mArchive.record(r.sbn);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002063 }
2064
2065 /**
Dianne Hackbornd8a43f62009-08-17 23:33:56 -07002066 * Cancels a notification ONLY if it has all of the {@code mustHaveFlags}
Doug Zongkerab5c49c2009-12-04 10:31:43 -08002067 * and none of the {@code mustNotHaveFlags}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002068 */
John Spurlocke6a7d932014-03-13 12:29:00 -04002069 void cancelNotification(final int callingUid, final int callingPid,
2070 final String pkg, final String tag, final int id,
Svetoslav Ganov835835e2013-08-04 20:17:52 -07002071 final int mustHaveFlags, final int mustNotHaveFlags, final boolean sendDelete,
John Spurlock7340fc82014-04-24 18:50:12 -04002072 final int userId, final int reason, final ManagedServiceInfo listener) {
Svetoslav Ganov835835e2013-08-04 20:17:52 -07002073 // In enqueueNotificationInternal notifications are added by scheduling the
2074 // work on the worker handler. Hence, we also schedule the cancel on this
2075 // handler to avoid a scenario where an add notification call followed by a
2076 // remove notification call ends up in not removing the notification.
2077 mHandler.post(new Runnable() {
2078 @Override
2079 public void run() {
Christoph Studere4ef156b2014-07-04 18:41:57 +02002080 String listenerName = listener == null ? null : listener.component.toShortString();
John Spurlocke6a7d932014-03-13 12:29:00 -04002081 EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, id, tag, userId,
Christoph Studere4ef156b2014-07-04 18:41:57 +02002082 mustHaveFlags, mustNotHaveFlags, reason, listenerName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002083
Svetoslav Ganov835835e2013-08-04 20:17:52 -07002084 synchronized (mNotificationList) {
2085 int index = indexOfNotificationLocked(pkg, tag, id, userId);
2086 if (index >= 0) {
2087 NotificationRecord r = mNotificationList.get(index);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08002088
Christoph Studer546bec82014-03-14 12:17:12 +01002089 // Ideally we'd do this in the caller of this method. However, that would
2090 // require the caller to also find the notification.
2091 if (reason == REASON_DELEGATE_CLICK) {
2092 mUsageStats.registerClickedByUser(r);
2093 }
2094
Svetoslav Ganov835835e2013-08-04 20:17:52 -07002095 if ((r.getNotification().flags & mustHaveFlags) != mustHaveFlags) {
2096 return;
2097 }
2098 if ((r.getNotification().flags & mustNotHaveFlags) != 0) {
2099 return;
2100 }
2101
2102 mNotificationList.remove(index);
John Spurlocka4294292014-03-24 18:02:32 -04002103 mNotificationsByKey.remove(r.sbn.getKey());
Svetoslav Ganov835835e2013-08-04 20:17:52 -07002104
Christoph Studer546bec82014-03-14 12:17:12 +01002105 cancelNotificationLocked(r, sendDelete, reason);
Christoph Studere4ef156b2014-07-04 18:41:57 +02002106 cancelGroupChildrenLocked(r, callingUid, callingPid, listenerName);
Svetoslav Ganov835835e2013-08-04 20:17:52 -07002107 updateLightsLocked();
2108 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002109 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002110 }
Svetoslav Ganov835835e2013-08-04 20:17:52 -07002111 });
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002112 }
2113
2114 /**
Daniel Sandler321e9c52012-10-12 10:59:26 -07002115 * Determine whether the userId applies to the notification in question, either because
2116 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard).
2117 */
2118 private boolean notificationMatchesUserId(NotificationRecord r, int userId) {
2119 return
2120 // looking for USER_ALL notifications? match everything
2121 userId == UserHandle.USER_ALL
2122 // a notification sent to USER_ALL matches any query
Daniel Sandlerfde19b12013-01-17 00:21:05 -05002123 || r.getUserId() == UserHandle.USER_ALL
Daniel Sandler321e9c52012-10-12 10:59:26 -07002124 // an exact user match
Daniel Sandlerfde19b12013-01-17 00:21:05 -05002125 || r.getUserId() == userId;
Daniel Sandler321e9c52012-10-12 10:59:26 -07002126 }
2127
2128 /**
Kenny Guy3a7c4a52014-03-03 18:24:03 +00002129 * Determine whether the userId applies to the notification in question, either because
2130 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard) or
Kenny Guy2a764942014-04-02 13:29:20 +01002131 * because it matches one of the users profiles.
Kenny Guy3a7c4a52014-03-03 18:24:03 +00002132 */
Kenny Guy2a764942014-04-02 13:29:20 +01002133 private boolean notificationMatchesCurrentProfiles(NotificationRecord r, int userId) {
Kenny Guya263e4e2014-03-03 18:24:03 +00002134 return notificationMatchesUserId(r, userId)
John Spurlockb408e8e2014-04-23 21:12:45 -04002135 || mUserProfiles.isCurrentProfile(r.getUserId());
Kenny Guy3a7c4a52014-03-03 18:24:03 +00002136 }
2137
2138 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002139 * Cancels all notifications from a given package that have all of the
2140 * {@code mustHaveFlags}.
2141 */
John Spurlocke6a7d932014-03-13 12:29:00 -04002142 boolean cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, int mustHaveFlags,
2143 int mustNotHaveFlags, boolean doit, int userId, int reason,
John Spurlock7340fc82014-04-24 18:50:12 -04002144 ManagedServiceInfo listener) {
Christoph Studere4ef156b2014-07-04 18:41:57 +02002145 String listenerName = listener == null ? null : listener.component.toShortString();
John Spurlocke6a7d932014-03-13 12:29:00 -04002146 EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
2147 pkg, userId, mustHaveFlags, mustNotHaveFlags, reason,
Christoph Studere4ef156b2014-07-04 18:41:57 +02002148 listenerName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002149
2150 synchronized (mNotificationList) {
2151 final int N = mNotificationList.size();
Christoph Studere4ef156b2014-07-04 18:41:57 +02002152 ArrayList<NotificationRecord> canceledNotifications = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002153 for (int i = N-1; i >= 0; --i) {
2154 NotificationRecord r = mNotificationList.get(i);
Daniel Sandler321e9c52012-10-12 10:59:26 -07002155 if (!notificationMatchesUserId(r, userId)) {
Dianne Hackborn41203752012-08-31 14:05:51 -07002156 continue;
2157 }
Amith Yamasani5ec00e92012-11-07 16:58:30 -08002158 // Don't remove notifications to all, if there's no package name specified
Daniel Sandlerfde19b12013-01-17 00:21:05 -05002159 if (r.getUserId() == UserHandle.USER_ALL && pkg == null) {
Amith Yamasani5ec00e92012-11-07 16:58:30 -08002160 continue;
2161 }
Daniel Sandlerfde19b12013-01-17 00:21:05 -05002162 if ((r.getFlags() & mustHaveFlags) != mustHaveFlags) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002163 continue;
2164 }
Daniel Sandlerfde19b12013-01-17 00:21:05 -05002165 if ((r.getFlags() & mustNotHaveFlags) != 0) {
Dianne Hackbornd8a43f62009-08-17 23:33:56 -07002166 continue;
2167 }
Daniel Sandler4f91efd2013-04-25 16:38:41 -04002168 if (pkg != null && !r.sbn.getPackageName().equals(pkg)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002169 continue;
2170 }
Christoph Studere4ef156b2014-07-04 18:41:57 +02002171 if (canceledNotifications == null) {
2172 canceledNotifications = new ArrayList<>();
2173 }
2174 canceledNotifications.add(r);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08002175 if (!doit) {
2176 return true;
2177 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002178 mNotificationList.remove(i);
John Spurlocka4294292014-03-24 18:02:32 -04002179 mNotificationsByKey.remove(r.sbn.getKey());
Christoph Studer546bec82014-03-14 12:17:12 +01002180 cancelNotificationLocked(r, false, reason);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002181 }
Christoph Studere4ef156b2014-07-04 18:41:57 +02002182 if (doit && canceledNotifications != null) {
2183 final int M = canceledNotifications.size();
2184 for (int i = 0; i < M; i++) {
2185 cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid,
2186 listenerName);
2187 }
2188 }
2189 if (canceledNotifications != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002190 updateLightsLocked();
2191 }
Christoph Studere4ef156b2014-07-04 18:41:57 +02002192 return canceledNotifications != null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002193 }
2194 }
2195
Adam Lesinski350159c2014-03-27 11:15:11 -07002196 void cancelAllLocked(int callingUid, int callingPid, int userId, int reason,
John Spurlock7340fc82014-04-24 18:50:12 -04002197 ManagedServiceInfo listener, boolean includeCurrentProfiles) {
Christoph Studere4ef156b2014-07-04 18:41:57 +02002198 String listenerName = listener == null ? null : listener.component.toShortString();
John Spurlocke6a7d932014-03-13 12:29:00 -04002199 EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
Christoph Studere4ef156b2014-07-04 18:41:57 +02002200 null, userId, 0, 0, reason, listenerName);
Christoph Studer546bec82014-03-14 12:17:12 +01002201
Christoph Studere4ef156b2014-07-04 18:41:57 +02002202 ArrayList<NotificationRecord> canceledNotifications = null;
Adam Lesinskie8240262014-03-26 16:01:00 -07002203 final int N = mNotificationList.size();
2204 for (int i=N-1; i>=0; i--) {
2205 NotificationRecord r = mNotificationList.get(i);
Kenny Guya263e4e2014-03-03 18:24:03 +00002206 if (includeCurrentProfiles) {
2207 if (!notificationMatchesCurrentProfiles(r, userId)) {
2208 continue;
2209 }
2210 } else {
2211 if (!notificationMatchesUserId(r, userId)) {
2212 continue;
2213 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002214 }
2215
Adam Lesinskie8240262014-03-26 16:01:00 -07002216 if ((r.getFlags() & (Notification.FLAG_ONGOING_EVENT
2217 | Notification.FLAG_NO_CLEAR)) == 0) {
2218 mNotificationList.remove(i);
John Spurlocka4294292014-03-24 18:02:32 -04002219 mNotificationsByKey.remove(r.sbn.getKey());
Christoph Studer546bec82014-03-14 12:17:12 +01002220 cancelNotificationLocked(r, true, reason);
Christoph Studere4ef156b2014-07-04 18:41:57 +02002221 // Make a note so we can cancel children later.
2222 if (canceledNotifications == null) {
2223 canceledNotifications = new ArrayList<>();
2224 }
2225 canceledNotifications.add(r);
Adam Lesinskie8240262014-03-26 16:01:00 -07002226 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002227 }
Christoph Studere4ef156b2014-07-04 18:41:57 +02002228 int M = canceledNotifications != null ? canceledNotifications.size() : 0;
2229 for (int i = 0; i < M; i++) {
2230 cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid,
2231 listenerName);
2232 }
Adam Lesinskie8240262014-03-26 16:01:00 -07002233 updateLightsLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002234 }
2235
Christoph Studere4ef156b2014-07-04 18:41:57 +02002236 // Warning: The caller is responsible for invoking updateLightsLocked().
2237 private void cancelGroupChildrenLocked(NotificationRecord r, int callingUid, int callingPid,
2238 String listenerName) {
2239 Notification n = r.getNotification();
2240 if (n.getGroup() == null || (n.flags & Notification.FLAG_GROUP_SUMMARY) == 0) {
2241 return;
2242 }
2243
2244 String pkg = r.sbn.getPackageName();
2245 int userId = r.getUserId();
2246
2247 if (pkg == null) {
2248 if (DBG) Log.e(TAG, "No package for group summary: " + r.getKey());
2249 return;
2250 }
2251
2252 final int N = mNotificationList.size();
2253 for (int i = N - 1; i >= 0; i--) {
2254 NotificationRecord childR = mNotificationList.get(i);
2255 Notification childN = childR.getNotification();
2256 StatusBarNotification childSbn = childR.sbn;
2257 if (childR.getUserId() == userId && pkg.equals(childSbn.getPackageName()) &&
2258 n.getGroup().equals(childN.getGroup())) {
2259 EventLogTags.writeNotificationCancel(callingUid, callingPid,
2260 pkg, childSbn.getId(), childSbn.getTag(), userId, 0, 0,
2261 REASON_GROUP_SUMMARY_CANCELED, listenerName);
2262 mNotificationList.remove(i);
2263 mNotificationsByKey.remove(childR.getKey());
2264 cancelNotificationLocked(childR, false, REASON_GROUP_SUMMARY_CANCELED);
2265 }
2266 }
2267 }
2268
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002269 // lock on mNotificationList
Adam Lesinski182f73f2013-12-05 16:48:06 -08002270 void updateLightsLocked()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002271 {
The Android Open Source Project10592532009-03-18 17:39:46 -07002272 // handle notification lights
2273 if (mLedNotification == null) {
2274 // get next notification, if any
2275 int n = mLights.size();
2276 if (n > 0) {
Chris Wrena3446562014-06-03 18:11:47 -04002277 mLedNotification = mNotificationsByKey.get(mLights.get(n-1));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002278 }
2279 }
Mike Lockwoodc22404a2009-12-02 11:15:02 -05002280
Mike Lockwood63b5ad92011-08-30 09:55:30 -04002281 // Don't flash while we are in a call or screen is on
2282 if (mLedNotification == null || mInCall || mScreenOn) {
Mike Lockwood3cb67a32009-11-27 14:25:58 -05002283 mNotificationLight.turnOff();
The Android Open Source Project10592532009-03-18 17:39:46 -07002284 } else {
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -04002285 final Notification ledno = mLedNotification.sbn.getNotification();
Daniel Sandlerfde19b12013-01-17 00:21:05 -05002286 int ledARGB = ledno.ledARGB;
2287 int ledOnMS = ledno.ledOnMS;
2288 int ledOffMS = ledno.ledOffMS;
2289 if ((ledno.defaults & Notification.DEFAULT_LIGHTS) != 0) {
Mike Lockwood670f9322010-01-20 12:13:36 -05002290 ledARGB = mDefaultNotificationColor;
2291 ledOnMS = mDefaultNotificationLedOn;
2292 ledOffMS = mDefaultNotificationLedOff;
2293 }
2294 if (mNotificationPulseEnabled) {
2295 // pulse repeatedly
Adam Lesinski182f73f2013-12-05 16:48:06 -08002296 mNotificationLight.setFlashing(ledARGB, Light.LIGHT_FLASH_TIMED,
Mike Lockwood670f9322010-01-20 12:13:36 -05002297 ledOnMS, ledOffMS);
Mike Lockwood670f9322010-01-20 12:13:36 -05002298 }
The Android Open Source Project10592532009-03-18 17:39:46 -07002299 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002300 }
2301
2302 // lock on mNotificationList
Adam Lesinski182f73f2013-12-05 16:48:06 -08002303 int indexOfNotificationLocked(String pkg, String tag, int id, int userId)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002304 {
2305 ArrayList<NotificationRecord> list = mNotificationList;
2306 final int len = list.size();
2307 for (int i=0; i<len; i++) {
2308 NotificationRecord r = list.get(i);
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -04002309 if (!notificationMatchesUserId(r, userId) || r.sbn.getId() != id) {
Dianne Hackborn41203752012-08-31 14:05:51 -07002310 continue;
2311 }
Fred Quintana6ecaff12009-09-25 14:23:13 -07002312 if (tag == null) {
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -04002313 if (r.sbn.getTag() != null) {
Fred Quintana6ecaff12009-09-25 14:23:13 -07002314 continue;
2315 }
2316 } else {
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -04002317 if (!tag.equals(r.sbn.getTag())) {
Fred Quintana6ecaff12009-09-25 14:23:13 -07002318 continue;
2319 }
2320 }
Daniel Sandler4f91efd2013-04-25 16:38:41 -04002321 if (r.sbn.getPackageName().equals(pkg)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002322 return i;
2323 }
2324 }
2325 return -1;
2326 }
2327
Christoph Studer71f18fd2014-05-20 17:02:04 +02002328 // lock on mNotificationList
2329 int indexOfNotificationLocked(String key) {
Christoph Studerc5115552014-06-12 20:22:31 +02002330 final int N = mNotificationList.size();
2331 for (int i = 0; i < N; i++) {
2332 if (key.equals(mNotificationList.get(i).getKey())) {
2333 return i;
2334 }
Christoph Studer71f18fd2014-05-20 17:02:04 +02002335 }
Christoph Studerc5115552014-06-12 20:22:31 +02002336 return -1;
Christoph Studer71f18fd2014-05-20 17:02:04 +02002337 }
2338
Mike Lockwoodc22404a2009-12-02 11:15:02 -05002339 private void updateNotificationPulse() {
2340 synchronized (mNotificationList) {
2341 updateLightsLocked();
2342 }
2343 }
John Spurlocke677d712014-02-13 12:52:19 -05002344
John Spurlock7340fc82014-04-24 18:50:12 -04002345 private static boolean isUidSystem(int uid) {
2346 final int appid = UserHandle.getAppId(uid);
2347 return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0);
2348 }
John Spurlockb408e8e2014-04-23 21:12:45 -04002349
John Spurlock7340fc82014-04-24 18:50:12 -04002350 private static boolean isCallerSystem() {
2351 return isUidSystem(Binder.getCallingUid());
2352 }
2353
2354 private static void checkCallerIsSystem() {
2355 if (isCallerSystem()) {
2356 return;
2357 }
2358 throw new SecurityException("Disallowed call for uid " + Binder.getCallingUid());
2359 }
2360
2361 private static void checkCallerIsSystemOrSameApp(String pkg) {
2362 if (isCallerSystem()) {
2363 return;
2364 }
2365 final int uid = Binder.getCallingUid();
2366 try {
2367 ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo(
2368 pkg, 0, UserHandle.getCallingUserId());
2369 if (!UserHandle.isSameApp(ai.uid, uid)) {
2370 throw new SecurityException("Calling uid " + uid + " gave package"
2371 + pkg + " which is owned by uid " + ai.uid);
2372 }
2373 } catch (RemoteException re) {
2374 throw new SecurityException("Unknown package " + pkg + "\n" + re);
2375 }
2376 }
2377
Christoph Studer05ad4822014-05-16 14:16:03 +02002378 /**
2379 * Generates a NotificationRankingUpdate from 'sbns', considering only
2380 * notifications visible to the given listener.
Chris Wren333a61c2014-05-28 16:40:57 -04002381 *
2382 * <p>Caller must hold a lock on mNotificationList.</p>
Christoph Studer05ad4822014-05-16 14:16:03 +02002383 */
Chris Wren333a61c2014-05-28 16:40:57 -04002384 private NotificationRankingUpdate makeRankingUpdateLocked(ManagedServiceInfo info) {
Christoph Studer05ad4822014-05-16 14:16:03 +02002385 int speedBumpIndex = -1;
Chris Wren333a61c2014-05-28 16:40:57 -04002386 final int N = mNotificationList.size();
2387 ArrayList<String> keys = new ArrayList<String>(N);
Christoph Studer1d599da2014-06-12 15:25:59 +02002388 ArrayList<String> interceptedKeys = new ArrayList<String>(N);
Chris Wren333a61c2014-05-28 16:40:57 -04002389 for (int i = 0; i < N; i++) {
2390 NotificationRecord record = mNotificationList.get(i);
2391 if (!info.enabledAndUserMatches(record.sbn.getUserId())) {
Christoph Studer05ad4822014-05-16 14:16:03 +02002392 continue;
2393 }
Chris Wren333a61c2014-05-28 16:40:57 -04002394 keys.add(record.sbn.getKey());
2395 if (record.isIntercepted()) {
Christoph Studer1d599da2014-06-12 15:25:59 +02002396 interceptedKeys.add(record.sbn.getKey());
Christoph Studer05ad4822014-05-16 14:16:03 +02002397 }
2398 if (speedBumpIndex == -1 &&
Chris Wren333a61c2014-05-28 16:40:57 -04002399 record.sbn.getNotification().priority == Notification.PRIORITY_MIN) {
Christoph Studer05ad4822014-05-16 14:16:03 +02002400 speedBumpIndex = keys.size() - 1;
2401 }
2402 }
2403 String[] keysAr = keys.toArray(new String[keys.size()]);
Christoph Studer1d599da2014-06-12 15:25:59 +02002404 String[] interceptedKeysAr = interceptedKeys.toArray(new String[interceptedKeys.size()]);
2405 return new NotificationRankingUpdate(keysAr, interceptedKeysAr, speedBumpIndex);
Christoph Studer05ad4822014-05-16 14:16:03 +02002406 }
2407
John Spurlock7340fc82014-04-24 18:50:12 -04002408 public class NotificationListeners extends ManagedServices {
2409
2410 public NotificationListeners() {
2411 super(getContext(), mHandler, mNotificationList, mUserProfiles);
2412 }
2413
2414 @Override
2415 protected Config getConfig() {
2416 Config c = new Config();
2417 c.caption = "notification listener";
2418 c.serviceInterface = NotificationListenerService.SERVICE_INTERFACE;
2419 c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_LISTENERS;
2420 c.bindPermission = android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE;
2421 c.settingsAction = Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS;
2422 c.clientLabel = R.string.notification_listener_binding_label;
2423 return c;
2424 }
2425
2426 @Override
2427 protected IInterface asInterface(IBinder binder) {
2428 return INotificationListener.Stub.asInterface(binder);
2429 }
2430
2431 @Override
John Spurlock3b98b3f2014-05-01 09:08:48 -04002432 public void onServiceAdded(ManagedServiceInfo info) {
2433 final INotificationListener listener = (INotificationListener) info.service;
Chris Wren333a61c2014-05-28 16:40:57 -04002434 final NotificationRankingUpdate update;
Christoph Studer05ad4822014-05-16 14:16:03 +02002435 synchronized (mNotificationList) {
Chris Wren333a61c2014-05-28 16:40:57 -04002436 update = makeRankingUpdateLocked(info);
Christoph Studer05ad4822014-05-16 14:16:03 +02002437 }
John Spurlock7340fc82014-04-24 18:50:12 -04002438 try {
Chris Wren333a61c2014-05-28 16:40:57 -04002439 listener.onListenerConnected(update);
John Spurlock7340fc82014-04-24 18:50:12 -04002440 } catch (RemoteException e) {
2441 // we tried
2442 }
2443 }
2444
2445 /**
2446 * asynchronously notify all listeners about a new notification
2447 */
Chris Wren333a61c2014-05-28 16:40:57 -04002448 public void notifyPostedLocked(StatusBarNotification sbn) {
John Spurlock7340fc82014-04-24 18:50:12 -04002449 // make a copy in case changes are made to the underlying Notification object
2450 final StatusBarNotification sbnClone = sbn.clone();
2451 for (final ManagedServiceInfo info : mServices) {
Christoph Studer05ad4822014-05-16 14:16:03 +02002452 if (!info.isEnabledForCurrentProfiles()) {
2453 continue;
Chris Wrenf9536642014-04-17 10:01:54 -04002454 }
Chris Wren333a61c2014-05-28 16:40:57 -04002455 final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
Christoph Studer05ad4822014-05-16 14:16:03 +02002456 if (update.getOrderedKeys().length == 0) {
2457 continue;
2458 }
2459 mHandler.post(new Runnable() {
2460 @Override
2461 public void run() {
2462 notifyPostedIfUserMatch(info, sbnClone, update);
2463 }
2464 });
Kenny Guy3a7c4a52014-03-03 18:24:03 +00002465 }
2466 }
Kenny Guy3a7c4a52014-03-03 18:24:03 +00002467
John Spurlock7340fc82014-04-24 18:50:12 -04002468 /**
2469 * asynchronously notify all listeners about a removed notification
2470 */
Chris Wren333a61c2014-05-28 16:40:57 -04002471 public void notifyRemovedLocked(StatusBarNotification sbn) {
John Spurlock7340fc82014-04-24 18:50:12 -04002472 // make a copy in case changes are made to the underlying Notification object
2473 // NOTE: this copy is lightweight: it doesn't include heavyweight parts of the
2474 // notification
2475 final StatusBarNotification sbnLight = sbn.cloneLight();
Chris Wrenf9536642014-04-17 10:01:54 -04002476 for (final ManagedServiceInfo info : mServices) {
Christoph Studer05ad4822014-05-16 14:16:03 +02002477 if (!info.isEnabledForCurrentProfiles()) {
2478 continue;
Chris Wrenf9536642014-04-17 10:01:54 -04002479 }
Chris Wren333a61c2014-05-28 16:40:57 -04002480 final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
Christoph Studer05ad4822014-05-16 14:16:03 +02002481 mHandler.post(new Runnable() {
2482 @Override
2483 public void run() {
Chris Wren333a61c2014-05-28 16:40:57 -04002484 notifyRemovedIfUserMatch(info, sbnLight, update);
Christoph Studer05ad4822014-05-16 14:16:03 +02002485 }
2486 });
Chris Wrenf9536642014-04-17 10:01:54 -04002487 }
2488 }
2489
2490 /**
2491 * asynchronously notify all listeners about a reordering of notifications
Chris Wrenf9536642014-04-17 10:01:54 -04002492 */
Chris Wren333a61c2014-05-28 16:40:57 -04002493 public void notifyRankingUpdateLocked() {
Chris Wrenf9536642014-04-17 10:01:54 -04002494 for (final ManagedServiceInfo serviceInfo : mServices) {
Christoph Studer05ad4822014-05-16 14:16:03 +02002495 if (!serviceInfo.isEnabledForCurrentProfiles()) {
2496 continue;
2497 }
Chris Wren333a61c2014-05-28 16:40:57 -04002498 final NotificationRankingUpdate update =
2499 makeRankingUpdateLocked(serviceInfo);
John Spurlock7340fc82014-04-24 18:50:12 -04002500 mHandler.post(new Runnable() {
2501 @Override
2502 public void run() {
Chris Wren333a61c2014-05-28 16:40:57 -04002503 notifyRankingUpdate(serviceInfo, update);
John Spurlock7340fc82014-04-24 18:50:12 -04002504 }
2505 });
Kenny Guya263e4e2014-03-03 18:24:03 +00002506 }
Kenny Guya263e4e2014-03-03 18:24:03 +00002507 }
Kenny Guya263e4e2014-03-03 18:24:03 +00002508
Chris Wrenf9536642014-04-17 10:01:54 -04002509 private void notifyPostedIfUserMatch(final ManagedServiceInfo info,
Christoph Studer05ad4822014-05-16 14:16:03 +02002510 final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) {
John Spurlock7340fc82014-04-24 18:50:12 -04002511 if (!info.enabledAndUserMatches(sbn.getUserId())) {
2512 return;
2513 }
2514 final INotificationListener listener = (INotificationListener)info.service;
2515 try {
Christoph Studer05ad4822014-05-16 14:16:03 +02002516 listener.onNotificationPosted(sbn, rankingUpdate);
John Spurlock7340fc82014-04-24 18:50:12 -04002517 } catch (RemoteException ex) {
2518 Log.e(TAG, "unable to notify listener (posted): " + listener, ex);
2519 }
2520 }
2521
Chris Wrenf9536642014-04-17 10:01:54 -04002522 private void notifyRemovedIfUserMatch(ManagedServiceInfo info, StatusBarNotification sbn,
Christoph Studer05ad4822014-05-16 14:16:03 +02002523 NotificationRankingUpdate rankingUpdate) {
John Spurlock7340fc82014-04-24 18:50:12 -04002524 if (!info.enabledAndUserMatches(sbn.getUserId())) {
2525 return;
2526 }
Christoph Studer05ad4822014-05-16 14:16:03 +02002527 final INotificationListener listener = (INotificationListener) info.service;
John Spurlock7340fc82014-04-24 18:50:12 -04002528 try {
Christoph Studer05ad4822014-05-16 14:16:03 +02002529 listener.onNotificationRemoved(sbn, rankingUpdate);
John Spurlock7340fc82014-04-24 18:50:12 -04002530 } catch (RemoteException ex) {
2531 Log.e(TAG, "unable to notify listener (removed): " + listener, ex);
John Spurlockb408e8e2014-04-23 21:12:45 -04002532 }
Kenny Guya263e4e2014-03-03 18:24:03 +00002533 }
Chris Wrenf9536642014-04-17 10:01:54 -04002534
Christoph Studer05ad4822014-05-16 14:16:03 +02002535 private void notifyRankingUpdate(ManagedServiceInfo info,
2536 NotificationRankingUpdate rankingUpdate) {
2537 final INotificationListener listener = (INotificationListener) info.service;
Chris Wrenf9536642014-04-17 10:01:54 -04002538 try {
Christoph Studer05ad4822014-05-16 14:16:03 +02002539 listener.onNotificationRankingUpdate(rankingUpdate);
Chris Wrenf9536642014-04-17 10:01:54 -04002540 } catch (RemoteException ex) {
2541 Log.e(TAG, "unable to notify listener (ranking update): " + listener, ex);
2542 }
2543 }
Kenny Guya263e4e2014-03-03 18:24:03 +00002544 }
John Spurlock25e2d242014-06-27 13:58:23 -04002545
2546 public static final class DumpFilter {
2547 public String pkgFilter;
John Spurlock50806fc2014-07-15 10:22:02 -04002548 public boolean zen;
John Spurlock25e2d242014-06-27 13:58:23 -04002549
2550 public static DumpFilter parseFromArguments(String[] args) {
John Spurlock50806fc2014-07-15 10:22:02 -04002551 if (args != null && args.length == 2 && "p".equals(args[0])
2552 && args[1] != null && !args[1].trim().isEmpty()) {
2553 final DumpFilter filter = new DumpFilter();
2554 filter.pkgFilter = args[1].trim().toLowerCase();
2555 return filter;
John Spurlock25e2d242014-06-27 13:58:23 -04002556 }
John Spurlock50806fc2014-07-15 10:22:02 -04002557 if (args != null && args.length == 1 && "zen".equals(args[0])) {
2558 final DumpFilter filter = new DumpFilter();
2559 filter.zen = true;
2560 return filter;
2561 }
2562 return null;
John Spurlock25e2d242014-06-27 13:58:23 -04002563 }
2564
2565 public boolean matches(StatusBarNotification sbn) {
John Spurlock50806fc2014-07-15 10:22:02 -04002566 return zen ? true : sbn != null
2567 && (matches(sbn.getPackageName()) || matches(sbn.getOpPkg()));
John Spurlock25e2d242014-06-27 13:58:23 -04002568 }
2569
2570 public boolean matches(ComponentName component) {
John Spurlock50806fc2014-07-15 10:22:02 -04002571 return zen ? true : component != null && matches(component.getPackageName());
John Spurlock25e2d242014-06-27 13:58:23 -04002572 }
2573
2574 public boolean matches(String pkg) {
John Spurlock50806fc2014-07-15 10:22:02 -04002575 return zen ? true : pkg != null && pkg.toLowerCase().contains(pkgFilter);
2576 }
2577
2578 @Override
2579 public String toString() {
2580 return zen ? "zen" : ('\'' + pkgFilter + '\'');
John Spurlock25e2d242014-06-27 13:58:23 -04002581 }
2582 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002583}