blob: fc1b74690be2fe65e647bf8dfdf108626cd7999d [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
John Spurlockd8afe3c2014-08-01 14:04:07 -040019import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_EFFECTS;
Jeff Sharkey098d5802012-04-26 17:30:34 -070020import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
21import static org.xmlpull.v1.XmlPullParser.END_TAG;
22import static org.xmlpull.v1.XmlPullParser.START_TAG;
svetoslavganov75986cf2009-05-14 22:28:01 -070023
Dianne Hackborn41203752012-08-31 14:05:51 -070024import android.app.ActivityManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080025import android.app.ActivityManagerNative;
John Spurlock7340fc82014-04-24 18:50:12 -040026import android.app.AppGlobals;
Daniel Sandler4a900ac2013-01-30 14:04:10 -050027import android.app.AppOpsManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080028import android.app.IActivityManager;
29import android.app.INotificationManager;
30import android.app.ITransientNotification;
31import android.app.Notification;
John Spurlockb4782522014-08-22 14:54:46 -040032import android.app.NotificationManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080033import android.app.PendingIntent;
34import android.app.StatusBarManager;
35import android.content.BroadcastReceiver;
Daniel Sandler5feceeb2013-03-22 18:29:23 -070036import android.content.ComponentName;
Dianne Hackborn1dac2772009-06-26 18:16:48 -070037import android.content.ContentResolver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080038import android.content.Context;
39import android.content.Intent;
40import android.content.IntentFilter;
John Spurlock7340fc82014-04-24 18:50:12 -040041import android.content.pm.ApplicationInfo;
Daniel Sandler4a900ac2013-01-30 14:04:10 -050042import android.content.pm.PackageInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080043import android.content.pm.PackageManager;
44import android.content.pm.PackageManager.NameNotFoundException;
Christoph Studercee44ba2014-05-20 18:36:43 +020045import android.content.pm.ParceledListSlice;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080046import android.content.res.Resources;
Dianne Hackborn1dac2772009-06-26 18:16:48 -070047import android.database.ContentObserver;
John Spurlock7b414672014-07-18 13:02:39 -040048import android.media.AudioAttributes;
svetoslavganov75986cf2009-05-14 22:28:01 -070049import android.media.AudioManager;
Jeff Sharkey098d5802012-04-26 17:30:34 -070050import android.media.IRingtonePlayer;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080051import android.net.Uri;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080052import android.os.Binder;
John Spurlock056c5192014-04-20 21:52:01 -040053import android.os.Environment;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080054import android.os.Handler;
Chris Wrenf9536642014-04-17 10:01:54 -040055import android.os.HandlerThread;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080056import android.os.IBinder;
John Spurlock7340fc82014-04-24 18:50:12 -040057import android.os.IInterface;
Chris Wrenf9536642014-04-17 10:01:54 -040058import android.os.Looper;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080059import android.os.Message;
Dianne Hackbornd8a43f62009-08-17 23:33:56 -070060import android.os.Process;
svetoslavganov75986cf2009-05-14 22:28:01 -070061import android.os.RemoteException;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -070062import android.os.UserHandle;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080063import android.os.Vibrator;
64import android.provider.Settings;
Chris Wren333a61c2014-05-28 16:40:57 -040065import android.service.notification.Condition;
John Spurlocke77bb362014-04-26 10:24:59 -040066import android.service.notification.IConditionListener;
John Spurlock7340fc82014-04-24 18:50:12 -040067import android.service.notification.IConditionProvider;
Chris Wren333a61c2014-05-28 16:40:57 -040068import android.service.notification.INotificationListener;
John Spurlock7340fc82014-04-24 18:50:12 -040069import android.service.notification.NotificationListenerService;
Christoph Studer05ad4822014-05-16 14:16:03 +020070import android.service.notification.NotificationRankingUpdate;
Daniel Sandler5feceeb2013-03-22 18:29:23 -070071import android.service.notification.StatusBarNotification;
John Spurlock056c5192014-04-20 21:52:01 -040072import android.service.notification.ZenModeConfig;
Daniel Sandlere96ffb12010-03-11 13:38:06 -050073import android.telephony.TelephonyManager;
svetoslavganov75986cf2009-05-14 22:28:01 -070074import android.text.TextUtils;
John Spurlocka4294292014-03-24 18:02:32 -040075import android.util.ArrayMap;
John Spurlock1fa865f2014-07-21 14:56:39 -040076import android.util.ArraySet;
Dianne Hackborn39606a02012-07-31 17:54:35 -070077import android.util.AtomicFile;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080078import android.util.Log;
Andy Stadler110988c2010-12-03 14:29:16 -080079import android.util.Slog;
Daniel Sandler0da673f2012-04-11 12:33:16 -040080import android.util.Xml;
svetoslavganov75986cf2009-05-14 22:28:01 -070081import android.view.accessibility.AccessibilityEvent;
82import android.view.accessibility.AccessibilityManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080083import android.widget.Toast;
84
Scott Greenwald9a05b312013-06-28 00:37:54 -040085import com.android.internal.R;
John Spurlock056c5192014-04-20 21:52:01 -040086import com.android.internal.util.FastXmlSerializer;
Adam Lesinski182f73f2013-12-05 16:48:06 -080087import com.android.server.EventLogTags;
Adam Lesinski182f73f2013-12-05 16:48:06 -080088import com.android.server.SystemService;
89import com.android.server.lights.Light;
90import com.android.server.lights.LightsManager;
John Spurlock7340fc82014-04-24 18:50:12 -040091import com.android.server.notification.ManagedServices.ManagedServiceInfo;
92import com.android.server.notification.ManagedServices.UserProfiles;
John Spurlockb408e8e2014-04-23 21:12:45 -040093import com.android.server.statusbar.StatusBarManagerInternal;
94
95import libcore.io.IoUtils;
Adam Lesinski182f73f2013-12-05 16:48:06 -080096
Jeff Sharkey098d5802012-04-26 17:30:34 -070097import org.xmlpull.v1.XmlPullParser;
98import org.xmlpull.v1.XmlPullParserException;
John Spurlock056c5192014-04-20 21:52:01 -040099import org.xmlpull.v1.XmlSerializer;
Jeff Sharkey098d5802012-04-26 17:30:34 -0700100
Daniel Sandler0da673f2012-04-11 12:33:16 -0400101import java.io.File;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800102import java.io.FileDescriptor;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400103import java.io.FileInputStream;
104import java.io.FileNotFoundException;
John Spurlock056c5192014-04-20 21:52:01 -0400105import java.io.FileOutputStream;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400106import java.io.IOException;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800107import java.io.PrintWriter;
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500108import java.util.ArrayDeque;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800109import java.util.ArrayList;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400110import java.util.HashSet;
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500111import java.util.Iterator;
112import java.util.NoSuchElementException;
John Spurlockb4782522014-08-22 14:54:46 -0400113import java.util.Objects;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400114
Daniel Sandlerd0a2f862010-08-03 15:29:31 -0400115/** {@hide} */
Adam Lesinski182f73f2013-12-05 16:48:06 -0800116public class NotificationManagerService extends SystemService {
117 static final String TAG = "NotificationService";
118 static final boolean DBG = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800119
Adam Lesinski182f73f2013-12-05 16:48:06 -0800120 static final int MAX_PACKAGE_NOTIFICATIONS = 50;
Joe Onoratobd73d012010-06-04 11:44:54 -0700121
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800122 // message codes
Adam Lesinski182f73f2013-12-05 16:48:06 -0800123 static final int MESSAGE_TIMEOUT = 2;
John Spurlock056c5192014-04-20 21:52:01 -0400124 static final int MESSAGE_SAVE_POLICY_FILE = 3;
Chris Wrenf9536642014-04-17 10:01:54 -0400125 static final int MESSAGE_RECONSIDER_RANKING = 4;
Chris Wren54bbef42014-07-09 18:37:56 -0400126 static final int MESSAGE_RANKING_CONFIG_CHANGE = 5;
127 static final int MESSAGE_SEND_RANKING_UPDATE = 6;
John Spurlockd8afe3c2014-08-01 14:04:07 -0400128 static final int MESSAGE_LISTENER_HINTS_CHANGED = 7;
Christoph Studer85a384b2014-08-27 20:16:15 +0200129 static final int MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED = 8;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800130
Adam Lesinski182f73f2013-12-05 16:48:06 -0800131 static final int LONG_DELAY = 3500; // 3.5 seconds
132 static final int SHORT_DELAY = 2000; // 2 seconds
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800133
Adam Lesinski182f73f2013-12-05 16:48:06 -0800134 static final long[] DEFAULT_VIBRATE_PATTERN = {0, 250, 250, 250};
135 static final int VIBRATE_PATTERN_MAXLEN = 8 * 2 + 1; // up to eight bumps
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800136
Adam Lesinski182f73f2013-12-05 16:48:06 -0800137 static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_NOTIFICATION;
138 static final boolean SCORE_ONGOING_HIGHER = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800139
Adam Lesinski182f73f2013-12-05 16:48:06 -0800140 static final int JUNK_SCORE = -1000;
141 static final int NOTIFICATION_PRIORITY_MULTIPLIER = 10;
142 static final int SCORE_DISPLAY_THRESHOLD = Notification.PRIORITY_MIN * NOTIFICATION_PRIORITY_MULTIPLIER;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400143
Daniel Sandler526fa0e2012-12-04 14:51:50 -0500144 // Notifications with scores below this will not interrupt the user, either via LED or
145 // sound or vibration
Adam Lesinski182f73f2013-12-05 16:48:06 -0800146 static final int SCORE_INTERRUPTION_THRESHOLD =
Daniel Sandler526fa0e2012-12-04 14:51:50 -0500147 Notification.PRIORITY_LOW * NOTIFICATION_PRIORITY_MULTIPLIER;
148
Adam Lesinski182f73f2013-12-05 16:48:06 -0800149 static final boolean ENABLE_BLOCKED_NOTIFICATIONS = true;
150 static final boolean ENABLE_BLOCKED_TOASTS = true;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400151
Adam Lesinski182f73f2013-12-05 16:48:06 -0800152 private IActivityManager mAm;
153 AudioManager mAudioManager;
154 StatusBarManagerInternal mStatusBar;
155 Vibrator mVibrator;
156
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800157 final IBinder mForegroundToken = new Binder();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800158 private WorkerHandler mHandler;
Chris Wrenf9536642014-04-17 10:01:54 -0400159 private final HandlerThread mRankingThread = new HandlerThread("ranker",
160 Process.THREAD_PRIORITY_BACKGROUND);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800161
Adam Lesinski182f73f2013-12-05 16:48:06 -0800162 private Light mNotificationLight;
163 Light mAttentionLight;
Mike Lockwood670f9322010-01-20 12:13:36 -0500164 private int mDefaultNotificationColor;
165 private int mDefaultNotificationLedOn;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800166
Mike Lockwood670f9322010-01-20 12:13:36 -0500167 private int mDefaultNotificationLedOff;
Daniel Sandleredbb3802012-11-13 20:49:47 -0800168 private long[] mDefaultVibrationPattern;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800169
Daniel Sandleredbb3802012-11-13 20:49:47 -0800170 private long[] mFallbackVibrationPattern;
Chris Wren5116a822014-06-04 15:59:50 -0400171 private boolean mUseAttentionLight;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800172 boolean mSystemReady;
Daniel Sandleredbb3802012-11-13 20:49:47 -0800173
John Spurlockd8afe3c2014-08-01 14:04:07 -0400174 private boolean mDisableNotificationEffects;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800175 NotificationRecord mSoundNotification;
176 NotificationRecord mVibrateNotification;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800177
John Spurlockd8afe3c2014-08-01 14:04:07 -0400178 private final ArraySet<ManagedServiceInfo> mListenersDisablingEffects = new ArraySet<>();
John Spurlockb4782522014-08-22 14:54:46 -0400179 private ComponentName mEffectsSuppressor;
John Spurlockd8afe3c2014-08-01 14:04:07 -0400180 private int mListenerHints; // right now, all hints are global
Christoph Studer85a384b2014-08-27 20:16:15 +0200181 private int mInterruptionFilter; // current ZEN mode as communicated to listeners
John Spurlock1fa865f2014-07-21 14:56:39 -0400182
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500183 // for enabling and disabling notification pulse behavior
Mike Lockwood63b5ad92011-08-30 09:55:30 -0400184 private boolean mScreenOn = true;
Daniel Sandlere96ffb12010-03-11 13:38:06 -0500185 private boolean mInCall = false;
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500186 private boolean mNotificationPulseEnabled;
187
Daniel Sandler09a247e2013-02-14 10:24:17 -0500188 // used as a mutex for access to all active notifications & listeners
Adam Lesinski182f73f2013-12-05 16:48:06 -0800189 final ArrayList<NotificationRecord> mNotificationList =
Fred Quintana6ecaff12009-09-25 14:23:13 -0700190 new ArrayList<NotificationRecord>();
John Spurlocka4294292014-03-24 18:02:32 -0400191 final ArrayMap<String, NotificationRecord> mNotificationsByKey =
192 new ArrayMap<String, NotificationRecord>();
Adam Lesinski182f73f2013-12-05 16:48:06 -0800193 final ArrayList<ToastRecord> mToastQueue = new ArrayList<ToastRecord>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800194
Chris Wrena3446562014-06-03 18:11:47 -0400195 ArrayList<String> mLights = new ArrayList<String>();
Adam Lesinski182f73f2013-12-05 16:48:06 -0800196 NotificationRecord mLedNotification;
svetoslavganov75986cf2009-05-14 22:28:01 -0700197
Adam Lesinski182f73f2013-12-05 16:48:06 -0800198 private AppOpsManager mAppOps;
Daniel Sandler4a900ac2013-01-30 14:04:10 -0500199
Griff Hazen9f637d12014-06-10 11:13:51 -0700200 private Archive mArchive;
201
Daniel Sandler0da673f2012-04-11 12:33:16 -0400202 // Notification control database. For now just contains disabled packages.
203 private AtomicFile mPolicyFile;
204 private HashSet<String> mBlockedPackages = new HashSet<String>();
205
206 private static final int DB_VERSION = 1;
207
208 private static final String TAG_BODY = "notification-policy";
209 private static final String ATTR_VERSION = "version";
210
211 private static final String TAG_BLOCKED_PKGS = "blocked-packages";
212 private static final String TAG_PACKAGE = "package";
213 private static final String ATTR_NAME = "name";
214
Chris Wren54bbef42014-07-09 18:37:56 -0400215 private RankingHelper mRankingHelper;
Scott Greenwald9a05b312013-06-28 00:37:54 -0400216
John Spurlockb408e8e2014-04-23 21:12:45 -0400217 private final UserProfiles mUserProfiles = new UserProfiles();
John Spurlock7340fc82014-04-24 18:50:12 -0400218 private NotificationListeners mListeners;
219 private ConditionProviders mConditionProviders;
Christoph Studer1c3f81f2014-04-16 15:05:56 +0200220 private NotificationUsageStats mUsageStats;
Christoph Studer546bec82014-03-14 12:17:12 +0100221
John Spurlocke6a7d932014-03-13 12:29:00 -0400222 private static final int MY_UID = Process.myUid();
223 private static final int MY_PID = Process.myPid();
224 private static final int REASON_DELEGATE_CLICK = 1;
225 private static final int REASON_DELEGATE_CANCEL = 2;
226 private static final int REASON_DELEGATE_CANCEL_ALL = 3;
227 private static final int REASON_DELEGATE_ERROR = 4;
228 private static final int REASON_PACKAGE_CHANGED = 5;
229 private static final int REASON_USER_STOPPED = 6;
230 private static final int REASON_PACKAGE_BANNED = 7;
231 private static final int REASON_NOMAN_CANCEL = 8;
232 private static final int REASON_NOMAN_CANCEL_ALL = 9;
233 private static final int REASON_LISTENER_CANCEL = 10;
234 private static final int REASON_LISTENER_CANCEL_ALL = 11;
Christoph Studere4ef156b2014-07-04 18:41:57 +0200235 private static final int REASON_GROUP_SUMMARY_CANCELED = 12;
John Spurlocke6a7d932014-03-13 12:29:00 -0400236
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500237 private static class Archive {
Griff Hazen9f637d12014-06-10 11:13:51 -0700238 final int mBufferSize;
239 final ArrayDeque<StatusBarNotification> mBuffer;
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500240
Griff Hazen9f637d12014-06-10 11:13:51 -0700241 public Archive(int size) {
242 mBufferSize = size;
243 mBuffer = new ArrayDeque<StatusBarNotification>(mBufferSize);
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500244 }
Jeff Sharkey0c1baf92013-04-03 13:08:52 -0700245
Daniel Sandler5e62e3a2013-04-15 20:57:02 -0400246 public String toString() {
247 final StringBuilder sb = new StringBuilder();
248 final int N = mBuffer.size();
249 sb.append("Archive (");
250 sb.append(N);
251 sb.append(" notification");
252 sb.append((N==1)?")":"s)");
253 return sb.toString();
254 }
255
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500256 public void record(StatusBarNotification nr) {
Griff Hazen9f637d12014-06-10 11:13:51 -0700257 if (mBuffer.size() == mBufferSize) {
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500258 mBuffer.removeFirst();
259 }
Daniel Sandler26b81d52013-05-20 20:56:43 -0400260
261 // We don't want to store the heavy bits of the notification in the archive,
262 // but other clients in the system process might be using the object, so we
263 // store a (lightened) copy.
264 mBuffer.addLast(nr.cloneLight());
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500265 }
266
267 public void clear() {
268 mBuffer.clear();
269 }
270
271 public Iterator<StatusBarNotification> descendingIterator() {
272 return mBuffer.descendingIterator();
273 }
274 public Iterator<StatusBarNotification> ascendingIterator() {
275 return mBuffer.iterator();
276 }
277 public Iterator<StatusBarNotification> filter(
278 final Iterator<StatusBarNotification> iter, final String pkg, final int userId) {
279 return new Iterator<StatusBarNotification>() {
280 StatusBarNotification mNext = findNext();
281
282 private StatusBarNotification findNext() {
283 while (iter.hasNext()) {
284 StatusBarNotification nr = iter.next();
Daniel Sandler4f91efd2013-04-25 16:38:41 -0400285 if ((pkg == null || nr.getPackageName() == pkg)
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500286 && (userId == UserHandle.USER_ALL || nr.getUserId() == userId)) {
287 return nr;
288 }
289 }
290 return null;
291 }
292
293 @Override
294 public boolean hasNext() {
295 return mNext == null;
296 }
297
298 @Override
299 public StatusBarNotification next() {
300 StatusBarNotification next = mNext;
301 if (next == null) {
302 throw new NoSuchElementException();
303 }
304 mNext = findNext();
305 return next;
306 }
307
308 @Override
309 public void remove() {
310 iter.remove();
311 }
312 };
313 }
Daniel Sandler78d0d252013-02-12 08:14:52 -0500314
315 public StatusBarNotification[] getArray(int count) {
Griff Hazen9f637d12014-06-10 11:13:51 -0700316 if (count == 0) count = mBufferSize;
Daniel Sandler78d0d252013-02-12 08:14:52 -0500317 final StatusBarNotification[] a
318 = new StatusBarNotification[Math.min(count, mBuffer.size())];
319 Iterator<StatusBarNotification> iter = descendingIterator();
320 int i=0;
321 while (iter.hasNext() && i < count) {
322 a[i++] = iter.next();
323 }
324 return a;
325 }
326
327 public StatusBarNotification[] getArray(int count, String pkg, int userId) {
Griff Hazen9f637d12014-06-10 11:13:51 -0700328 if (count == 0) count = mBufferSize;
Daniel Sandler78d0d252013-02-12 08:14:52 -0500329 final StatusBarNotification[] a
330 = new StatusBarNotification[Math.min(count, mBuffer.size())];
331 Iterator<StatusBarNotification> iter = filter(descendingIterator(), pkg, userId);
332 int i=0;
333 while (iter.hasNext() && i < count) {
334 a[i++] = iter.next();
335 }
336 return a;
337 }
338
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500339 }
340
John Spurlock056c5192014-04-20 21:52:01 -0400341 private void loadPolicyFile() {
342 synchronized(mPolicyFile) {
343 mBlockedPackages.clear();
Daniel Sandler0da673f2012-04-11 12:33:16 -0400344
John Spurlock056c5192014-04-20 21:52:01 -0400345 FileInputStream infile = null;
346 try {
347 infile = mPolicyFile.openRead();
348 final XmlPullParser parser = Xml.newPullParser();
349 parser.setInput(infile, null);
Daniel Sandler0da673f2012-04-11 12:33:16 -0400350
John Spurlock056c5192014-04-20 21:52:01 -0400351 int type;
352 String tag;
353 int version = DB_VERSION;
354 while ((type = parser.next()) != END_DOCUMENT) {
355 tag = parser.getName();
356 if (type == START_TAG) {
357 if (TAG_BODY.equals(tag)) {
358 version = Integer.parseInt(
359 parser.getAttributeValue(null, ATTR_VERSION));
360 } else if (TAG_BLOCKED_PKGS.equals(tag)) {
361 while ((type = parser.next()) != END_DOCUMENT) {
362 tag = parser.getName();
363 if (TAG_PACKAGE.equals(tag)) {
364 mBlockedPackages.add(
365 parser.getAttributeValue(null, ATTR_NAME));
366 } else if (TAG_BLOCKED_PKGS.equals(tag) && type == END_TAG) {
367 break;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400368 }
369 }
370 }
371 }
John Spurlock056c5192014-04-20 21:52:01 -0400372 mZenModeHelper.readXml(parser);
Chris Wren54bbef42014-07-09 18:37:56 -0400373 mRankingHelper.readXml(parser);
Daniel Sandler0da673f2012-04-11 12:33:16 -0400374 }
John Spurlock056c5192014-04-20 21:52:01 -0400375 } catch (FileNotFoundException e) {
376 // No data yet
377 } catch (IOException e) {
378 Log.wtf(TAG, "Unable to read notification policy", e);
379 } catch (NumberFormatException e) {
380 Log.wtf(TAG, "Unable to parse notification policy", e);
381 } catch (XmlPullParserException e) {
382 Log.wtf(TAG, "Unable to parse notification policy", e);
383 } finally {
384 IoUtils.closeQuietly(infile);
385 }
386 }
387 }
388
389 public void savePolicyFile() {
390 mHandler.removeMessages(MESSAGE_SAVE_POLICY_FILE);
391 mHandler.sendEmptyMessage(MESSAGE_SAVE_POLICY_FILE);
392 }
393
394 private void handleSavePolicyFile() {
395 Slog.d(TAG, "handleSavePolicyFile");
396 synchronized (mPolicyFile) {
397 final FileOutputStream stream;
398 try {
399 stream = mPolicyFile.startWrite();
400 } catch (IOException e) {
401 Slog.w(TAG, "Failed to save policy file", e);
402 return;
403 }
404
405 try {
406 final XmlSerializer out = new FastXmlSerializer();
407 out.setOutput(stream, "utf-8");
408 out.startDocument(null, true);
409 out.startTag(null, TAG_BODY);
410 out.attribute(null, ATTR_VERSION, Integer.toString(DB_VERSION));
411 mZenModeHelper.writeXml(out);
Chris Wren54bbef42014-07-09 18:37:56 -0400412 mRankingHelper.writeXml(out);
John Spurlock056c5192014-04-20 21:52:01 -0400413 out.endTag(null, TAG_BODY);
414 out.endDocument();
415 mPolicyFile.finishWrite(stream);
416 } catch (IOException e) {
417 Slog.w(TAG, "Failed to save policy file, restoring backup", e);
418 mPolicyFile.failWrite(stream);
Daniel Sandler0da673f2012-04-11 12:33:16 -0400419 }
420 }
421 }
422
Daniel Sandler4a900ac2013-01-30 14:04:10 -0500423 /** Use this when you actually want to post a notification or toast.
424 *
425 * Unchecked. Not exposed via Binder, but can be called in the course of enqueue*().
426 */
427 private boolean noteNotificationOp(String pkg, int uid) {
428 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
429 != AppOpsManager.MODE_ALLOWED) {
430 Slog.v(TAG, "notifications are disabled by AppOps for " + pkg);
431 return false;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400432 }
Daniel Sandler4a900ac2013-01-30 14:04:10 -0500433 return true;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400434 }
435
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800436 private static final class ToastRecord
437 {
438 final int pid;
439 final String pkg;
440 final ITransientNotification callback;
441 int duration;
442
443 ToastRecord(int pid, String pkg, ITransientNotification callback, int duration)
444 {
445 this.pid = pid;
446 this.pkg = pkg;
447 this.callback = callback;
448 this.duration = duration;
449 }
450
451 void update(int duration) {
452 this.duration = duration;
453 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800454
John Spurlock25e2d242014-06-27 13:58:23 -0400455 void dump(PrintWriter pw, String prefix, DumpFilter filter) {
456 if (filter != null && !filter.matches(pkg)) return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800457 pw.println(prefix + this);
458 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800459
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800460 @Override
461 public final String toString()
462 {
463 return "ToastRecord{"
464 + Integer.toHexString(System.identityHashCode(this))
465 + " pkg=" + pkg
466 + " callback=" + callback
467 + " duration=" + duration;
468 }
469 }
470
Adam Lesinski182f73f2013-12-05 16:48:06 -0800471 private final NotificationDelegate mNotificationDelegate = new NotificationDelegate() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800472
Adam Lesinski182f73f2013-12-05 16:48:06 -0800473 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800474 public void onSetDisabled(int status) {
475 synchronized (mNotificationList) {
John Spurlockd8afe3c2014-08-01 14:04:07 -0400476 mDisableNotificationEffects =
477 (status & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0;
478 if (disableNotificationEffects()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800479 // cancel whatever's going on
480 long identity = Binder.clearCallingIdentity();
481 try {
Adam Lesinski182f73f2013-12-05 16:48:06 -0800482 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
Jeff Sharkey098d5802012-04-26 17:30:34 -0700483 if (player != null) {
484 player.stopAsync();
485 }
486 } catch (RemoteException e) {
487 } finally {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800488 Binder.restoreCallingIdentity(identity);
489 }
490
491 identity = Binder.clearCallingIdentity();
492 try {
493 mVibrator.cancel();
Jeff Sharkey098d5802012-04-26 17:30:34 -0700494 } finally {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800495 Binder.restoreCallingIdentity(identity);
496 }
497 }
498 }
499 }
500
Adam Lesinski182f73f2013-12-05 16:48:06 -0800501 @Override
John Spurlocke6a7d932014-03-13 12:29:00 -0400502 public void onClearAll(int callingUid, int callingPid, int userId) {
Adam Lesinskie8240262014-03-26 16:01:00 -0700503 synchronized (mNotificationList) {
Kenny Guya263e4e2014-03-03 18:24:03 +0000504 cancelAllLocked(callingUid, callingPid, userId, REASON_DELEGATE_CANCEL_ALL, null,
505 /*includeCurrentProfiles*/ true);
Adam Lesinskie8240262014-03-26 16:01:00 -0700506 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800507 }
508
Adam Lesinski182f73f2013-12-05 16:48:06 -0800509 @Override
Christoph Studer03b87a22014-04-30 17:33:27 +0200510 public void onNotificationClick(int callingUid, int callingPid, String key) {
511 synchronized (mNotificationList) {
512 EventLogTags.writeNotificationClicked(key);
513 NotificationRecord r = mNotificationsByKey.get(key);
514 if (r == null) {
515 Log.w(TAG, "No notification with key: " + key);
516 return;
517 }
518 StatusBarNotification sbn = r.sbn;
519 cancelNotification(callingUid, callingPid, sbn.getPackageName(), sbn.getTag(),
520 sbn.getId(), Notification.FLAG_AUTO_CANCEL,
521 Notification.FLAG_FOREGROUND_SERVICE, false, r.getUserId(),
522 REASON_DELEGATE_CLICK, null);
523 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800524 }
525
Adam Lesinski182f73f2013-12-05 16:48:06 -0800526 @Override
John Spurlocke6a7d932014-03-13 12:29:00 -0400527 public void onNotificationClear(int callingUid, int callingPid,
528 String pkg, String tag, int id, int userId) {
529 cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
Kenny Guy3a7c4a52014-03-03 18:24:03 +0000530 Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
John Spurlocke6a7d932014-03-13 12:29:00 -0400531 true, userId, REASON_DELEGATE_CANCEL, null);
Daniel Sandler0f0b11c2010-08-04 15:54:58 -0400532 }
533
Adam Lesinski182f73f2013-12-05 16:48:06 -0800534 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800535 public void onPanelRevealed() {
Christoph Studer760ea552014-03-21 13:10:21 +0100536 EventLogTags.writeNotificationPanelRevealed();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800537 synchronized (mNotificationList) {
538 // sound
539 mSoundNotification = null;
Jeff Sharkey098d5802012-04-26 17:30:34 -0700540
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800541 long identity = Binder.clearCallingIdentity();
542 try {
Adam Lesinski182f73f2013-12-05 16:48:06 -0800543 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
Jeff Sharkey098d5802012-04-26 17:30:34 -0700544 if (player != null) {
545 player.stopAsync();
546 }
547 } catch (RemoteException e) {
548 } finally {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800549 Binder.restoreCallingIdentity(identity);
550 }
551
552 // vibrate
553 mVibrateNotification = null;
554 identity = Binder.clearCallingIdentity();
555 try {
556 mVibrator.cancel();
Jeff Sharkey098d5802012-04-26 17:30:34 -0700557 } finally {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800558 Binder.restoreCallingIdentity(identity);
559 }
560
561 // light
562 mLights.clear();
563 mLedNotification = null;
564 updateLightsLocked();
565 }
566 }
Joe Onorato005847b2010-06-04 16:08:02 -0400567
Adam Lesinski182f73f2013-12-05 16:48:06 -0800568 @Override
Christoph Studer760ea552014-03-21 13:10:21 +0100569 public void onPanelHidden() {
570 EventLogTags.writeNotificationPanelHidden();
571 }
572
573 @Override
John Spurlocke6a7d932014-03-13 12:29:00 -0400574 public void onNotificationError(int callingUid, int callingPid, String pkg, String tag, int id,
Kenny Guy3a7c4a52014-03-03 18:24:03 +0000575 int uid, int initialPid, String message, int userId) {
Daniel Sandlerd0a2f862010-08-03 15:29:31 -0400576 Slog.d(TAG, "onNotification error pkg=" + pkg + " tag=" + tag + " id=" + id
577 + "; will crashApplication(uid=" + uid + ", pid=" + initialPid + ")");
John Spurlocke6a7d932014-03-13 12:29:00 -0400578 cancelNotification(callingUid, callingPid, pkg, tag, id, 0, 0, false, userId,
579 REASON_DELEGATE_ERROR, null);
Dianne Hackborn9d39d0c2010-06-24 15:57:42 -0700580 long ident = Binder.clearCallingIdentity();
581 try {
582 ActivityManagerNative.getDefault().crashApplication(uid, initialPid, pkg,
583 "Bad notification posted from package " + pkg
584 + ": " + message);
585 } catch (RemoteException e) {
586 }
587 Binder.restoreCallingIdentity(ident);
Joe Onorato005847b2010-06-04 16:08:02 -0400588 }
John Spurlocke677d712014-02-13 12:52:19 -0500589
590 @Override
Christoph Studer92b389d2014-04-01 18:44:40 +0200591 public void onNotificationVisibilityChanged(
592 String[] newlyVisibleKeys, String[] noLongerVisibleKeys) {
593 // Using ';' as separator since eventlogs uses ',' to separate
594 // args.
595 EventLogTags.writeNotificationVisibilityChanged(
596 TextUtils.join(";", newlyVisibleKeys),
597 TextUtils.join(";", noLongerVisibleKeys));
Christoph Studerffeb0c32014-05-07 22:23:56 +0200598 synchronized (mNotificationList) {
599 for (String key : newlyVisibleKeys) {
600 NotificationRecord r = mNotificationsByKey.get(key);
601 if (r == null) continue;
602 r.stats.onVisibilityChanged(true);
603 }
604 // Note that we might receive this event after notifications
605 // have already left the system, e.g. after dismissing from the
606 // shade. Hence not finding notifications in
607 // mNotificationsByKey is not an exceptional condition.
608 for (String key : noLongerVisibleKeys) {
609 NotificationRecord r = mNotificationsByKey.get(key);
610 if (r == null) continue;
611 r.stats.onVisibilityChanged(false);
612 }
613 }
Christoph Studer92b389d2014-04-01 18:44:40 +0200614 }
Chris Wren78403d72014-07-28 10:23:24 +0100615
616 @Override
617 public void onNotificationExpansionChanged(String key,
618 boolean userAction, boolean expanded) {
619 EventLogTags.writeNotificationExpansion(key, userAction ? 1 : 0, expanded ? 1 : 0);
620 synchronized (mNotificationList) {
621 NotificationRecord r = mNotificationsByKey.get(key);
622 if (r != null) {
623 r.stats.onExpansionChanged(userAction, expanded);
624 }
625 }
626 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800627 };
628
629 private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
630 @Override
631 public void onReceive(Context context, Intent intent) {
632 String action = intent.getAction();
633
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800634 boolean queryRestart = false;
Chris Wrenae9bb572013-05-15 14:50:28 -0400635 boolean queryRemove = false;
Daniel Sandler26ece572012-06-01 15:38:46 -0400636 boolean packageChanged = false;
John Spurlock79f78922013-05-16 09:10:05 -0400637 boolean cancelNotifications = true;
Chris Wrenf9536642014-04-17 10:01:54 -0400638
Chris Wren3da73022013-05-10 14:41:21 -0400639 if (action.equals(Intent.ACTION_PACKAGE_ADDED)
Chris Wrenae9bb572013-05-15 14:50:28 -0400640 || (queryRemove=action.equals(Intent.ACTION_PACKAGE_REMOVED))
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800641 || action.equals(Intent.ACTION_PACKAGE_RESTARTED)
Daniel Sandler26ece572012-06-01 15:38:46 -0400642 || (packageChanged=action.equals(Intent.ACTION_PACKAGE_CHANGED))
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800643 || (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART))
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800644 || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800645 String pkgList[] = null;
Chris Wrenae9bb572013-05-15 14:50:28 -0400646 boolean queryReplace = queryRemove &&
647 intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
John Spurlocke77bb362014-04-26 10:24:59 -0400648 if (DBG) Slog.i(TAG, "action=" + action + " queryReplace=" + queryReplace);
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800649 if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800650 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800651 } else if (queryRestart) {
652 pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800653 } else {
654 Uri uri = intent.getData();
655 if (uri == null) {
656 return;
657 }
658 String pkgName = uri.getSchemeSpecificPart();
659 if (pkgName == null) {
660 return;
661 }
Daniel Sandler26ece572012-06-01 15:38:46 -0400662 if (packageChanged) {
663 // We cancel notifications for packages which have just been disabled
Christopher Tate06e5fed2013-10-09 14:39:15 -0700664 try {
Adam Lesinski182f73f2013-12-05 16:48:06 -0800665 final int enabled = getContext().getPackageManager()
Christopher Tate06e5fed2013-10-09 14:39:15 -0700666 .getApplicationEnabledSetting(pkgName);
667 if (enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED
668 || enabled == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
669 cancelNotifications = false;
670 }
671 } catch (IllegalArgumentException e) {
672 // Package doesn't exist; probably racing with uninstall.
673 // cancelNotifications is already true, so nothing to do here.
674 if (DBG) {
675 Slog.i(TAG, "Exception trying to look up app enabled setting", e);
676 }
Daniel Sandler26ece572012-06-01 15:38:46 -0400677 }
678 }
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800679 pkgList = new String[]{pkgName};
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800680 }
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700681
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800682 if (pkgList != null && (pkgList.length > 0)) {
683 for (String pkgName : pkgList) {
John Spurlock79f78922013-05-16 09:10:05 -0400684 if (cancelNotifications) {
John Spurlocke6a7d932014-03-13 12:29:00 -0400685 cancelAllNotificationsInt(MY_UID, MY_PID, pkgName, 0, 0, !queryRestart,
686 UserHandle.USER_ALL, REASON_PACKAGE_CHANGED, null);
John Spurlock79f78922013-05-16 09:10:05 -0400687 }
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800688 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800689 }
John Spurlockb408e8e2014-04-23 21:12:45 -0400690 mListeners.onPackagesChanged(queryReplace, pkgList);
John Spurlock7340fc82014-04-24 18:50:12 -0400691 mConditionProviders.onPackagesChanged(queryReplace, pkgList);
Mike Lockwood63b5ad92011-08-30 09:55:30 -0400692 } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
693 // Keep track of screen on/off state, but do not turn off the notification light
694 // until user passes through the lock screen or views the notification.
695 mScreenOn = true;
696 } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
697 mScreenOn = false;
Daniel Sandlere96ffb12010-03-11 13:38:06 -0500698 } else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) {
John Spurlock5d2eeb12014-01-16 10:46:36 -0500699 mInCall = TelephonyManager.EXTRA_STATE_OFFHOOK
700 .equals(intent.getStringExtra(TelephonyManager.EXTRA_STATE));
Daniel Sandlere96ffb12010-03-11 13:38:06 -0500701 updateNotificationPulse();
Dianne Hackborn80a4af22012-08-27 19:18:31 -0700702 } else if (action.equals(Intent.ACTION_USER_STOPPED)) {
703 int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
704 if (userHandle >= 0) {
John Spurlocke6a7d932014-03-13 12:29:00 -0400705 cancelAllNotificationsInt(MY_UID, MY_PID, null, 0, 0, true, userHandle,
706 REASON_USER_STOPPED, null);
Dianne Hackborn80a4af22012-08-27 19:18:31 -0700707 }
Mike Lockwood63b5ad92011-08-30 09:55:30 -0400708 } else if (action.equals(Intent.ACTION_USER_PRESENT)) {
709 // turn off LED when user passes through lock screen
710 mNotificationLight.turnOff();
John Spurlockcb566aa2014-08-03 22:58:28 -0400711 mStatusBar.notificationLightOff();
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400712 } else if (action.equals(Intent.ACTION_USER_SWITCHED)) {
713 // reload per-user settings
714 mSettingsObserver.update(null);
John Spurlockb408e8e2014-04-23 21:12:45 -0400715 mUserProfiles.updateCache(context);
Kenny Guy3a7c4a52014-03-03 18:24:03 +0000716 } else if (action.equals(Intent.ACTION_USER_ADDED)) {
John Spurlockb408e8e2014-04-23 21:12:45 -0400717 mUserProfiles.updateCache(context);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800718 }
719 }
720 };
721
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700722 class SettingsObserver extends ContentObserver {
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400723 private final Uri NOTIFICATION_LIGHT_PULSE_URI
724 = Settings.System.getUriFor(Settings.System.NOTIFICATION_LIGHT_PULSE);
725
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700726 SettingsObserver(Handler handler) {
727 super(handler);
728 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800729
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700730 void observe() {
Adam Lesinski182f73f2013-12-05 16:48:06 -0800731 ContentResolver resolver = getContext().getContentResolver();
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400732 resolver.registerContentObserver(NOTIFICATION_LIGHT_PULSE_URI,
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700733 false, this, UserHandle.USER_ALL);
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400734 update(null);
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700735 }
736
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400737 @Override public void onChange(boolean selfChange, Uri uri) {
738 update(uri);
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700739 }
740
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400741 public void update(Uri uri) {
Adam Lesinski182f73f2013-12-05 16:48:06 -0800742 ContentResolver resolver = getContext().getContentResolver();
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400743 if (uri == null || NOTIFICATION_LIGHT_PULSE_URI.equals(uri)) {
744 boolean pulseEnabled = Settings.System.getInt(resolver,
745 Settings.System.NOTIFICATION_LIGHT_PULSE, 0) != 0;
746 if (mNotificationPulseEnabled != pulseEnabled) {
747 mNotificationPulseEnabled = pulseEnabled;
748 updateNotificationPulse();
749 }
750 }
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700751 }
752 }
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500753
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400754 private SettingsObserver mSettingsObserver;
John Spurlock056c5192014-04-20 21:52:01 -0400755 private ZenModeHelper mZenModeHelper;
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400756
John Spurlockcad57682014-07-26 17:09:56 -0400757 private final Runnable mBuzzBeepBlinked = new Runnable() {
758 @Override
759 public void run() {
760 mStatusBar.buzzBeepBlinked();
761 }
762 };
763
Daniel Sandleredbb3802012-11-13 20:49:47 -0800764 static long[] getLongArray(Resources r, int resid, int maxlen, long[] def) {
765 int[] ar = r.getIntArray(resid);
766 if (ar == null) {
767 return def;
768 }
769 final int len = ar.length > maxlen ? maxlen : ar.length;
770 long[] out = new long[len];
771 for (int i=0; i<len; i++) {
772 out[i] = ar[i];
773 }
774 return out;
775 }
776
Jeff Brownb880d882014-02-10 19:47:07 -0800777 public NotificationManagerService(Context context) {
778 super(context);
779 }
780
Adam Lesinski182f73f2013-12-05 16:48:06 -0800781 @Override
782 public void onStart() {
Chris Wren54bbef42014-07-09 18:37:56 -0400783 Resources resources = getContext().getResources();
784
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800785 mAm = ActivityManagerNative.getDefault();
Adam Lesinski182f73f2013-12-05 16:48:06 -0800786 mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
787 mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
San Mehat3ee13172010-02-04 20:54:43 -0800788
Adam Lesinski182f73f2013-12-05 16:48:06 -0800789 mHandler = new WorkerHandler();
Chris Wrenf9536642014-04-17 10:01:54 -0400790 mRankingThread.start();
Chris Wren54bbef42014-07-09 18:37:56 -0400791 String[] extractorNames;
792 try {
793 extractorNames = resources.getStringArray(R.array.config_notificationSignalExtractors);
794 } catch (Resources.NotFoundException e) {
795 extractorNames = new String[0];
796 }
797 mRankingHelper = new RankingHelper(getContext(),
798 new RankingWorkerHandler(mRankingThread.getLooper()),
799 extractorNames);
John Spurlock056c5192014-04-20 21:52:01 -0400800 mZenModeHelper = new ZenModeHelper(getContext(), mHandler);
John Spurlock1c923a32014-04-27 16:42:29 -0400801 mZenModeHelper.addCallback(new ZenModeHelper.Callback() {
John Spurlock056c5192014-04-20 21:52:01 -0400802 @Override
803 public void onConfigChanged() {
804 savePolicyFile();
805 }
John Spurlockd8afe3c2014-08-01 14:04:07 -0400806
807 @Override
808 void onZenModeChanged() {
809 synchronized(mNotificationList) {
Christoph Studer85a384b2014-08-27 20:16:15 +0200810 updateInterruptionFilterLocked();
John Spurlockd8afe3c2014-08-01 14:04:07 -0400811 }
812 }
John Spurlock056c5192014-04-20 21:52:01 -0400813 });
814 final File systemDir = new File(Environment.getDataDirectory(), "system");
815 mPolicyFile = new AtomicFile(new File(systemDir, "notification_policy.xml"));
Christoph Studer1c3f81f2014-04-16 15:05:56 +0200816 mUsageStats = new NotificationUsageStats(getContext());
Daniel Sandler4a900ac2013-01-30 14:04:10 -0500817
818 importOldBlockDb();
Daniel Sandler0da673f2012-04-11 12:33:16 -0400819
John Spurlock7340fc82014-04-24 18:50:12 -0400820 mListeners = new NotificationListeners();
821 mConditionProviders = new ConditionProviders(getContext(),
John Spurlocke77bb362014-04-26 10:24:59 -0400822 mHandler, mUserProfiles, mZenModeHelper);
Adam Lesinski182f73f2013-12-05 16:48:06 -0800823 mStatusBar = getLocalService(StatusBarManagerInternal.class);
824 mStatusBar.setNotificationDelegate(mNotificationDelegate);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800825
Adam Lesinski182f73f2013-12-05 16:48:06 -0800826 final LightsManager lights = getLocalService(LightsManager.class);
827 mNotificationLight = lights.getLight(LightsManager.LIGHT_ID_NOTIFICATIONS);
828 mAttentionLight = lights.getLight(LightsManager.LIGHT_ID_ATTENTION);
Mike Lockwood3cb67a32009-11-27 14:25:58 -0500829
Mike Lockwood670f9322010-01-20 12:13:36 -0500830 mDefaultNotificationColor = resources.getColor(
Scott Greenwald9a05b312013-06-28 00:37:54 -0400831 R.color.config_defaultNotificationColor);
Mike Lockwood670f9322010-01-20 12:13:36 -0500832 mDefaultNotificationLedOn = resources.getInteger(
Scott Greenwald9a05b312013-06-28 00:37:54 -0400833 R.integer.config_defaultNotificationLedOn);
Mike Lockwood670f9322010-01-20 12:13:36 -0500834 mDefaultNotificationLedOff = resources.getInteger(
Scott Greenwald9a05b312013-06-28 00:37:54 -0400835 R.integer.config_defaultNotificationLedOff);
Mike Lockwood670f9322010-01-20 12:13:36 -0500836
Daniel Sandleredbb3802012-11-13 20:49:47 -0800837 mDefaultVibrationPattern = getLongArray(resources,
Scott Greenwald9a05b312013-06-28 00:37:54 -0400838 R.array.config_defaultNotificationVibePattern,
Daniel Sandleredbb3802012-11-13 20:49:47 -0800839 VIBRATE_PATTERN_MAXLEN,
840 DEFAULT_VIBRATE_PATTERN);
841
842 mFallbackVibrationPattern = getLongArray(resources,
Scott Greenwald9a05b312013-06-28 00:37:54 -0400843 R.array.config_notificationFallbackVibePattern,
Daniel Sandleredbb3802012-11-13 20:49:47 -0800844 VIBRATE_PATTERN_MAXLEN,
845 DEFAULT_VIBRATE_PATTERN);
846
Chris Wren5116a822014-06-04 15:59:50 -0400847 mUseAttentionLight = resources.getBoolean(R.bool.config_useAttentionLight);
848
Joe Onorato39f5b6a2009-07-23 12:29:19 -0400849 // Don't start allowing notifications until the setup wizard has run once.
850 // After that, including subsequent boots, init with notifications turned on.
851 // This works on the first boot because the setup wizard will toggle this
852 // flag at least once and we'll go back to 0 after that.
Adam Lesinski182f73f2013-12-05 16:48:06 -0800853 if (0 == Settings.Global.getInt(getContext().getContentResolver(),
Jeff Brownbf6f6f92012-09-25 15:03:20 -0700854 Settings.Global.DEVICE_PROVISIONED, 0)) {
John Spurlockd8afe3c2014-08-01 14:04:07 -0400855 mDisableNotificationEffects = true;
Joe Onorato39f5b6a2009-07-23 12:29:19 -0400856 }
John Spurlock056c5192014-04-20 21:52:01 -0400857 mZenModeHelper.updateZenMode();
Joe Onorato39f5b6a2009-07-23 12:29:19 -0400858
John Spurlockb408e8e2014-04-23 21:12:45 -0400859 mUserProfiles.updateCache(getContext());
Kenny Guya263e4e2014-03-03 18:24:03 +0000860
Mike Lockwood35e16bf2010-11-30 19:53:36 -0500861 // register for various Intents
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800862 IntentFilter filter = new IntentFilter();
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500863 filter.addAction(Intent.ACTION_SCREEN_ON);
864 filter.addAction(Intent.ACTION_SCREEN_OFF);
Daniel Sandlere96ffb12010-03-11 13:38:06 -0500865 filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
Mike Lockwood63b5ad92011-08-30 09:55:30 -0400866 filter.addAction(Intent.ACTION_USER_PRESENT);
Dianne Hackborn80a4af22012-08-27 19:18:31 -0700867 filter.addAction(Intent.ACTION_USER_STOPPED);
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400868 filter.addAction(Intent.ACTION_USER_SWITCHED);
Kenny Guy3a7c4a52014-03-03 18:24:03 +0000869 filter.addAction(Intent.ACTION_USER_ADDED);
Adam Lesinski182f73f2013-12-05 16:48:06 -0800870 getContext().registerReceiver(mIntentReceiver, filter);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800871 IntentFilter pkgFilter = new IntentFilter();
Chris Wren3da73022013-05-10 14:41:21 -0400872 pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800873 pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
Daniel Sandleraac0eb02011-08-06 22:51:56 -0400874 pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800875 pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
876 pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
877 pkgFilter.addDataScheme("package");
Adam Lesinski182f73f2013-12-05 16:48:06 -0800878 getContext().registerReceiver(mIntentReceiver, pkgFilter);
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800879 IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
Adam Lesinski182f73f2013-12-05 16:48:06 -0800880 getContext().registerReceiver(mIntentReceiver, sdFilter);
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800881
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400882 mSettingsObserver = new SettingsObserver(mHandler);
Scott Greenwald9a05b312013-06-28 00:37:54 -0400883
Griff Hazen9f637d12014-06-10 11:13:51 -0700884 mArchive = new Archive(resources.getInteger(
885 R.integer.config_notificationServiceArchiveSize));
886
Adam Lesinski182f73f2013-12-05 16:48:06 -0800887 publishBinderService(Context.NOTIFICATION_SERVICE, mService);
888 publishLocalService(NotificationManagerInternal.class, mInternalService);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800889 }
890
Daniel Sandler4a900ac2013-01-30 14:04:10 -0500891 /**
892 * Read the old XML-based app block database and import those blockages into the AppOps system.
893 */
894 private void importOldBlockDb() {
John Spurlock056c5192014-04-20 21:52:01 -0400895 loadPolicyFile();
Daniel Sandler4a900ac2013-01-30 14:04:10 -0500896
Adam Lesinski182f73f2013-12-05 16:48:06 -0800897 PackageManager pm = getContext().getPackageManager();
Daniel Sandler4a900ac2013-01-30 14:04:10 -0500898 for (String pkg : mBlockedPackages) {
899 PackageInfo info = null;
900 try {
901 info = pm.getPackageInfo(pkg, 0);
Adam Lesinski182f73f2013-12-05 16:48:06 -0800902 setNotificationsEnabledForPackageImpl(pkg, info.applicationInfo.uid, false);
Daniel Sandler4a900ac2013-01-30 14:04:10 -0500903 } catch (NameNotFoundException e) {
904 // forget you
905 }
906 }
907 mBlockedPackages.clear();
Daniel Sandler4a900ac2013-01-30 14:04:10 -0500908 }
909
Adam Lesinski182f73f2013-12-05 16:48:06 -0800910 @Override
911 public void onBootPhase(int phase) {
912 if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
913 // no beeping until we're basically done booting
914 mSystemReady = true;
Jeff Sharkey098d5802012-04-26 17:30:34 -0700915
Adam Lesinski182f73f2013-12-05 16:48:06 -0800916 // Grab our optional AudioService
917 mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
John Spurlockae641c92014-06-30 18:11:40 -0400918 mZenModeHelper.setAudioManager(mAudioManager);
Adam Lesinskia6db4ab2014-03-24 12:31:45 -0700919 } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
920 // This observer will force an update when observe is called, causing us to
921 // bind to listener services.
922 mSettingsObserver.observe();
John Spurlockb408e8e2014-04-23 21:12:45 -0400923 mListeners.onBootPhaseAppsCanStart();
John Spurlock7340fc82014-04-24 18:50:12 -0400924 mConditionProviders.onBootPhaseAppsCanStart();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800925 }
926 }
927
Adam Lesinski182f73f2013-12-05 16:48:06 -0800928 void setNotificationsEnabledForPackageImpl(String pkg, int uid, boolean enabled) {
929 Slog.v(TAG, (enabled?"en":"dis") + "abling notifications for " + pkg);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800930
Adam Lesinski182f73f2013-12-05 16:48:06 -0800931 mAppOps.setMode(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg,
932 enabled ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800933
Adam Lesinski182f73f2013-12-05 16:48:06 -0800934 // Now, cancel any outstanding notifications that are part of a just-disabled app
935 if (ENABLE_BLOCKED_NOTIFICATIONS && !enabled) {
John Spurlocke6a7d932014-03-13 12:29:00 -0400936 cancelAllNotificationsInt(MY_UID, MY_PID, pkg, 0, 0, true, UserHandle.getUserId(uid),
937 REASON_PACKAGE_BANNED, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800938 }
939 }
940
John Spurlockd8afe3c2014-08-01 14:04:07 -0400941 private void updateListenerHintsLocked() {
Christoph Studer85a384b2014-08-27 20:16:15 +0200942 final int hints = mListenersDisablingEffects.isEmpty() ? 0 : HINT_HOST_DISABLE_EFFECTS;
John Spurlockd8afe3c2014-08-01 14:04:07 -0400943 if (hints == mListenerHints) return;
944 mListenerHints = hints;
945 scheduleListenerHintsChanged(hints);
John Spurlock1fa865f2014-07-21 14:56:39 -0400946 }
947
John Spurlockb4782522014-08-22 14:54:46 -0400948 private void updateEffectsSuppressorLocked() {
949 final ComponentName suppressor = !mListenersDisablingEffects.isEmpty()
950 ? mListenersDisablingEffects.valueAt(0).component : null;
951 if (Objects.equals(suppressor, mEffectsSuppressor)) return;
952 mEffectsSuppressor = suppressor;
953 getContext().sendBroadcast(new Intent(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED)
954 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY));
955 }
956
Christoph Studer85a384b2014-08-27 20:16:15 +0200957 private void updateInterruptionFilterLocked() {
958 int interruptionFilter = mZenModeHelper.getZenModeListenerInterruptionFilter();
959 if (interruptionFilter == mInterruptionFilter) return;
960 mInterruptionFilter = interruptionFilter;
961 scheduleInterruptionFilterChanged(interruptionFilter);
962 }
963
Adam Lesinski182f73f2013-12-05 16:48:06 -0800964 private final IBinder mService = new INotificationManager.Stub() {
965 // Toasts
966 // ============================================================================
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800967
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800968 @Override
Adam Lesinski182f73f2013-12-05 16:48:06 -0800969 public void enqueueToast(String pkg, ITransientNotification callback, int duration)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800970 {
Adam Lesinski182f73f2013-12-05 16:48:06 -0800971 if (DBG) {
972 Slog.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback
973 + " duration=" + duration);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800974 }
Adam Lesinski182f73f2013-12-05 16:48:06 -0800975
976 if (pkg == null || callback == null) {
977 Slog.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback);
978 return ;
979 }
980
John Spurlock7340fc82014-04-24 18:50:12 -0400981 final boolean isSystemToast = isCallerSystem() || ("android".equals(pkg));
Adam Lesinski182f73f2013-12-05 16:48:06 -0800982
983 if (ENABLE_BLOCKED_TOASTS && !noteNotificationOp(pkg, Binder.getCallingUid())) {
984 if (!isSystemToast) {
985 Slog.e(TAG, "Suppressing toast from package " + pkg + " by user request.");
986 return;
987 }
988 }
989
990 synchronized (mToastQueue) {
991 int callingPid = Binder.getCallingPid();
992 long callingId = Binder.clearCallingIdentity();
993 try {
994 ToastRecord record;
995 int index = indexOfToastLocked(pkg, callback);
996 // If it's already in the queue, we update it in place, we don't
997 // move it to the end of the queue.
998 if (index >= 0) {
999 record = mToastQueue.get(index);
1000 record.update(duration);
1001 } else {
1002 // Limit the number of toasts that any given package except the android
1003 // package can enqueue. Prevents DOS attacks and deals with leaks.
1004 if (!isSystemToast) {
1005 int count = 0;
1006 final int N = mToastQueue.size();
1007 for (int i=0; i<N; i++) {
1008 final ToastRecord r = mToastQueue.get(i);
1009 if (r.pkg.equals(pkg)) {
1010 count++;
1011 if (count >= MAX_PACKAGE_NOTIFICATIONS) {
1012 Slog.e(TAG, "Package has already posted " + count
1013 + " toasts. Not showing more. Package=" + pkg);
1014 return;
1015 }
1016 }
1017 }
1018 }
1019
1020 record = new ToastRecord(callingPid, pkg, callback, duration);
1021 mToastQueue.add(record);
1022 index = mToastQueue.size() - 1;
1023 keepProcessAliveLocked(callingPid);
1024 }
1025 // If it's at index 0, it's the current toast. It doesn't matter if it's
1026 // new or just been updated. Call back and tell it to show itself.
1027 // If the callback fails, this will remove it from the list, so don't
1028 // assume that it's valid after this.
1029 if (index == 0) {
1030 showNextToastLocked();
1031 }
1032 } finally {
1033 Binder.restoreCallingIdentity(callingId);
1034 }
1035 }
1036 }
1037
1038 @Override
1039 public void cancelToast(String pkg, ITransientNotification callback) {
1040 Slog.i(TAG, "cancelToast pkg=" + pkg + " callback=" + callback);
1041
1042 if (pkg == null || callback == null) {
1043 Slog.e(TAG, "Not cancelling notification. pkg=" + pkg + " callback=" + callback);
1044 return ;
1045 }
1046
1047 synchronized (mToastQueue) {
1048 long callingId = Binder.clearCallingIdentity();
1049 try {
1050 int index = indexOfToastLocked(pkg, callback);
1051 if (index >= 0) {
1052 cancelToastLocked(index);
1053 } else {
1054 Slog.w(TAG, "Toast already cancelled. pkg=" + pkg
1055 + " callback=" + callback);
1056 }
1057 } finally {
1058 Binder.restoreCallingIdentity(callingId);
1059 }
1060 }
1061 }
1062
1063 @Override
Christoph Studer8fd7f1e2014-04-11 17:35:05 -04001064 public void enqueueNotificationWithTag(String pkg, String opPkg, String tag, int id,
Adam Lesinski182f73f2013-12-05 16:48:06 -08001065 Notification notification, int[] idOut, int userId) throws RemoteException {
Christoph Studer8fd7f1e2014-04-11 17:35:05 -04001066 enqueueNotificationInternal(pkg, opPkg, Binder.getCallingUid(),
Adam Lesinski182f73f2013-12-05 16:48:06 -08001067 Binder.getCallingPid(), tag, id, notification, idOut, userId);
1068 }
1069
1070 @Override
1071 public void cancelNotificationWithTag(String pkg, String tag, int id, int userId) {
John Spurlock7340fc82014-04-24 18:50:12 -04001072 checkCallerIsSystemOrSameApp(pkg);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001073 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1074 Binder.getCallingUid(), userId, true, false, "cancelNotificationWithTag", pkg);
1075 // Don't allow client applications to cancel foreground service notis.
John Spurlocke6a7d932014-03-13 12:29:00 -04001076 cancelNotification(Binder.getCallingUid(), Binder.getCallingPid(), pkg, tag, id, 0,
Adam Lesinski182f73f2013-12-05 16:48:06 -08001077 Binder.getCallingUid() == Process.SYSTEM_UID
John Spurlocke6a7d932014-03-13 12:29:00 -04001078 ? 0 : Notification.FLAG_FOREGROUND_SERVICE, false, userId, REASON_NOMAN_CANCEL,
1079 null);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001080 }
1081
1082 @Override
1083 public void cancelAllNotifications(String pkg, int userId) {
John Spurlock7340fc82014-04-24 18:50:12 -04001084 checkCallerIsSystemOrSameApp(pkg);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001085
1086 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1087 Binder.getCallingUid(), userId, true, false, "cancelAllNotifications", pkg);
1088
1089 // Calling from user space, don't allow the canceling of actively
1090 // running foreground services.
John Spurlocke6a7d932014-03-13 12:29:00 -04001091 cancelAllNotificationsInt(Binder.getCallingUid(), Binder.getCallingPid(),
1092 pkg, 0, Notification.FLAG_FOREGROUND_SERVICE, true, userId,
1093 REASON_NOMAN_CANCEL_ALL, null);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001094 }
1095
1096 @Override
1097 public void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) {
John Spurlock7340fc82014-04-24 18:50:12 -04001098 checkCallerIsSystem();
Adam Lesinski182f73f2013-12-05 16:48:06 -08001099
1100 setNotificationsEnabledForPackageImpl(pkg, uid, enabled);
1101 }
1102
1103 /**
1104 * Use this when you just want to know if notifications are OK for this package.
1105 */
1106 @Override
1107 public boolean areNotificationsEnabledForPackage(String pkg, int uid) {
John Spurlock7340fc82014-04-24 18:50:12 -04001108 checkCallerIsSystem();
Adam Lesinski182f73f2013-12-05 16:48:06 -08001109 return (mAppOps.checkOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
1110 == AppOpsManager.MODE_ALLOWED);
1111 }
1112
Chris Wren54bbef42014-07-09 18:37:56 -04001113 @Override
1114 public void setPackagePriority(String pkg, int uid, int priority) {
1115 checkCallerIsSystem();
1116 mRankingHelper.setPackagePriority(pkg, uid, priority);
1117 savePolicyFile();
1118 }
1119
1120 @Override
1121 public int getPackagePriority(String pkg, int uid) {
1122 checkCallerIsSystem();
1123 return mRankingHelper.getPackagePriority(pkg, uid);
1124 }
1125
Adam Lesinski182f73f2013-12-05 16:48:06 -08001126 /**
1127 * System-only API for getting a list of current (i.e. not cleared) notifications.
1128 *
1129 * Requires ACCESS_NOTIFICATIONS which is signature|system.
Chris Wrenf9536642014-04-17 10:01:54 -04001130 * @returns A list of all the notifications, in natural order.
Adam Lesinski182f73f2013-12-05 16:48:06 -08001131 */
1132 @Override
1133 public StatusBarNotification[] getActiveNotifications(String callingPkg) {
1134 // enforce() will ensure the calling uid has the correct permission
1135 getContext().enforceCallingOrSelfPermission(
1136 android.Manifest.permission.ACCESS_NOTIFICATIONS,
1137 "NotificationManagerService.getActiveNotifications");
1138
1139 StatusBarNotification[] tmp = null;
1140 int uid = Binder.getCallingUid();
1141
1142 // noteOp will check to make sure the callingPkg matches the uid
1143 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
1144 == AppOpsManager.MODE_ALLOWED) {
1145 synchronized (mNotificationList) {
1146 tmp = new StatusBarNotification[mNotificationList.size()];
1147 final int N = mNotificationList.size();
1148 for (int i=0; i<N; i++) {
1149 tmp[i] = mNotificationList.get(i).sbn;
1150 }
1151 }
1152 }
1153 return tmp;
1154 }
1155
1156 /**
1157 * System-only API for getting a list of recent (cleared, no longer shown) notifications.
1158 *
1159 * Requires ACCESS_NOTIFICATIONS which is signature|system.
1160 */
1161 @Override
1162 public StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count) {
1163 // enforce() will ensure the calling uid has the correct permission
1164 getContext().enforceCallingOrSelfPermission(
1165 android.Manifest.permission.ACCESS_NOTIFICATIONS,
1166 "NotificationManagerService.getHistoricalNotifications");
1167
1168 StatusBarNotification[] tmp = null;
1169 int uid = Binder.getCallingUid();
1170
1171 // noteOp will check to make sure the callingPkg matches the uid
1172 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
1173 == AppOpsManager.MODE_ALLOWED) {
1174 synchronized (mArchive) {
1175 tmp = mArchive.getArray(count);
1176 }
1177 }
1178 return tmp;
1179 }
1180
1181 /**
1182 * Register a listener binder directly with the notification manager.
1183 *
1184 * Only works with system callers. Apps should extend
1185 * {@link android.service.notification.NotificationListenerService}.
1186 */
1187 @Override
1188 public void registerListener(final INotificationListener listener,
1189 final ComponentName component, final int userid) {
Christoph Studer3e144d32014-05-22 16:48:40 +02001190 enforceSystemOrSystemUI("INotificationManager.registerListener");
John Spurlock7340fc82014-04-24 18:50:12 -04001191 mListeners.registerService(listener, component, userid);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001192 }
1193
1194 /**
1195 * Remove a listener binder directly
1196 */
1197 @Override
1198 public void unregisterListener(INotificationListener listener, int userid) {
John Spurlock7340fc82014-04-24 18:50:12 -04001199 mListeners.unregisterService(listener, userid);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001200 }
1201
1202 /**
1203 * Allow an INotificationListener to simulate a "clear all" operation.
1204 *
1205 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onClearAllNotifications}
1206 *
1207 * @param token The binder for the listener, to check that the caller is allowed
1208 */
1209 @Override
John Spurlocka4294292014-03-24 18:02:32 -04001210 public void cancelNotificationsFromListener(INotificationListener token, String[] keys) {
John Spurlocke6a7d932014-03-13 12:29:00 -04001211 final int callingUid = Binder.getCallingUid();
1212 final int callingPid = Binder.getCallingPid();
Adam Lesinski182f73f2013-12-05 16:48:06 -08001213 long identity = Binder.clearCallingIdentity();
1214 try {
Adam Lesinskie8240262014-03-26 16:01:00 -07001215 synchronized (mNotificationList) {
John Spurlock7340fc82014-04-24 18:50:12 -04001216 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
John Spurlocka4294292014-03-24 18:02:32 -04001217 if (keys != null) {
1218 final int N = keys.length;
1219 for (int i = 0; i < N; i++) {
1220 NotificationRecord r = mNotificationsByKey.get(keys[i]);
Kenny Guya263e4e2014-03-03 18:24:03 +00001221 final int userId = r.sbn.getUserId();
1222 if (userId != info.userid && userId != UserHandle.USER_ALL &&
John Spurlockb408e8e2014-04-23 21:12:45 -04001223 !mUserProfiles.isCurrentProfile(userId)) {
Kenny Guya263e4e2014-03-03 18:24:03 +00001224 throw new SecurityException("Disallowed call from listener: "
John Spurlock7340fc82014-04-24 18:50:12 -04001225 + info.service);
Kenny Guya263e4e2014-03-03 18:24:03 +00001226 }
John Spurlocka4294292014-03-24 18:02:32 -04001227 if (r != null) {
1228 cancelNotificationFromListenerLocked(info, callingUid, callingPid,
Kenny Guya263e4e2014-03-03 18:24:03 +00001229 r.sbn.getPackageName(), r.sbn.getTag(), r.sbn.getId(),
1230 userId);
John Spurlocka4294292014-03-24 18:02:32 -04001231 }
1232 }
1233 } else {
1234 cancelAllLocked(callingUid, callingPid, info.userid,
Kenny Guya263e4e2014-03-03 18:24:03 +00001235 REASON_LISTENER_CANCEL_ALL, info, info.supportsProfiles());
John Spurlocka4294292014-03-24 18:02:32 -04001236 }
Adam Lesinskie8240262014-03-26 16:01:00 -07001237 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001238 } finally {
1239 Binder.restoreCallingIdentity(identity);
1240 }
1241 }
1242
John Spurlock7340fc82014-04-24 18:50:12 -04001243 private void cancelNotificationFromListenerLocked(ManagedServiceInfo info,
Kenny Guya263e4e2014-03-03 18:24:03 +00001244 int callingUid, int callingPid, String pkg, String tag, int id, int userId) {
John Spurlocka4294292014-03-24 18:02:32 -04001245 cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
1246 Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
1247 true,
Kenny Guya263e4e2014-03-03 18:24:03 +00001248 userId, REASON_LISTENER_CANCEL, info);
John Spurlocka4294292014-03-24 18:02:32 -04001249 }
1250
Adam Lesinski182f73f2013-12-05 16:48:06 -08001251 /**
1252 * Allow an INotificationListener to simulate clearing (dismissing) a single notification.
1253 *
1254 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onNotificationClear}
1255 *
1256 * @param token The binder for the listener, to check that the caller is allowed
1257 */
1258 @Override
1259 public void cancelNotificationFromListener(INotificationListener token, String pkg,
1260 String tag, int id) {
John Spurlocke6a7d932014-03-13 12:29:00 -04001261 final int callingUid = Binder.getCallingUid();
1262 final int callingPid = Binder.getCallingPid();
Adam Lesinski182f73f2013-12-05 16:48:06 -08001263 long identity = Binder.clearCallingIdentity();
1264 try {
Adam Lesinskie8240262014-03-26 16:01:00 -07001265 synchronized (mNotificationList) {
John Spurlock7340fc82014-04-24 18:50:12 -04001266 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
Kenny Guya263e4e2014-03-03 18:24:03 +00001267 if (info.supportsProfiles()) {
1268 Log.e(TAG, "Ignoring deprecated cancelNotification(pkg, tag, id) "
1269 + "from " + info.component
1270 + " use cancelNotification(key) instead.");
1271 } else {
1272 cancelNotificationFromListenerLocked(info, callingUid, callingPid,
1273 pkg, tag, id, info.userid);
1274 }
Adam Lesinskie8240262014-03-26 16:01:00 -07001275 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001276 } finally {
1277 Binder.restoreCallingIdentity(identity);
1278 }
1279 }
1280
1281 /**
1282 * Allow an INotificationListener to request the list of outstanding notifications seen by
1283 * the current user. Useful when starting up, after which point the listener callbacks
1284 * should be used.
1285 *
1286 * @param token The binder for the listener, to check that the caller is allowed
Dan Sandlerea75fdd2014-08-12 12:29:19 -04001287 * @param keys An array of notification keys to fetch, or null to fetch everything
Chris Wrenf9536642014-04-17 10:01:54 -04001288 * @returns The return value will contain the notifications specified in keys, in that
1289 * order, or if keys is null, all the notifications, in natural order.
Adam Lesinski182f73f2013-12-05 16:48:06 -08001290 */
1291 @Override
Christoph Studercee44ba2014-05-20 18:36:43 +02001292 public ParceledListSlice<StatusBarNotification> getActiveNotificationsFromListener(
Dan Sandlerea75fdd2014-08-12 12:29:19 -04001293 INotificationListener token, String[] keys) {
Adam Lesinski182f73f2013-12-05 16:48:06 -08001294 synchronized (mNotificationList) {
John Spurlock7340fc82014-04-24 18:50:12 -04001295 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
John Spurlocka4294292014-03-24 18:02:32 -04001296 final ArrayList<StatusBarNotification> list
1297 = new ArrayList<StatusBarNotification>();
Dan Sandlerea75fdd2014-08-12 12:29:19 -04001298 final boolean getKeys = keys != null;
1299 final int N = getKeys ? keys.length : mNotificationList.size();
1300 list.ensureCapacity(N);
Christoph Studercee44ba2014-05-20 18:36:43 +02001301 for (int i=0; i<N; i++) {
Dan Sandlerea75fdd2014-08-12 12:29:19 -04001302 final NotificationRecord r = getKeys
1303 ? mNotificationsByKey.get(keys[i])
1304 : mNotificationList.get(i);
1305 if (r != null) {
1306 StatusBarNotification sbn = r.sbn;
1307 if (isVisibleToListener(sbn, info)) {
1308 list.add(sbn);
1309 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001310 }
1311 }
Christoph Studercee44ba2014-05-20 18:36:43 +02001312 return new ParceledListSlice<StatusBarNotification>(list);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001313 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001314 }
1315
1316 @Override
John Spurlockd8afe3c2014-08-01 14:04:07 -04001317 public void requestHintsFromListener(INotificationListener token, int hints) {
1318 final long identity = Binder.clearCallingIdentity();
1319 try {
1320 synchronized (mNotificationList) {
1321 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1322 final boolean disableEffects = (hints & HINT_HOST_DISABLE_EFFECTS) != 0;
1323 if (disableEffects) {
1324 mListenersDisablingEffects.add(info);
1325 } else {
1326 mListenersDisablingEffects.remove(info);
1327 }
John Spurlockd8afe3c2014-08-01 14:04:07 -04001328 updateListenerHintsLocked();
John Spurlockb4782522014-08-22 14:54:46 -04001329 updateEffectsSuppressorLocked();
John Spurlock1fa865f2014-07-21 14:56:39 -04001330 }
John Spurlockd8afe3c2014-08-01 14:04:07 -04001331 } finally {
1332 Binder.restoreCallingIdentity(identity);
John Spurlock1fa865f2014-07-21 14:56:39 -04001333 }
1334 }
1335
1336 @Override
John Spurlockd8afe3c2014-08-01 14:04:07 -04001337 public int getHintsFromListener(INotificationListener token) {
John Spurlock1fa865f2014-07-21 14:56:39 -04001338 synchronized (mNotificationList) {
John Spurlockd8afe3c2014-08-01 14:04:07 -04001339 return mListenerHints;
John Spurlock1fa865f2014-07-21 14:56:39 -04001340 }
1341 }
1342
1343 @Override
Christoph Studer85a384b2014-08-27 20:16:15 +02001344 public void requestInterruptionFilterFromListener(INotificationListener token,
1345 int interruptionFilter) throws RemoteException {
1346 final long identity = Binder.clearCallingIdentity();
1347 try {
1348 synchronized (mNotificationList) {
1349 mListeners.checkServiceTokenLocked(token);
1350 mZenModeHelper.requestFromListener(interruptionFilter);
1351 updateInterruptionFilterLocked();
1352 }
1353 } finally {
1354 Binder.restoreCallingIdentity(identity);
1355 }
1356 }
1357
1358 @Override
1359 public int getInterruptionFilterFromListener(INotificationListener token)
1360 throws RemoteException {
1361 synchronized (mNotificationLight) {
1362 return mInterruptionFilter;
1363 }
1364 }
1365
1366 @Override
John Spurlock056c5192014-04-20 21:52:01 -04001367 public ZenModeConfig getZenModeConfig() {
John Spurlock856edeb2014-06-01 20:36:47 -04001368 enforceSystemOrSystemUI("INotificationManager.getZenModeConfig");
John Spurlock056c5192014-04-20 21:52:01 -04001369 return mZenModeHelper.getConfig();
1370 }
1371
1372 @Override
1373 public boolean setZenModeConfig(ZenModeConfig config) {
John Spurlock7340fc82014-04-24 18:50:12 -04001374 checkCallerIsSystem();
John Spurlock056c5192014-04-20 21:52:01 -04001375 return mZenModeHelper.setConfig(config);
1376 }
1377
1378 @Override
John Spurlocke77bb362014-04-26 10:24:59 -04001379 public void notifyConditions(String pkg, IConditionProvider provider,
1380 Condition[] conditions) {
1381 final ManagedServiceInfo info = mConditionProviders.checkServiceToken(provider);
1382 checkCallerIsSystemOrSameApp(pkg);
1383 final long identity = Binder.clearCallingIdentity();
1384 try {
1385 mConditionProviders.notifyConditions(pkg, info, conditions);
1386 } finally {
1387 Binder.restoreCallingIdentity(identity);
1388 }
1389 }
1390
1391 @Override
John Spurlock3b98b3f2014-05-01 09:08:48 -04001392 public void requestZenModeConditions(IConditionListener callback, int relevance) {
John Spurlocke77bb362014-04-26 10:24:59 -04001393 enforceSystemOrSystemUI("INotificationManager.requestZenModeConditions");
John Spurlock3b98b3f2014-05-01 09:08:48 -04001394 mConditionProviders.requestZenModeConditions(callback, relevance);
John Spurlocke77bb362014-04-26 10:24:59 -04001395 }
1396
1397 @Override
John Spurlock4db0d982014-08-13 09:19:03 -04001398 public void setZenModeCondition(Condition condition) {
John Spurlocke77bb362014-04-26 10:24:59 -04001399 enforceSystemOrSystemUI("INotificationManager.setZenModeCondition");
John Spurlockaf8d6c42014-05-07 17:49:08 -04001400 final long identity = Binder.clearCallingIdentity();
1401 try {
John Spurlock4db0d982014-08-13 09:19:03 -04001402 mConditionProviders.setZenModeCondition(condition, "binderCall");
John Spurlockaf8d6c42014-05-07 17:49:08 -04001403 } finally {
1404 Binder.restoreCallingIdentity(identity);
1405 }
John Spurlocke77bb362014-04-26 10:24:59 -04001406 }
1407
John Spurlock3b98b3f2014-05-01 09:08:48 -04001408 @Override
1409 public void setAutomaticZenModeConditions(Uri[] conditionIds) {
1410 enforceSystemOrSystemUI("INotificationManager.setAutomaticZenModeConditions");
1411 mConditionProviders.setAutomaticZenModeConditions(conditionIds);
1412 }
1413
1414 @Override
1415 public Condition[] getAutomaticZenModeConditions() {
1416 enforceSystemOrSystemUI("INotificationManager.getAutomaticZenModeConditions");
1417 return mConditionProviders.getAutomaticZenModeConditions();
1418 }
1419
John Spurlocke77bb362014-04-26 10:24:59 -04001420 private void enforceSystemOrSystemUI(String message) {
1421 if (isCallerSystem()) return;
1422 getContext().enforceCallingPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
1423 message);
John Spurlock7340fc82014-04-24 18:50:12 -04001424 }
1425
1426 @Override
Adam Lesinski182f73f2013-12-05 16:48:06 -08001427 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1428 if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
1429 != PackageManager.PERMISSION_GRANTED) {
1430 pw.println("Permission Denial: can't dump NotificationManager from from pid="
1431 + Binder.getCallingPid()
1432 + ", uid=" + Binder.getCallingUid());
1433 return;
1434 }
1435
John Spurlock25e2d242014-06-27 13:58:23 -04001436 dumpImpl(pw, DumpFilter.parseFromArguments(args));
Adam Lesinski182f73f2013-12-05 16:48:06 -08001437 }
John Spurlockb4782522014-08-22 14:54:46 -04001438
1439 @Override
1440 public ComponentName getEffectsSuppressor() {
1441 enforceSystemOrSystemUI("INotificationManager.getEffectsSuppressor");
1442 return mEffectsSuppressor;
1443 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001444 };
1445
Chris Wrenf9536642014-04-17 10:01:54 -04001446 private String[] getActiveNotificationKeys(INotificationListener token) {
1447 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1448 final ArrayList<String> keys = new ArrayList<String>();
1449 if (info.isEnabledForCurrentProfiles()) {
1450 synchronized (mNotificationList) {
1451 final int N = mNotificationList.size();
1452 for (int i = 0; i < N; i++) {
1453 final StatusBarNotification sbn = mNotificationList.get(i).sbn;
1454 if (info.enabledAndUserMatches(sbn.getUserId())) {
1455 keys.add(sbn.getKey());
1456 }
John Spurlocka4294292014-03-24 18:02:32 -04001457 }
1458 }
John Spurlocka4294292014-03-24 18:02:32 -04001459 }
Chris Wrenf9536642014-04-17 10:01:54 -04001460 return keys.toArray(new String[keys.size()]);
John Spurlocka4294292014-03-24 18:02:32 -04001461 }
1462
John Spurlockd8afe3c2014-08-01 14:04:07 -04001463 private boolean disableNotificationEffects() {
1464 return mDisableNotificationEffects || (mListenerHints & HINT_HOST_DISABLE_EFFECTS) != 0;
John Spurlock1fa865f2014-07-21 14:56:39 -04001465 }
1466
John Spurlock25e2d242014-06-27 13:58:23 -04001467 void dumpImpl(PrintWriter pw, DumpFilter filter) {
1468 pw.print("Current Notification Manager state");
1469 if (filter != null) {
John Spurlock50806fc2014-07-15 10:22:02 -04001470 pw.print(" (filtered to "); pw.print(filter); pw.print(")");
John Spurlock25e2d242014-06-27 13:58:23 -04001471 }
1472 pw.println(':');
Adam Lesinski182f73f2013-12-05 16:48:06 -08001473 int N;
John Spurlock50806fc2014-07-15 10:22:02 -04001474 final boolean zenOnly = filter != null && filter.zen;
Adam Lesinski182f73f2013-12-05 16:48:06 -08001475
John Spurlock50806fc2014-07-15 10:22:02 -04001476 if (!zenOnly) {
1477 synchronized (mToastQueue) {
1478 N = mToastQueue.size();
1479 if (N > 0) {
1480 pw.println(" Toast Queue:");
1481 for (int i=0; i<N; i++) {
1482 mToastQueue.get(i).dump(pw, " ", filter);
1483 }
1484 pw.println(" ");
Adam Lesinski182f73f2013-12-05 16:48:06 -08001485 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001486 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001487 }
1488
1489 synchronized (mNotificationList) {
John Spurlock50806fc2014-07-15 10:22:02 -04001490 if (!zenOnly) {
1491 N = mNotificationList.size();
John Spurlock25e2d242014-06-27 13:58:23 -04001492 if (N > 0) {
John Spurlock50806fc2014-07-15 10:22:02 -04001493 pw.println(" Notification List:");
John Spurlock25e2d242014-06-27 13:58:23 -04001494 for (int i=0; i<N; i++) {
John Spurlock50806fc2014-07-15 10:22:02 -04001495 final NotificationRecord nr = mNotificationList.get(i);
1496 if (filter != null && !filter.matches(nr.sbn)) continue;
1497 nr.dump(pw, " ", getContext());
John Spurlock25e2d242014-06-27 13:58:23 -04001498 }
1499 pw.println(" ");
Adam Lesinski182f73f2013-12-05 16:48:06 -08001500 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001501
John Spurlock50806fc2014-07-15 10:22:02 -04001502 if (filter == null) {
1503 N = mLights.size();
1504 if (N > 0) {
1505 pw.println(" Lights List:");
1506 for (int i=0; i<N; i++) {
1507 pw.println(" " + mLights.get(i));
1508 }
1509 pw.println(" ");
1510 }
John Spurlockcb566aa2014-08-03 22:58:28 -04001511 pw.println(" mUseAttentionLight=" + mUseAttentionLight);
1512 pw.println(" mNotificationPulseEnabled=" + mNotificationPulseEnabled);
John Spurlock50806fc2014-07-15 10:22:02 -04001513 pw.println(" mSoundNotification=" + mSoundNotification);
1514 pw.println(" mVibrateNotification=" + mVibrateNotification);
John Spurlockd8afe3c2014-08-01 14:04:07 -04001515 pw.println(" mDisableNotificationEffects=" + mDisableNotificationEffects);
John Spurlock50806fc2014-07-15 10:22:02 -04001516 pw.println(" mSystemReady=" + mSystemReady);
1517 }
1518 pw.println(" mArchive=" + mArchive.toString());
1519 Iterator<StatusBarNotification> iter = mArchive.descendingIterator();
1520 int i=0;
1521 while (iter.hasNext()) {
1522 final StatusBarNotification sbn = iter.next();
1523 if (filter != null && !filter.matches(sbn)) continue;
1524 pw.println(" " + sbn);
1525 if (++i >= 5) {
1526 if (iter.hasNext()) pw.println(" ...");
1527 break;
1528 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001529 }
1530 }
1531
John Spurlock50806fc2014-07-15 10:22:02 -04001532 if (!zenOnly) {
1533 pw.println("\n Usage Stats:");
1534 mUsageStats.dump(pw, " ", filter);
1535 }
Christoph Studer546bec82014-03-14 12:17:12 +01001536
John Spurlock50806fc2014-07-15 10:22:02 -04001537 if (filter == null || zenOnly) {
John Spurlock25e2d242014-06-27 13:58:23 -04001538 pw.println("\n Zen Mode:");
1539 mZenModeHelper.dump(pw, " ");
John Spurlock6ae82a72014-07-16 16:23:01 -04001540
1541 pw.println("\n Zen Log:");
1542 ZenLog.dump(pw, " ");
John Spurlock25e2d242014-06-27 13:58:23 -04001543 }
John Spurlocke77bb362014-04-26 10:24:59 -04001544
John Spurlock50806fc2014-07-15 10:22:02 -04001545 if (!zenOnly) {
1546 pw.println("\n Ranking Config:");
1547 mRankingHelper.dump(pw, " ", filter);
Chris Wren54bbef42014-07-09 18:37:56 -04001548
John Spurlock50806fc2014-07-15 10:22:02 -04001549 pw.println("\n Notification listeners:");
1550 mListeners.dump(pw, filter);
John Spurlockd8afe3c2014-08-01 14:04:07 -04001551 pw.print(" mListenerHints: "); pw.println(mListenerHints);
1552 pw.print(" mListenersDisablingEffects: (");
1553 N = mListenersDisablingEffects.size();
John Spurlock1fa865f2014-07-21 14:56:39 -04001554 for (int i = 0; i < N; i++) {
John Spurlockd8afe3c2014-08-01 14:04:07 -04001555 final ManagedServiceInfo listener = mListenersDisablingEffects.valueAt(i);
John Spurlock1fa865f2014-07-21 14:56:39 -04001556 if (i > 0) pw.print(',');
1557 pw.print(listener.component);
1558 }
1559 pw.println(')');
John Spurlock50806fc2014-07-15 10:22:02 -04001560 }
John Spurlocke77bb362014-04-26 10:24:59 -04001561
1562 pw.println("\n Condition providers:");
John Spurlock25e2d242014-06-27 13:58:23 -04001563 mConditionProviders.dump(pw, filter);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001564 }
1565 }
1566
Adam Lesinski182f73f2013-12-05 16:48:06 -08001567 /**
1568 * The private API only accessible to the system process.
1569 */
1570 private final NotificationManagerInternal mInternalService = new NotificationManagerInternal() {
1571 @Override
Christoph Studer8fd7f1e2014-04-11 17:35:05 -04001572 public void enqueueNotification(String pkg, String opPkg, int callingUid, int callingPid,
Adam Lesinski182f73f2013-12-05 16:48:06 -08001573 String tag, int id, Notification notification, int[] idReceived, int userId) {
Christoph Studer8fd7f1e2014-04-11 17:35:05 -04001574 enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, notification,
Adam Lesinski182f73f2013-12-05 16:48:06 -08001575 idReceived, userId);
1576 }
1577 };
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001578
Christoph Studer8fd7f1e2014-04-11 17:35:05 -04001579 void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,
Scott Greenwald9b05c612013-06-25 23:44:05 -04001580 final int callingPid, final String tag, final int id, final Notification notification,
Adam Lesinski182f73f2013-12-05 16:48:06 -08001581 int[] idOut, int incomingUserId) {
Daniel Sandler0da673f2012-04-11 12:33:16 -04001582 if (DBG) {
Adam Lesinski182f73f2013-12-05 16:48:06 -08001583 Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id
1584 + " notification=" + notification);
Daniel Sandler0da673f2012-04-11 12:33:16 -04001585 }
John Spurlock7340fc82014-04-24 18:50:12 -04001586 checkCallerIsSystemOrSameApp(pkg);
1587 final boolean isSystemNotification = isUidSystem(callingUid) || ("android".equals(pkg));
Justin Koh38156c52014-06-04 13:57:49 -07001588 final boolean isNotificationFromListener = mListeners.isListenerPackage(pkg);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001589
Scott Greenwald9b05c612013-06-25 23:44:05 -04001590 final int userId = ActivityManager.handleIncomingUser(callingPid,
1591 callingUid, incomingUserId, true, false, "enqueueNotification", pkg);
Jeff Sharkey65c4a2b2012-09-25 17:22:27 -07001592 final UserHandle user = new UserHandle(userId);
Dianne Hackborn41203752012-08-31 14:05:51 -07001593
Joe Onoratobd73d012010-06-04 11:44:54 -07001594 // Limit the number of notifications that any given package except the android
Justin Koh38156c52014-06-04 13:57:49 -07001595 // package or a registered listener can enqueue. Prevents DOS attacks and deals with leaks.
1596 if (!isSystemNotification && !isNotificationFromListener) {
Joe Onoratobd73d012010-06-04 11:44:54 -07001597 synchronized (mNotificationList) {
1598 int count = 0;
1599 final int N = mNotificationList.size();
1600 for (int i=0; i<N; i++) {
1601 final NotificationRecord r = mNotificationList.get(i);
Daniel Sandler4f91efd2013-04-25 16:38:41 -04001602 if (r.sbn.getPackageName().equals(pkg) && r.sbn.getUserId() == userId) {
Joe Onoratobd73d012010-06-04 11:44:54 -07001603 count++;
1604 if (count >= MAX_PACKAGE_NOTIFICATIONS) {
1605 Slog.e(TAG, "Package has already posted " + count
1606 + " notifications. Not showing more. package=" + pkg);
1607 return;
1608 }
1609 }
1610 }
1611 }
1612 }
1613
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001614 // This conditional is a dirty hack to limit the logging done on
1615 // behalf of the download manager without affecting other apps.
1616 if (!pkg.equals("com.android.providers.downloads")
1617 || Log.isLoggable("DownloadManager", Log.VERBOSE)) {
John Spurlocke6a7d932014-03-13 12:29:00 -04001618 EventLogTags.writeNotificationEnqueue(callingUid, callingPid,
1619 pkg, id, tag, userId, notification.toString());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001620 }
1621
1622 if (pkg == null || notification == null) {
1623 throw new IllegalArgumentException("null not allowed: pkg=" + pkg
1624 + " id=" + id + " notification=" + notification);
1625 }
1626 if (notification.icon != 0) {
Christoph Studer4600f9b2014-07-22 22:44:43 +02001627 if (!notification.isValid()) {
1628 throw new IllegalArgumentException("Invalid notification (): pkg=" + pkg
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001629 + " id=" + id + " notification=" + notification);
1630 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001631 }
1632
Scott Greenwald9b05c612013-06-25 23:44:05 -04001633 mHandler.post(new Runnable() {
1634 @Override
1635 public void run() {
Daniel Sandler0da673f2012-04-11 12:33:16 -04001636
Scott Greenwald9b05c612013-06-25 23:44:05 -04001637 synchronized (mNotificationList) {
Christoph Studercd4adf82014-08-19 17:50:49 +02001638
1639 // === Scoring ===
1640
1641 // 0. Sanitize inputs
1642 notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN,
1643 Notification.PRIORITY_MAX);
1644 // Migrate notification flags to scores
1645 if (0 != (notification.flags & Notification.FLAG_HIGH_PRIORITY)) {
1646 if (notification.priority < Notification.PRIORITY_MAX) {
1647 notification.priority = Notification.PRIORITY_MAX;
1648 }
1649 } else if (SCORE_ONGOING_HIGHER &&
1650 0 != (notification.flags & Notification.FLAG_ONGOING_EVENT)) {
1651 if (notification.priority < Notification.PRIORITY_HIGH) {
1652 notification.priority = Notification.PRIORITY_HIGH;
1653 }
1654 }
1655
1656 // 1. initial score: buckets of 10, around the app [-20..20]
1657 final int score = notification.priority * NOTIFICATION_PRIORITY_MULTIPLIER;
1658
1659 // 2. extract ranking signals from the notification data
1660 final StatusBarNotification n = new StatusBarNotification(
1661 pkg, opPkg, id, tag, callingUid, callingPid, score, notification,
1662 user);
1663 NotificationRecord r = new NotificationRecord(n, score);
1664 NotificationRecord old = mNotificationsByKey.get(n.getKey());
1665 if (old != null) {
1666 // Retain ranking information from previous record
1667 r.copyRankingInformation(old);
1668 }
1669 mRankingHelper.extractSignals(r);
1670
1671 // 3. Apply local rules
1672
1673 // blocked apps
1674 if (ENABLE_BLOCKED_NOTIFICATIONS && !noteNotificationOp(pkg, callingUid)) {
1675 if (!isSystemNotification) {
1676 r.score = JUNK_SCORE;
1677 Slog.e(TAG, "Suppressing notification from package " + pkg
1678 + " by user request.");
1679 }
1680 }
1681
1682 if (r.score < SCORE_DISPLAY_THRESHOLD) {
1683 // Notification will be blocked because the score is too low.
1684 return;
1685 }
1686
Christoph Studerc44caa92014-08-22 19:16:00 +02001687 // Clear out group children of the old notification if the update causes the
1688 // group summary to go away. This happens when the old notification was a
1689 // summary and the new one isn't, or when the old notification was a summary
1690 // and its group key changed.
1691 if (old != null && old.getNotification().isGroupSummary() &&
1692 (!notification.isGroupSummary() ||
1693 !old.getGroupKey().equals(r.getGroupKey()))) {
1694 cancelGroupChildrenLocked(old, callingUid, callingPid, null);
1695 }
1696
Christoph Studer71f18fd2014-05-20 17:02:04 +02001697 int index = indexOfNotificationLocked(n.getKey());
Scott Greenwald9b05c612013-06-25 23:44:05 -04001698 if (index < 0) {
1699 mNotificationList.add(r);
Christoph Studer546bec82014-03-14 12:17:12 +01001700 mUsageStats.registerPostedByApp(r);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001701 } else {
Christoph Studerd89188d2014-04-03 00:02:39 +02001702 old = mNotificationList.get(index);
1703 mNotificationList.set(index, r);
Christoph Studer061dee22014-05-09 12:28:55 +02001704 mUsageStats.registerUpdatedByApp(r, old);
Scott Greenwald9b05c612013-06-25 23:44:05 -04001705 // Make sure we don't lose the foreground service state.
Christoph Studer71f18fd2014-05-20 17:02:04 +02001706 notification.flags |=
Chris Wrena3446562014-06-03 18:11:47 -04001707 old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;
Chris Wrena3446562014-06-03 18:11:47 -04001708 r.isUpdate = true;
John Spurlocka4294292014-03-24 18:02:32 -04001709 }
Christoph Studer1cd5add2014-07-03 00:23:05 +02001710
John Spurlocka4294292014-03-24 18:02:32 -04001711 mNotificationsByKey.put(n.getKey(), r);
Scott Greenwald9b05c612013-06-25 23:44:05 -04001712
Christoph Studer1cd5add2014-07-03 00:23:05 +02001713 // Ensure if this is a foreground service that the proper additional
1714 // flags are set.
1715 if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
1716 notification.flags |= Notification.FLAG_ONGOING_EVENT
1717 | Notification.FLAG_NO_CLEAR;
1718 }
1719
Chris Wren52eba542014-06-02 15:40:32 -04001720 applyZenModeLocked(r);
Christoph Studercd4adf82014-08-19 17:50:49 +02001721 mRankingHelper.sort(mNotificationList);
Chris Wrenf9536642014-04-17 10:01:54 -04001722
Scott Greenwald9b05c612013-06-25 23:44:05 -04001723 if (notification.icon != 0) {
Christoph Studercef37cf2014-07-25 14:18:17 +02001724 StatusBarNotification oldSbn = (old != null) ? old.sbn : null;
1725 mListeners.notifyPostedLocked(n, oldSbn);
Scott Greenwald9b05c612013-06-25 23:44:05 -04001726 } else {
1727 Slog.e(TAG, "Not posting notification with icon==0: " + notification);
Christoph Studer71f18fd2014-05-20 17:02:04 +02001728 if (old != null && !old.isCanceled) {
Chris Wrena3446562014-06-03 18:11:47 -04001729 mListeners.notifyRemovedLocked(n);
Scott Greenwald9b05c612013-06-25 23:44:05 -04001730 }
1731 // ATTENTION: in a future release we will bail out here
Adam Lesinski182f73f2013-12-05 16:48:06 -08001732 // so that we do not play sounds, show lights, etc. for invalid
1733 // notifications
Scott Greenwald9b05c612013-06-25 23:44:05 -04001734 Slog.e(TAG, "WARNING: In a future release this will crash the app: "
1735 + n.getPackageName());
1736 }
1737
Chris Wrena3446562014-06-03 18:11:47 -04001738 buzzBeepBlinkLocked(r);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001739 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001740 }
Scott Greenwald9b05c612013-06-25 23:44:05 -04001741 });
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001742
1743 idOut[0] = id;
1744 }
1745
Chris Wrena3446562014-06-03 18:11:47 -04001746 private void buzzBeepBlinkLocked(NotificationRecord record) {
John Spurlockcad57682014-07-26 17:09:56 -04001747 boolean buzzBeepBlinked = false;
Chris Wrena3446562014-06-03 18:11:47 -04001748 final Notification notification = record.sbn.getNotification();
1749
1750 // Should this notification make noise, vibe, or use the LED?
1751 final boolean canInterrupt = (record.score >= SCORE_INTERRUPTION_THRESHOLD) &&
1752 !record.isIntercepted();
1753 if (DBG || record.isIntercepted())
1754 Slog.v(TAG,
1755 "pkg=" + record.sbn.getPackageName() + " canInterrupt=" + canInterrupt +
1756 " intercept=" + record.isIntercepted()
1757 );
1758
1759 final int currentUser;
1760 final long token = Binder.clearCallingIdentity();
1761 try {
1762 currentUser = ActivityManager.getCurrentUser();
1763 } finally {
1764 Binder.restoreCallingIdentity(token);
1765 }
1766
1767 // If we're not supposed to beep, vibrate, etc. then don't.
John Spurlockd8afe3c2014-08-01 14:04:07 -04001768 if (!disableNotificationEffects()
Chris Wrena3446562014-06-03 18:11:47 -04001769 && (!(record.isUpdate
1770 && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 ))
1771 && (record.getUserId() == UserHandle.USER_ALL ||
1772 record.getUserId() == currentUser ||
1773 mUserProfiles.isCurrentProfile(record.getUserId()))
1774 && canInterrupt
1775 && mSystemReady
1776 && mAudioManager != null) {
1777 if (DBG) Slog.v(TAG, "Interrupting!");
1778
1779 sendAccessibilityEvent(notification, record.sbn.getPackageName());
1780
1781 // sound
1782
1783 // should we use the default notification sound? (indicated either by
1784 // DEFAULT_SOUND or because notification.sound is pointing at
1785 // Settings.System.NOTIFICATION_SOUND)
1786 final boolean useDefaultSound =
1787 (notification.defaults & Notification.DEFAULT_SOUND) != 0 ||
1788 Settings.System.DEFAULT_NOTIFICATION_URI
1789 .equals(notification.sound);
1790
1791 Uri soundUri = null;
1792 boolean hasValidSound = false;
1793
1794 if (useDefaultSound) {
1795 soundUri = Settings.System.DEFAULT_NOTIFICATION_URI;
1796
1797 // check to see if the default notification sound is silent
1798 ContentResolver resolver = getContext().getContentResolver();
1799 hasValidSound = Settings.System.getString(resolver,
1800 Settings.System.NOTIFICATION_SOUND) != null;
1801 } else if (notification.sound != null) {
1802 soundUri = notification.sound;
1803 hasValidSound = (soundUri != null);
1804 }
1805
1806 if (hasValidSound) {
1807 boolean looping =
1808 (notification.flags & Notification.FLAG_INSISTENT) != 0;
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -07001809 AudioAttributes audioAttributes;
1810 if (notification.audioAttributes != null) {
1811 audioAttributes = notification.audioAttributes;
Chris Wrena3446562014-06-03 18:11:47 -04001812 } else {
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -07001813 audioAttributes = Notification.AUDIO_ATTRIBUTES_DEFAULT;
Chris Wrena3446562014-06-03 18:11:47 -04001814 }
1815 mSoundNotification = record;
1816 // do not play notifications if stream volume is 0 (typically because
1817 // ringer mode is silent) or if there is a user of exclusive audio focus
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -07001818 if ((mAudioManager.getStreamVolume(
1819 AudioAttributes.toLegacyStreamType(audioAttributes)) != 0)
1820 && !mAudioManager.isAudioFocusExclusive()) {
Chris Wrena3446562014-06-03 18:11:47 -04001821 final long identity = Binder.clearCallingIdentity();
1822 try {
1823 final IRingtonePlayer player =
1824 mAudioManager.getRingtonePlayer();
1825 if (player != null) {
1826 if (DBG) Slog.v(TAG, "Playing sound " + soundUri
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -07001827 + " with attributes " + audioAttributes);
Chris Wrena3446562014-06-03 18:11:47 -04001828 player.playAsync(soundUri, record.sbn.getUser(), looping,
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -07001829 audioAttributes);
John Spurlockcad57682014-07-26 17:09:56 -04001830 buzzBeepBlinked = true;
Chris Wrena3446562014-06-03 18:11:47 -04001831 }
1832 } catch (RemoteException e) {
1833 } finally {
1834 Binder.restoreCallingIdentity(identity);
1835 }
1836 }
1837 }
1838
1839 // vibrate
1840 // Does the notification want to specify its own vibration?
1841 final boolean hasCustomVibrate = notification.vibrate != null;
1842
1843 // new in 4.2: if there was supposed to be a sound and we're in vibrate
1844 // mode, and no other vibration is specified, we fall back to vibration
1845 final boolean convertSoundToVibration =
1846 !hasCustomVibrate
1847 && hasValidSound
1848 && (mAudioManager.getRingerMode()
1849 == AudioManager.RINGER_MODE_VIBRATE);
1850
1851 // The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback.
1852 final boolean useDefaultVibrate =
1853 (notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
1854
1855 if ((useDefaultVibrate || convertSoundToVibration || hasCustomVibrate)
1856 && !(mAudioManager.getRingerMode()
1857 == AudioManager.RINGER_MODE_SILENT)) {
1858 mVibrateNotification = record;
1859
1860 if (useDefaultVibrate || convertSoundToVibration) {
1861 // Escalate privileges so we can use the vibrator even if the
1862 // notifying app does not have the VIBRATE permission.
1863 long identity = Binder.clearCallingIdentity();
1864 try {
1865 mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
1866 useDefaultVibrate ? mDefaultVibrationPattern
1867 : mFallbackVibrationPattern,
1868 ((notification.flags & Notification.FLAG_INSISTENT) != 0)
John Spurlock7b414672014-07-18 13:02:39 -04001869 ? 0: -1, audioAttributesForNotification(notification));
John Spurlockcad57682014-07-26 17:09:56 -04001870 buzzBeepBlinked = true;
Chris Wrena3446562014-06-03 18:11:47 -04001871 } finally {
1872 Binder.restoreCallingIdentity(identity);
1873 }
1874 } else if (notification.vibrate.length > 1) {
1875 // If you want your own vibration pattern, you need the VIBRATE
1876 // permission
1877 mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
1878 notification.vibrate,
1879 ((notification.flags & Notification.FLAG_INSISTENT) != 0)
John Spurlock7b414672014-07-18 13:02:39 -04001880 ? 0: -1, audioAttributesForNotification(notification));
John Spurlockcad57682014-07-26 17:09:56 -04001881 buzzBeepBlinked = true;
Chris Wrena3446562014-06-03 18:11:47 -04001882 }
1883 }
1884 }
1885
1886 // light
1887 // release the light
1888 boolean wasShowLights = mLights.remove(record.getKey());
1889 if (mLedNotification != null && record.getKey().equals(mLedNotification.getKey())) {
1890 mLedNotification = null;
1891 }
1892 if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0 && canInterrupt) {
1893 mLights.add(record.getKey());
1894 updateLightsLocked();
Chris Wren5116a822014-06-04 15:59:50 -04001895 if (mUseAttentionLight) {
1896 mAttentionLight.pulse();
1897 }
John Spurlockcad57682014-07-26 17:09:56 -04001898 buzzBeepBlinked = true;
Chris Wrena3446562014-06-03 18:11:47 -04001899 } else if (wasShowLights) {
1900 updateLightsLocked();
1901 }
John Spurlockcad57682014-07-26 17:09:56 -04001902 if (buzzBeepBlinked) {
1903 mHandler.post(mBuzzBeepBlinked);
1904 }
Chris Wrena3446562014-06-03 18:11:47 -04001905 }
1906
John Spurlock7b414672014-07-18 13:02:39 -04001907 private static AudioAttributes audioAttributesForNotification(Notification n) {
John Spurlockbfa5dc42014-07-28 23:30:45 -04001908 if (n.audioAttributes != null
1909 && !Notification.AUDIO_ATTRIBUTES_DEFAULT.equals(n.audioAttributes)) {
1910 return n.audioAttributes;
1911 }
1912 return new AudioAttributes.Builder()
1913 .setLegacyStreamType(n.audioStreamType)
1914 .setUsage(AudioAttributes.usageForLegacyStreamType(n.audioStreamType))
1915 .build();
John Spurlock7b414672014-07-18 13:02:39 -04001916 }
1917
Adam Lesinski182f73f2013-12-05 16:48:06 -08001918 void showNextToastLocked() {
1919 ToastRecord record = mToastQueue.get(0);
1920 while (record != null) {
1921 if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback);
1922 try {
1923 record.callback.show();
1924 scheduleTimeoutLocked(record);
1925 return;
1926 } catch (RemoteException e) {
1927 Slog.w(TAG, "Object died trying to show notification " + record.callback
1928 + " in package " + record.pkg);
1929 // remove it from the list and let the process die
1930 int index = mToastQueue.indexOf(record);
1931 if (index >= 0) {
1932 mToastQueue.remove(index);
1933 }
1934 keepProcessAliveLocked(record.pid);
1935 if (mToastQueue.size() > 0) {
1936 record = mToastQueue.get(0);
1937 } else {
1938 record = null;
1939 }
1940 }
1941 }
1942 }
1943
1944 void cancelToastLocked(int index) {
1945 ToastRecord record = mToastQueue.get(index);
1946 try {
1947 record.callback.hide();
1948 } catch (RemoteException e) {
1949 Slog.w(TAG, "Object died trying to hide notification " + record.callback
1950 + " in package " + record.pkg);
1951 // don't worry about this, we're about to remove it from
1952 // the list anyway
1953 }
1954 mToastQueue.remove(index);
1955 keepProcessAliveLocked(record.pid);
1956 if (mToastQueue.size() > 0) {
1957 // Show the next one. If the callback fails, this will remove
1958 // it from the list, so don't assume that the list hasn't changed
1959 // after this point.
1960 showNextToastLocked();
1961 }
1962 }
1963
1964 private void scheduleTimeoutLocked(ToastRecord r)
1965 {
1966 mHandler.removeCallbacksAndMessages(r);
1967 Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r);
1968 long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY;
1969 mHandler.sendMessageDelayed(m, delay);
1970 }
1971
1972 private void handleTimeout(ToastRecord record)
1973 {
1974 if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback);
1975 synchronized (mToastQueue) {
1976 int index = indexOfToastLocked(record.pkg, record.callback);
1977 if (index >= 0) {
1978 cancelToastLocked(index);
1979 }
1980 }
1981 }
1982
1983 // lock on mToastQueue
1984 int indexOfToastLocked(String pkg, ITransientNotification callback)
1985 {
1986 IBinder cbak = callback.asBinder();
1987 ArrayList<ToastRecord> list = mToastQueue;
1988 int len = list.size();
1989 for (int i=0; i<len; i++) {
1990 ToastRecord r = list.get(i);
1991 if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) {
1992 return i;
1993 }
1994 }
1995 return -1;
1996 }
1997
1998 // lock on mToastQueue
1999 void keepProcessAliveLocked(int pid)
2000 {
2001 int toastCount = 0; // toasts from this pid
2002 ArrayList<ToastRecord> list = mToastQueue;
2003 int N = list.size();
2004 for (int i=0; i<N; i++) {
2005 ToastRecord r = list.get(i);
2006 if (r.pid == pid) {
2007 toastCount++;
2008 }
2009 }
2010 try {
2011 mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0);
2012 } catch (RemoteException e) {
2013 // Shouldn't happen.
2014 }
2015 }
2016
Chris Wrenf9536642014-04-17 10:01:54 -04002017 private void handleRankingReconsideration(Message message) {
Chris Wren470c1ac2014-05-21 15:28:10 -04002018 if (!(message.obj instanceof RankingReconsideration)) return;
2019 RankingReconsideration recon = (RankingReconsideration) message.obj;
2020 recon.run();
Chris Wren333a61c2014-05-28 16:40:57 -04002021 boolean changed;
Chris Wren470c1ac2014-05-21 15:28:10 -04002022 synchronized (mNotificationList) {
2023 final NotificationRecord record = mNotificationsByKey.get(recon.getKey());
2024 if (record == null) {
2025 return;
Chris Wrenf9536642014-04-17 10:01:54 -04002026 }
Chris Wren333a61c2014-05-28 16:40:57 -04002027 int indexBefore = findNotificationRecordIndexLocked(record);
2028 boolean interceptBefore = record.isIntercepted();
Chris Wren470c1ac2014-05-21 15:28:10 -04002029 recon.applyChangesLocked(record);
Chris Wren333a61c2014-05-28 16:40:57 -04002030 applyZenModeLocked(record);
Chris Wren54bbef42014-07-09 18:37:56 -04002031 mRankingHelper.sort(mNotificationList);
Chris Wren333a61c2014-05-28 16:40:57 -04002032 int indexAfter = findNotificationRecordIndexLocked(record);
2033 boolean interceptAfter = record.isIntercepted();
2034 changed = indexBefore != indexAfter || interceptBefore != interceptAfter;
Chris Wrena3446562014-06-03 18:11:47 -04002035 if (interceptBefore && !interceptAfter) {
2036 buzzBeepBlinkLocked(record);
2037 }
Chris Wrenf9536642014-04-17 10:01:54 -04002038 }
Chris Wren333a61c2014-05-28 16:40:57 -04002039 if (changed) {
Chris Wren470c1ac2014-05-21 15:28:10 -04002040 scheduleSendRankingUpdate();
2041 }
2042 }
2043
Chris Wren54bbef42014-07-09 18:37:56 -04002044 private void handleRankingConfigChange() {
2045 synchronized (mNotificationList) {
2046 final int N = mNotificationList.size();
2047 ArrayList<String> orderBefore = new ArrayList<String>(N);
2048 for (int i = 0; i < N; i++) {
2049 final NotificationRecord r = mNotificationList.get(i);
2050 orderBefore.add(r.getKey());
2051 mRankingHelper.extractSignals(r);
2052 }
2053 mRankingHelper.sort(mNotificationList);
2054 for (int i = 0; i < N; i++) {
2055 if (!orderBefore.get(i).equals(mNotificationList.get(i).getKey())) {
2056 scheduleSendRankingUpdate();
2057 return;
2058 }
2059 }
2060 }
2061 }
2062
Christoph Studerd5092bc2014-07-03 17:47:58 +02002063 // let zen mode evaluate this record
Chris Wren333a61c2014-05-28 16:40:57 -04002064 private void applyZenModeLocked(NotificationRecord record) {
Christoph Studerd5092bc2014-07-03 17:47:58 +02002065 record.setIntercepted(mZenModeHelper.shouldIntercept(record));
Chris Wren333a61c2014-05-28 16:40:57 -04002066 }
2067
Chris Wren470c1ac2014-05-21 15:28:10 -04002068 // lock on mNotificationList
2069 private int findNotificationRecordIndexLocked(NotificationRecord target) {
Chris Wren54bbef42014-07-09 18:37:56 -04002070 return mRankingHelper.indexOf(mNotificationList, target);
Chris Wrenf9536642014-04-17 10:01:54 -04002071 }
2072
2073 private void scheduleSendRankingUpdate() {
2074 mHandler.removeMessages(MESSAGE_SEND_RANKING_UPDATE);
2075 Message m = Message.obtain(mHandler, MESSAGE_SEND_RANKING_UPDATE);
2076 mHandler.sendMessage(m);
2077 }
2078
2079 private void handleSendRankingUpdate() {
2080 synchronized (mNotificationList) {
Chris Wren333a61c2014-05-28 16:40:57 -04002081 mListeners.notifyRankingUpdateLocked();
Chris Wrenf9536642014-04-17 10:01:54 -04002082 }
2083 }
2084
John Spurlockd8afe3c2014-08-01 14:04:07 -04002085 private void scheduleListenerHintsChanged(int state) {
2086 mHandler.removeMessages(MESSAGE_LISTENER_HINTS_CHANGED);
2087 mHandler.obtainMessage(MESSAGE_LISTENER_HINTS_CHANGED, state, 0).sendToTarget();
John Spurlock1fa865f2014-07-21 14:56:39 -04002088 }
2089
Christoph Studer85a384b2014-08-27 20:16:15 +02002090 private void scheduleInterruptionFilterChanged(int listenerInterruptionFilter) {
2091 mHandler.removeMessages(MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED);
2092 mHandler.obtainMessage(
2093 MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED,
2094 listenerInterruptionFilter,
2095 0).sendToTarget();
2096 }
2097
John Spurlockd8afe3c2014-08-01 14:04:07 -04002098 private void handleListenerHintsChanged(int hints) {
John Spurlock1fa865f2014-07-21 14:56:39 -04002099 synchronized (mNotificationList) {
John Spurlockd8afe3c2014-08-01 14:04:07 -04002100 mListeners.notifyListenerHintsChangedLocked(hints);
John Spurlock1fa865f2014-07-21 14:56:39 -04002101 }
2102 }
2103
Christoph Studer85a384b2014-08-27 20:16:15 +02002104 private void handleListenerInterruptionFilterChanged(int interruptionFilter) {
2105 synchronized (mNotificationList) {
2106 mListeners.notifyInterruptionFilterChanged(interruptionFilter);
2107 }
2108 }
2109
Adam Lesinski182f73f2013-12-05 16:48:06 -08002110 private final class WorkerHandler extends Handler
2111 {
2112 @Override
2113 public void handleMessage(Message msg)
2114 {
2115 switch (msg.what)
2116 {
2117 case MESSAGE_TIMEOUT:
2118 handleTimeout((ToastRecord)msg.obj);
2119 break;
John Spurlock056c5192014-04-20 21:52:01 -04002120 case MESSAGE_SAVE_POLICY_FILE:
2121 handleSavePolicyFile();
2122 break;
Chris Wrenf9536642014-04-17 10:01:54 -04002123 case MESSAGE_SEND_RANKING_UPDATE:
2124 handleSendRankingUpdate();
2125 break;
John Spurlockd8afe3c2014-08-01 14:04:07 -04002126 case MESSAGE_LISTENER_HINTS_CHANGED:
2127 handleListenerHintsChanged(msg.arg1);
John Spurlock1fa865f2014-07-21 14:56:39 -04002128 break;
Christoph Studer85a384b2014-08-27 20:16:15 +02002129 case MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED:
2130 handleListenerInterruptionFilterChanged(msg.arg1);
2131 break;
Chris Wrenf9536642014-04-17 10:01:54 -04002132 }
2133 }
2134
2135 }
2136
2137 private final class RankingWorkerHandler extends Handler
2138 {
2139 public RankingWorkerHandler(Looper looper) {
2140 super(looper);
2141 }
2142
2143 @Override
2144 public void handleMessage(Message msg) {
2145 switch (msg.what) {
2146 case MESSAGE_RECONSIDER_RANKING:
2147 handleRankingReconsideration(msg);
2148 break;
Chris Wren54bbef42014-07-09 18:37:56 -04002149 case MESSAGE_RANKING_CONFIG_CHANGE:
2150 handleRankingConfigChange();
2151 break;
Adam Lesinski182f73f2013-12-05 16:48:06 -08002152 }
2153 }
2154 }
2155
Adam Lesinski182f73f2013-12-05 16:48:06 -08002156 // Notifications
2157 // ============================================================================
2158 static int clamp(int x, int low, int high) {
2159 return (x < low) ? low : ((x > high) ? high : x);
2160 }
2161
2162 void sendAccessibilityEvent(Notification notification, CharSequence packageName) {
2163 AccessibilityManager manager = AccessibilityManager.getInstance(getContext());
svetoslavganov75986cf2009-05-14 22:28:01 -07002164 if (!manager.isEnabled()) {
2165 return;
2166 }
2167
2168 AccessibilityEvent event =
2169 AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
2170 event.setPackageName(packageName);
2171 event.setClassName(Notification.class.getName());
2172 event.setParcelableData(notification);
2173 CharSequence tickerText = notification.tickerText;
2174 if (!TextUtils.isEmpty(tickerText)) {
2175 event.getText().add(tickerText);
2176 }
2177
2178 manager.sendAccessibilityEvent(event);
2179 }
2180
Christoph Studer546bec82014-03-14 12:17:12 +01002181 private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason) {
Joe Onorato46439ce2010-11-19 13:56:21 -08002182 // tell the app
2183 if (sendDelete) {
Daniel Sandlerfde19b12013-01-17 00:21:05 -05002184 if (r.getNotification().deleteIntent != null) {
Joe Onorato46439ce2010-11-19 13:56:21 -08002185 try {
Daniel Sandlerfde19b12013-01-17 00:21:05 -05002186 r.getNotification().deleteIntent.send();
Joe Onorato46439ce2010-11-19 13:56:21 -08002187 } catch (PendingIntent.CanceledException ex) {
2188 // do nothing - there's no relevant way to recover, and
2189 // no reason to let this propagate
Daniel Sandler4f91efd2013-04-25 16:38:41 -04002190 Slog.w(TAG, "canceled PendingIntent for " + r.sbn.getPackageName(), ex);
Joe Onorato46439ce2010-11-19 13:56:21 -08002191 }
2192 }
2193 }
2194
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002195 // status bar
Daniel Sandlerfde19b12013-01-17 00:21:05 -05002196 if (r.getNotification().icon != 0) {
Christoph Studer71f18fd2014-05-20 17:02:04 +02002197 r.isCanceled = true;
Chris Wren333a61c2014-05-28 16:40:57 -04002198 mListeners.notifyRemovedLocked(r.sbn);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002199 }
2200
2201 // sound
2202 if (mSoundNotification == r) {
2203 mSoundNotification = null;
Jeff Sharkey098d5802012-04-26 17:30:34 -07002204 final long identity = Binder.clearCallingIdentity();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002205 try {
Adam Lesinski182f73f2013-12-05 16:48:06 -08002206 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
Jeff Sharkey098d5802012-04-26 17:30:34 -07002207 if (player != null) {
2208 player.stopAsync();
2209 }
2210 } catch (RemoteException e) {
2211 } finally {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002212 Binder.restoreCallingIdentity(identity);
2213 }
2214 }
2215
2216 // vibrate
2217 if (mVibrateNotification == r) {
2218 mVibrateNotification = null;
2219 long identity = Binder.clearCallingIdentity();
2220 try {
2221 mVibrator.cancel();
2222 }
2223 finally {
2224 Binder.restoreCallingIdentity(identity);
2225 }
2226 }
2227
2228 // light
Chris Wrena3446562014-06-03 18:11:47 -04002229 mLights.remove(r.getKey());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002230 if (mLedNotification == r) {
2231 mLedNotification = null;
2232 }
Daniel Sandler23d7c702013-03-07 16:32:06 -05002233
Christoph Studer546bec82014-03-14 12:17:12 +01002234 // Record usage stats
2235 switch (reason) {
2236 case REASON_DELEGATE_CANCEL:
2237 case REASON_DELEGATE_CANCEL_ALL:
2238 case REASON_LISTENER_CANCEL:
2239 case REASON_LISTENER_CANCEL_ALL:
2240 mUsageStats.registerDismissedByUser(r);
2241 break;
2242 case REASON_NOMAN_CANCEL:
2243 case REASON_NOMAN_CANCEL_ALL:
2244 mUsageStats.registerRemovedByApp(r);
2245 break;
2246 case REASON_DELEGATE_CLICK:
2247 mUsageStats.registerCancelDueToClick(r);
2248 break;
2249 default:
2250 mUsageStats.registerCancelUnknown(r);
2251 break;
2252 }
2253
Christoph Studercef37cf2014-07-25 14:18:17 +02002254 mNotificationsByKey.remove(r.sbn.getKey());
2255
Daniel Sandler23d7c702013-03-07 16:32:06 -05002256 // Save it for users of getHistoricalNotifications()
2257 mArchive.record(r.sbn);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002258 }
2259
2260 /**
Dianne Hackbornd8a43f62009-08-17 23:33:56 -07002261 * Cancels a notification ONLY if it has all of the {@code mustHaveFlags}
Doug Zongkerab5c49c2009-12-04 10:31:43 -08002262 * and none of the {@code mustNotHaveFlags}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002263 */
John Spurlocke6a7d932014-03-13 12:29:00 -04002264 void cancelNotification(final int callingUid, final int callingPid,
2265 final String pkg, final String tag, final int id,
Svetoslav Ganov835835e2013-08-04 20:17:52 -07002266 final int mustHaveFlags, final int mustNotHaveFlags, final boolean sendDelete,
John Spurlock7340fc82014-04-24 18:50:12 -04002267 final int userId, final int reason, final ManagedServiceInfo listener) {
Svetoslav Ganov835835e2013-08-04 20:17:52 -07002268 // In enqueueNotificationInternal notifications are added by scheduling the
2269 // work on the worker handler. Hence, we also schedule the cancel on this
2270 // handler to avoid a scenario where an add notification call followed by a
2271 // remove notification call ends up in not removing the notification.
2272 mHandler.post(new Runnable() {
2273 @Override
2274 public void run() {
Christoph Studere4ef156b2014-07-04 18:41:57 +02002275 String listenerName = listener == null ? null : listener.component.toShortString();
John Spurlocke6a7d932014-03-13 12:29:00 -04002276 EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, id, tag, userId,
Christoph Studere4ef156b2014-07-04 18:41:57 +02002277 mustHaveFlags, mustNotHaveFlags, reason, listenerName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002278
Svetoslav Ganov835835e2013-08-04 20:17:52 -07002279 synchronized (mNotificationList) {
2280 int index = indexOfNotificationLocked(pkg, tag, id, userId);
2281 if (index >= 0) {
2282 NotificationRecord r = mNotificationList.get(index);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08002283
Christoph Studer546bec82014-03-14 12:17:12 +01002284 // Ideally we'd do this in the caller of this method. However, that would
2285 // require the caller to also find the notification.
2286 if (reason == REASON_DELEGATE_CLICK) {
2287 mUsageStats.registerClickedByUser(r);
2288 }
2289
Svetoslav Ganov835835e2013-08-04 20:17:52 -07002290 if ((r.getNotification().flags & mustHaveFlags) != mustHaveFlags) {
2291 return;
2292 }
2293 if ((r.getNotification().flags & mustNotHaveFlags) != 0) {
2294 return;
2295 }
2296
2297 mNotificationList.remove(index);
2298
Christoph Studer546bec82014-03-14 12:17:12 +01002299 cancelNotificationLocked(r, sendDelete, reason);
Christoph Studere4ef156b2014-07-04 18:41:57 +02002300 cancelGroupChildrenLocked(r, callingUid, callingPid, listenerName);
Svetoslav Ganov835835e2013-08-04 20:17:52 -07002301 updateLightsLocked();
2302 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002303 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002304 }
Svetoslav Ganov835835e2013-08-04 20:17:52 -07002305 });
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002306 }
2307
2308 /**
Daniel Sandler321e9c52012-10-12 10:59:26 -07002309 * Determine whether the userId applies to the notification in question, either because
2310 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard).
2311 */
2312 private boolean notificationMatchesUserId(NotificationRecord r, int userId) {
2313 return
2314 // looking for USER_ALL notifications? match everything
2315 userId == UserHandle.USER_ALL
2316 // a notification sent to USER_ALL matches any query
Daniel Sandlerfde19b12013-01-17 00:21:05 -05002317 || r.getUserId() == UserHandle.USER_ALL
Daniel Sandler321e9c52012-10-12 10:59:26 -07002318 // an exact user match
Daniel Sandlerfde19b12013-01-17 00:21:05 -05002319 || r.getUserId() == userId;
Daniel Sandler321e9c52012-10-12 10:59:26 -07002320 }
2321
2322 /**
Kenny Guy3a7c4a52014-03-03 18:24:03 +00002323 * Determine whether the userId applies to the notification in question, either because
2324 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard) or
Kenny Guy2a764942014-04-02 13:29:20 +01002325 * because it matches one of the users profiles.
Kenny Guy3a7c4a52014-03-03 18:24:03 +00002326 */
Kenny Guy2a764942014-04-02 13:29:20 +01002327 private boolean notificationMatchesCurrentProfiles(NotificationRecord r, int userId) {
Kenny Guya263e4e2014-03-03 18:24:03 +00002328 return notificationMatchesUserId(r, userId)
John Spurlockb408e8e2014-04-23 21:12:45 -04002329 || mUserProfiles.isCurrentProfile(r.getUserId());
Kenny Guy3a7c4a52014-03-03 18:24:03 +00002330 }
2331
2332 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002333 * Cancels all notifications from a given package that have all of the
2334 * {@code mustHaveFlags}.
2335 */
John Spurlocke6a7d932014-03-13 12:29:00 -04002336 boolean cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, int mustHaveFlags,
2337 int mustNotHaveFlags, boolean doit, int userId, int reason,
John Spurlock7340fc82014-04-24 18:50:12 -04002338 ManagedServiceInfo listener) {
Christoph Studere4ef156b2014-07-04 18:41:57 +02002339 String listenerName = listener == null ? null : listener.component.toShortString();
John Spurlocke6a7d932014-03-13 12:29:00 -04002340 EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
2341 pkg, userId, mustHaveFlags, mustNotHaveFlags, reason,
Christoph Studere4ef156b2014-07-04 18:41:57 +02002342 listenerName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002343
2344 synchronized (mNotificationList) {
2345 final int N = mNotificationList.size();
Christoph Studere4ef156b2014-07-04 18:41:57 +02002346 ArrayList<NotificationRecord> canceledNotifications = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002347 for (int i = N-1; i >= 0; --i) {
2348 NotificationRecord r = mNotificationList.get(i);
Daniel Sandler321e9c52012-10-12 10:59:26 -07002349 if (!notificationMatchesUserId(r, userId)) {
Dianne Hackborn41203752012-08-31 14:05:51 -07002350 continue;
2351 }
Amith Yamasani5ec00e92012-11-07 16:58:30 -08002352 // Don't remove notifications to all, if there's no package name specified
Daniel Sandlerfde19b12013-01-17 00:21:05 -05002353 if (r.getUserId() == UserHandle.USER_ALL && pkg == null) {
Amith Yamasani5ec00e92012-11-07 16:58:30 -08002354 continue;
2355 }
Daniel Sandlerfde19b12013-01-17 00:21:05 -05002356 if ((r.getFlags() & mustHaveFlags) != mustHaveFlags) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002357 continue;
2358 }
Daniel Sandlerfde19b12013-01-17 00:21:05 -05002359 if ((r.getFlags() & mustNotHaveFlags) != 0) {
Dianne Hackbornd8a43f62009-08-17 23:33:56 -07002360 continue;
2361 }
Daniel Sandler4f91efd2013-04-25 16:38:41 -04002362 if (pkg != null && !r.sbn.getPackageName().equals(pkg)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002363 continue;
2364 }
Christoph Studere4ef156b2014-07-04 18:41:57 +02002365 if (canceledNotifications == null) {
2366 canceledNotifications = new ArrayList<>();
2367 }
2368 canceledNotifications.add(r);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08002369 if (!doit) {
2370 return true;
2371 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002372 mNotificationList.remove(i);
Christoph Studer546bec82014-03-14 12:17:12 +01002373 cancelNotificationLocked(r, false, reason);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002374 }
Christoph Studere4ef156b2014-07-04 18:41:57 +02002375 if (doit && canceledNotifications != null) {
2376 final int M = canceledNotifications.size();
2377 for (int i = 0; i < M; i++) {
2378 cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid,
2379 listenerName);
2380 }
2381 }
2382 if (canceledNotifications != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002383 updateLightsLocked();
2384 }
Christoph Studere4ef156b2014-07-04 18:41:57 +02002385 return canceledNotifications != null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002386 }
2387 }
2388
Adam Lesinski350159c2014-03-27 11:15:11 -07002389 void cancelAllLocked(int callingUid, int callingPid, int userId, int reason,
John Spurlock7340fc82014-04-24 18:50:12 -04002390 ManagedServiceInfo listener, boolean includeCurrentProfiles) {
Christoph Studere4ef156b2014-07-04 18:41:57 +02002391 String listenerName = listener == null ? null : listener.component.toShortString();
John Spurlocke6a7d932014-03-13 12:29:00 -04002392 EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
Christoph Studere4ef156b2014-07-04 18:41:57 +02002393 null, userId, 0, 0, reason, listenerName);
Christoph Studer546bec82014-03-14 12:17:12 +01002394
Christoph Studere4ef156b2014-07-04 18:41:57 +02002395 ArrayList<NotificationRecord> canceledNotifications = null;
Adam Lesinskie8240262014-03-26 16:01:00 -07002396 final int N = mNotificationList.size();
2397 for (int i=N-1; i>=0; i--) {
2398 NotificationRecord r = mNotificationList.get(i);
Kenny Guya263e4e2014-03-03 18:24:03 +00002399 if (includeCurrentProfiles) {
2400 if (!notificationMatchesCurrentProfiles(r, userId)) {
2401 continue;
2402 }
2403 } else {
2404 if (!notificationMatchesUserId(r, userId)) {
2405 continue;
2406 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002407 }
2408
Adam Lesinskie8240262014-03-26 16:01:00 -07002409 if ((r.getFlags() & (Notification.FLAG_ONGOING_EVENT
2410 | Notification.FLAG_NO_CLEAR)) == 0) {
2411 mNotificationList.remove(i);
Christoph Studer546bec82014-03-14 12:17:12 +01002412 cancelNotificationLocked(r, true, reason);
Christoph Studere4ef156b2014-07-04 18:41:57 +02002413 // Make a note so we can cancel children later.
2414 if (canceledNotifications == null) {
2415 canceledNotifications = new ArrayList<>();
2416 }
2417 canceledNotifications.add(r);
Adam Lesinskie8240262014-03-26 16:01:00 -07002418 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002419 }
Christoph Studere4ef156b2014-07-04 18:41:57 +02002420 int M = canceledNotifications != null ? canceledNotifications.size() : 0;
2421 for (int i = 0; i < M; i++) {
2422 cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid,
2423 listenerName);
2424 }
Adam Lesinskie8240262014-03-26 16:01:00 -07002425 updateLightsLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002426 }
2427
Christoph Studere4ef156b2014-07-04 18:41:57 +02002428 // Warning: The caller is responsible for invoking updateLightsLocked().
2429 private void cancelGroupChildrenLocked(NotificationRecord r, int callingUid, int callingPid,
2430 String listenerName) {
2431 Notification n = r.getNotification();
Christoph Studer3f31f5d2014-07-31 16:55:32 +02002432 if (!n.isGroupSummary()) {
Christoph Studere4ef156b2014-07-04 18:41:57 +02002433 return;
2434 }
2435
2436 String pkg = r.sbn.getPackageName();
2437 int userId = r.getUserId();
2438
2439 if (pkg == null) {
2440 if (DBG) Log.e(TAG, "No package for group summary: " + r.getKey());
2441 return;
2442 }
2443
2444 final int N = mNotificationList.size();
2445 for (int i = N - 1; i >= 0; i--) {
2446 NotificationRecord childR = mNotificationList.get(i);
Christoph Studere4ef156b2014-07-04 18:41:57 +02002447 StatusBarNotification childSbn = childR.sbn;
Christoph Studerc44caa92014-08-22 19:16:00 +02002448 if (childR.getNotification().isGroupChild() &&
2449 childR.getGroupKey().equals(r.getGroupKey())) {
Christoph Studere4ef156b2014-07-04 18:41:57 +02002450 EventLogTags.writeNotificationCancel(callingUid, callingPid,
2451 pkg, childSbn.getId(), childSbn.getTag(), userId, 0, 0,
2452 REASON_GROUP_SUMMARY_CANCELED, listenerName);
2453 mNotificationList.remove(i);
Christoph Studere4ef156b2014-07-04 18:41:57 +02002454 cancelNotificationLocked(childR, false, REASON_GROUP_SUMMARY_CANCELED);
2455 }
2456 }
2457 }
2458
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002459 // lock on mNotificationList
Adam Lesinski182f73f2013-12-05 16:48:06 -08002460 void updateLightsLocked()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002461 {
The Android Open Source Project10592532009-03-18 17:39:46 -07002462 // handle notification lights
2463 if (mLedNotification == null) {
2464 // get next notification, if any
2465 int n = mLights.size();
2466 if (n > 0) {
Chris Wrena3446562014-06-03 18:11:47 -04002467 mLedNotification = mNotificationsByKey.get(mLights.get(n-1));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002468 }
2469 }
Mike Lockwoodc22404a2009-12-02 11:15:02 -05002470
Mike Lockwood63b5ad92011-08-30 09:55:30 -04002471 // Don't flash while we are in a call or screen is on
2472 if (mLedNotification == null || mInCall || mScreenOn) {
Mike Lockwood3cb67a32009-11-27 14:25:58 -05002473 mNotificationLight.turnOff();
John Spurlockcb566aa2014-08-03 22:58:28 -04002474 mStatusBar.notificationLightOff();
The Android Open Source Project10592532009-03-18 17:39:46 -07002475 } else {
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -04002476 final Notification ledno = mLedNotification.sbn.getNotification();
Daniel Sandlerfde19b12013-01-17 00:21:05 -05002477 int ledARGB = ledno.ledARGB;
2478 int ledOnMS = ledno.ledOnMS;
2479 int ledOffMS = ledno.ledOffMS;
2480 if ((ledno.defaults & Notification.DEFAULT_LIGHTS) != 0) {
Mike Lockwood670f9322010-01-20 12:13:36 -05002481 ledARGB = mDefaultNotificationColor;
2482 ledOnMS = mDefaultNotificationLedOn;
2483 ledOffMS = mDefaultNotificationLedOff;
2484 }
2485 if (mNotificationPulseEnabled) {
2486 // pulse repeatedly
Adam Lesinski182f73f2013-12-05 16:48:06 -08002487 mNotificationLight.setFlashing(ledARGB, Light.LIGHT_FLASH_TIMED,
Mike Lockwood670f9322010-01-20 12:13:36 -05002488 ledOnMS, ledOffMS);
Mike Lockwood670f9322010-01-20 12:13:36 -05002489 }
John Spurlock39292322014-08-13 11:00:59 -04002490 // let SystemUI make an independent decision
2491 mStatusBar.notificationLightPulse(ledARGB, ledOnMS, ledOffMS);
The Android Open Source Project10592532009-03-18 17:39:46 -07002492 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002493 }
2494
2495 // lock on mNotificationList
Adam Lesinski182f73f2013-12-05 16:48:06 -08002496 int indexOfNotificationLocked(String pkg, String tag, int id, int userId)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002497 {
2498 ArrayList<NotificationRecord> list = mNotificationList;
2499 final int len = list.size();
2500 for (int i=0; i<len; i++) {
2501 NotificationRecord r = list.get(i);
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -04002502 if (!notificationMatchesUserId(r, userId) || r.sbn.getId() != id) {
Dianne Hackborn41203752012-08-31 14:05:51 -07002503 continue;
2504 }
Fred Quintana6ecaff12009-09-25 14:23:13 -07002505 if (tag == null) {
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -04002506 if (r.sbn.getTag() != null) {
Fred Quintana6ecaff12009-09-25 14:23:13 -07002507 continue;
2508 }
2509 } else {
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -04002510 if (!tag.equals(r.sbn.getTag())) {
Fred Quintana6ecaff12009-09-25 14:23:13 -07002511 continue;
2512 }
2513 }
Daniel Sandler4f91efd2013-04-25 16:38:41 -04002514 if (r.sbn.getPackageName().equals(pkg)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002515 return i;
2516 }
2517 }
2518 return -1;
2519 }
2520
Christoph Studer71f18fd2014-05-20 17:02:04 +02002521 // lock on mNotificationList
2522 int indexOfNotificationLocked(String key) {
Christoph Studerc5115552014-06-12 20:22:31 +02002523 final int N = mNotificationList.size();
2524 for (int i = 0; i < N; i++) {
2525 if (key.equals(mNotificationList.get(i).getKey())) {
2526 return i;
2527 }
Christoph Studer71f18fd2014-05-20 17:02:04 +02002528 }
Christoph Studerc5115552014-06-12 20:22:31 +02002529 return -1;
Christoph Studer71f18fd2014-05-20 17:02:04 +02002530 }
2531
Mike Lockwoodc22404a2009-12-02 11:15:02 -05002532 private void updateNotificationPulse() {
2533 synchronized (mNotificationList) {
2534 updateLightsLocked();
2535 }
2536 }
John Spurlocke677d712014-02-13 12:52:19 -05002537
John Spurlock7340fc82014-04-24 18:50:12 -04002538 private static boolean isUidSystem(int uid) {
2539 final int appid = UserHandle.getAppId(uid);
2540 return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0);
2541 }
John Spurlockb408e8e2014-04-23 21:12:45 -04002542
John Spurlock7340fc82014-04-24 18:50:12 -04002543 private static boolean isCallerSystem() {
2544 return isUidSystem(Binder.getCallingUid());
2545 }
2546
2547 private static void checkCallerIsSystem() {
2548 if (isCallerSystem()) {
2549 return;
2550 }
2551 throw new SecurityException("Disallowed call for uid " + Binder.getCallingUid());
2552 }
2553
2554 private static void checkCallerIsSystemOrSameApp(String pkg) {
2555 if (isCallerSystem()) {
2556 return;
2557 }
2558 final int uid = Binder.getCallingUid();
2559 try {
2560 ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo(
2561 pkg, 0, UserHandle.getCallingUserId());
Dan Sandler09afc2e2014-07-18 14:29:20 -04002562 if (ai == null) {
2563 throw new SecurityException("Unknown package " + pkg);
2564 }
John Spurlock7340fc82014-04-24 18:50:12 -04002565 if (!UserHandle.isSameApp(ai.uid, uid)) {
2566 throw new SecurityException("Calling uid " + uid + " gave package"
2567 + pkg + " which is owned by uid " + ai.uid);
2568 }
2569 } catch (RemoteException re) {
2570 throw new SecurityException("Unknown package " + pkg + "\n" + re);
2571 }
2572 }
2573
Christoph Studer05ad4822014-05-16 14:16:03 +02002574 /**
2575 * Generates a NotificationRankingUpdate from 'sbns', considering only
2576 * notifications visible to the given listener.
Chris Wren333a61c2014-05-28 16:40:57 -04002577 *
2578 * <p>Caller must hold a lock on mNotificationList.</p>
Christoph Studer05ad4822014-05-16 14:16:03 +02002579 */
Chris Wren333a61c2014-05-28 16:40:57 -04002580 private NotificationRankingUpdate makeRankingUpdateLocked(ManagedServiceInfo info) {
Christoph Studer05ad4822014-05-16 14:16:03 +02002581 int speedBumpIndex = -1;
Chris Wren333a61c2014-05-28 16:40:57 -04002582 final int N = mNotificationList.size();
2583 ArrayList<String> keys = new ArrayList<String>(N);
Christoph Studer1d599da2014-06-12 15:25:59 +02002584 ArrayList<String> interceptedKeys = new ArrayList<String>(N);
Chris Wren333a61c2014-05-28 16:40:57 -04002585 for (int i = 0; i < N; i++) {
2586 NotificationRecord record = mNotificationList.get(i);
Christoph Studercef37cf2014-07-25 14:18:17 +02002587 if (!isVisibleToListener(record.sbn, info)) {
Christoph Studer05ad4822014-05-16 14:16:03 +02002588 continue;
2589 }
Chris Wren333a61c2014-05-28 16:40:57 -04002590 keys.add(record.sbn.getKey());
2591 if (record.isIntercepted()) {
Christoph Studer1d599da2014-06-12 15:25:59 +02002592 interceptedKeys.add(record.sbn.getKey());
Christoph Studer05ad4822014-05-16 14:16:03 +02002593 }
2594 if (speedBumpIndex == -1 &&
Chris Wren333a61c2014-05-28 16:40:57 -04002595 record.sbn.getNotification().priority == Notification.PRIORITY_MIN) {
Christoph Studer05ad4822014-05-16 14:16:03 +02002596 speedBumpIndex = keys.size() - 1;
2597 }
2598 }
2599 String[] keysAr = keys.toArray(new String[keys.size()]);
Christoph Studer1d599da2014-06-12 15:25:59 +02002600 String[] interceptedKeysAr = interceptedKeys.toArray(new String[interceptedKeys.size()]);
2601 return new NotificationRankingUpdate(keysAr, interceptedKeysAr, speedBumpIndex);
Christoph Studer05ad4822014-05-16 14:16:03 +02002602 }
2603
Christoph Studercef37cf2014-07-25 14:18:17 +02002604 private boolean isVisibleToListener(StatusBarNotification sbn, ManagedServiceInfo listener) {
2605 if (!listener.enabledAndUserMatches(sbn.getUserId())) {
2606 return false;
2607 }
Justin Koh8d11a5a2014-08-04 18:29:49 -07002608 // TODO: remove this for older listeners.
Christoph Studercef37cf2014-07-25 14:18:17 +02002609 return true;
2610 }
2611
John Spurlock7340fc82014-04-24 18:50:12 -04002612 public class NotificationListeners extends ManagedServices {
2613
2614 public NotificationListeners() {
2615 super(getContext(), mHandler, mNotificationList, mUserProfiles);
2616 }
2617
2618 @Override
2619 protected Config getConfig() {
2620 Config c = new Config();
2621 c.caption = "notification listener";
2622 c.serviceInterface = NotificationListenerService.SERVICE_INTERFACE;
2623 c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_LISTENERS;
2624 c.bindPermission = android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE;
2625 c.settingsAction = Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS;
2626 c.clientLabel = R.string.notification_listener_binding_label;
2627 return c;
2628 }
2629
2630 @Override
2631 protected IInterface asInterface(IBinder binder) {
2632 return INotificationListener.Stub.asInterface(binder);
2633 }
2634
2635 @Override
John Spurlock3b98b3f2014-05-01 09:08:48 -04002636 public void onServiceAdded(ManagedServiceInfo info) {
2637 final INotificationListener listener = (INotificationListener) info.service;
Chris Wren333a61c2014-05-28 16:40:57 -04002638 final NotificationRankingUpdate update;
Christoph Studer05ad4822014-05-16 14:16:03 +02002639 synchronized (mNotificationList) {
Chris Wren333a61c2014-05-28 16:40:57 -04002640 update = makeRankingUpdateLocked(info);
Christoph Studer05ad4822014-05-16 14:16:03 +02002641 }
John Spurlock7340fc82014-04-24 18:50:12 -04002642 try {
Chris Wren333a61c2014-05-28 16:40:57 -04002643 listener.onListenerConnected(update);
John Spurlock7340fc82014-04-24 18:50:12 -04002644 } catch (RemoteException e) {
2645 // we tried
2646 }
2647 }
2648
John Spurlock1fa865f2014-07-21 14:56:39 -04002649 @Override
2650 protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
John Spurlockd8afe3c2014-08-01 14:04:07 -04002651 if (mListenersDisablingEffects.remove(removed)) {
2652 updateListenerHintsLocked();
John Spurlock1fa865f2014-07-21 14:56:39 -04002653 }
2654 }
2655
John Spurlock7340fc82014-04-24 18:50:12 -04002656 /**
2657 * asynchronously notify all listeners about a new notification
Christoph Studercef37cf2014-07-25 14:18:17 +02002658 *
2659 * <p>
2660 * Also takes care of removing a notification that has been visible to a listener before,
2661 * but isn't anymore.
John Spurlock7340fc82014-04-24 18:50:12 -04002662 */
Christoph Studercef37cf2014-07-25 14:18:17 +02002663 public void notifyPostedLocked(StatusBarNotification sbn, StatusBarNotification oldSbn) {
John Spurlock7340fc82014-04-24 18:50:12 -04002664 // make a copy in case changes are made to the underlying Notification object
2665 final StatusBarNotification sbnClone = sbn.clone();
2666 for (final ManagedServiceInfo info : mServices) {
Christoph Studercef37cf2014-07-25 14:18:17 +02002667 boolean sbnVisible = isVisibleToListener(sbn, info);
2668 boolean oldSbnVisible = oldSbn != null ? isVisibleToListener(oldSbn, info) : false;
2669 // This notification hasn't been and still isn't visible -> ignore.
2670 if (!oldSbnVisible && !sbnVisible) {
Christoph Studer05ad4822014-05-16 14:16:03 +02002671 continue;
Chris Wrenf9536642014-04-17 10:01:54 -04002672 }
Chris Wren333a61c2014-05-28 16:40:57 -04002673 final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
Christoph Studercef37cf2014-07-25 14:18:17 +02002674
2675 // This notification became invisible -> remove the old one.
2676 if (oldSbnVisible && !sbnVisible) {
2677 final StatusBarNotification oldSbnLightClone = oldSbn.cloneLight();
2678 mHandler.post(new Runnable() {
2679 @Override
2680 public void run() {
2681 notifyRemoved(info, oldSbnLightClone, update);
2682 }
2683 });
Christoph Studer05ad4822014-05-16 14:16:03 +02002684 continue;
2685 }
Christoph Studercef37cf2014-07-25 14:18:17 +02002686
Christoph Studer05ad4822014-05-16 14:16:03 +02002687 mHandler.post(new Runnable() {
2688 @Override
2689 public void run() {
Christoph Studercef37cf2014-07-25 14:18:17 +02002690 notifyPosted(info, sbnClone, update);
Christoph Studer05ad4822014-05-16 14:16:03 +02002691 }
2692 });
Kenny Guy3a7c4a52014-03-03 18:24:03 +00002693 }
2694 }
Kenny Guy3a7c4a52014-03-03 18:24:03 +00002695
John Spurlock7340fc82014-04-24 18:50:12 -04002696 /**
2697 * asynchronously notify all listeners about a removed notification
2698 */
Chris Wren333a61c2014-05-28 16:40:57 -04002699 public void notifyRemovedLocked(StatusBarNotification sbn) {
John Spurlock7340fc82014-04-24 18:50:12 -04002700 // make a copy in case changes are made to the underlying Notification object
2701 // NOTE: this copy is lightweight: it doesn't include heavyweight parts of the
2702 // notification
2703 final StatusBarNotification sbnLight = sbn.cloneLight();
Chris Wrenf9536642014-04-17 10:01:54 -04002704 for (final ManagedServiceInfo info : mServices) {
Christoph Studercef37cf2014-07-25 14:18:17 +02002705 if (!isVisibleToListener(sbn, info)) {
Christoph Studer05ad4822014-05-16 14:16:03 +02002706 continue;
Chris Wrenf9536642014-04-17 10:01:54 -04002707 }
Chris Wren333a61c2014-05-28 16:40:57 -04002708 final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
Christoph Studer05ad4822014-05-16 14:16:03 +02002709 mHandler.post(new Runnable() {
2710 @Override
2711 public void run() {
Christoph Studercef37cf2014-07-25 14:18:17 +02002712 notifyRemoved(info, sbnLight, update);
Christoph Studer05ad4822014-05-16 14:16:03 +02002713 }
2714 });
Chris Wrenf9536642014-04-17 10:01:54 -04002715 }
2716 }
2717
2718 /**
2719 * asynchronously notify all listeners about a reordering of notifications
Chris Wrenf9536642014-04-17 10:01:54 -04002720 */
Chris Wren333a61c2014-05-28 16:40:57 -04002721 public void notifyRankingUpdateLocked() {
Chris Wrenf9536642014-04-17 10:01:54 -04002722 for (final ManagedServiceInfo serviceInfo : mServices) {
Christoph Studer05ad4822014-05-16 14:16:03 +02002723 if (!serviceInfo.isEnabledForCurrentProfiles()) {
2724 continue;
2725 }
Christoph Studercef37cf2014-07-25 14:18:17 +02002726 final NotificationRankingUpdate update = makeRankingUpdateLocked(serviceInfo);
John Spurlock7340fc82014-04-24 18:50:12 -04002727 mHandler.post(new Runnable() {
2728 @Override
2729 public void run() {
Chris Wren333a61c2014-05-28 16:40:57 -04002730 notifyRankingUpdate(serviceInfo, update);
John Spurlock7340fc82014-04-24 18:50:12 -04002731 }
2732 });
Kenny Guya263e4e2014-03-03 18:24:03 +00002733 }
Kenny Guya263e4e2014-03-03 18:24:03 +00002734 }
Kenny Guya263e4e2014-03-03 18:24:03 +00002735
John Spurlockd8afe3c2014-08-01 14:04:07 -04002736 public void notifyListenerHintsChangedLocked(final int hints) {
John Spurlock1fa865f2014-07-21 14:56:39 -04002737 for (final ManagedServiceInfo serviceInfo : mServices) {
2738 if (!serviceInfo.isEnabledForCurrentProfiles()) {
2739 continue;
2740 }
2741 mHandler.post(new Runnable() {
2742 @Override
2743 public void run() {
John Spurlockd8afe3c2014-08-01 14:04:07 -04002744 notifyListenerHintsChanged(serviceInfo, hints);
John Spurlock1fa865f2014-07-21 14:56:39 -04002745 }
2746 });
2747 }
2748 }
2749
Christoph Studer85a384b2014-08-27 20:16:15 +02002750 public void notifyInterruptionFilterChanged(final int interruptionFilter) {
2751 for (final ManagedServiceInfo serviceInfo : mServices) {
2752 if (!serviceInfo.isEnabledForCurrentProfiles()) {
2753 continue;
2754 }
2755 mHandler.post(new Runnable() {
2756 @Override
2757 public void run() {
2758 notifyInterruptionFilterChanged(serviceInfo, interruptionFilter);
2759 }
2760 });
2761 }
2762 }
2763
Christoph Studercef37cf2014-07-25 14:18:17 +02002764 private void notifyPosted(final ManagedServiceInfo info,
Christoph Studer05ad4822014-05-16 14:16:03 +02002765 final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) {
John Spurlock7340fc82014-04-24 18:50:12 -04002766 final INotificationListener listener = (INotificationListener)info.service;
2767 try {
Christoph Studer05ad4822014-05-16 14:16:03 +02002768 listener.onNotificationPosted(sbn, rankingUpdate);
John Spurlock7340fc82014-04-24 18:50:12 -04002769 } catch (RemoteException ex) {
2770 Log.e(TAG, "unable to notify listener (posted): " + listener, ex);
2771 }
2772 }
2773
Christoph Studercef37cf2014-07-25 14:18:17 +02002774 private void notifyRemoved(ManagedServiceInfo info, StatusBarNotification sbn,
Christoph Studer05ad4822014-05-16 14:16:03 +02002775 NotificationRankingUpdate rankingUpdate) {
John Spurlock7340fc82014-04-24 18:50:12 -04002776 if (!info.enabledAndUserMatches(sbn.getUserId())) {
2777 return;
2778 }
Christoph Studer05ad4822014-05-16 14:16:03 +02002779 final INotificationListener listener = (INotificationListener) info.service;
John Spurlock7340fc82014-04-24 18:50:12 -04002780 try {
Christoph Studer05ad4822014-05-16 14:16:03 +02002781 listener.onNotificationRemoved(sbn, rankingUpdate);
John Spurlock7340fc82014-04-24 18:50:12 -04002782 } catch (RemoteException ex) {
2783 Log.e(TAG, "unable to notify listener (removed): " + listener, ex);
John Spurlockb408e8e2014-04-23 21:12:45 -04002784 }
Kenny Guya263e4e2014-03-03 18:24:03 +00002785 }
Chris Wrenf9536642014-04-17 10:01:54 -04002786
Christoph Studer05ad4822014-05-16 14:16:03 +02002787 private void notifyRankingUpdate(ManagedServiceInfo info,
2788 NotificationRankingUpdate rankingUpdate) {
2789 final INotificationListener listener = (INotificationListener) info.service;
Chris Wrenf9536642014-04-17 10:01:54 -04002790 try {
Christoph Studer05ad4822014-05-16 14:16:03 +02002791 listener.onNotificationRankingUpdate(rankingUpdate);
Chris Wrenf9536642014-04-17 10:01:54 -04002792 } catch (RemoteException ex) {
2793 Log.e(TAG, "unable to notify listener (ranking update): " + listener, ex);
2794 }
2795 }
John Spurlock1fa865f2014-07-21 14:56:39 -04002796
John Spurlockd8afe3c2014-08-01 14:04:07 -04002797 private void notifyListenerHintsChanged(ManagedServiceInfo info, int hints) {
John Spurlock1fa865f2014-07-21 14:56:39 -04002798 final INotificationListener listener = (INotificationListener) info.service;
2799 try {
John Spurlockd8afe3c2014-08-01 14:04:07 -04002800 listener.onListenerHintsChanged(hints);
John Spurlock1fa865f2014-07-21 14:56:39 -04002801 } catch (RemoteException ex) {
John Spurlockd8afe3c2014-08-01 14:04:07 -04002802 Log.e(TAG, "unable to notify listener (listener hints): " + listener, ex);
John Spurlock1fa865f2014-07-21 14:56:39 -04002803 }
2804 }
Justin Koh38156c52014-06-04 13:57:49 -07002805
Christoph Studer85a384b2014-08-27 20:16:15 +02002806 private void notifyInterruptionFilterChanged(ManagedServiceInfo info,
2807 int interruptionFilter) {
2808 final INotificationListener listener = (INotificationListener) info.service;
2809 try {
2810 listener.onInterruptionFilterChanged(interruptionFilter);
2811 } catch (RemoteException ex) {
2812 Log.e(TAG, "unable to notify listener (interruption filter): " + listener, ex);
2813 }
2814 }
2815
Justin Koh38156c52014-06-04 13:57:49 -07002816 private boolean isListenerPackage(String packageName) {
2817 if (packageName == null) {
2818 return false;
2819 }
2820 // TODO: clean up locking object later
2821 synchronized (mNotificationList) {
2822 for (final ManagedServiceInfo serviceInfo : mServices) {
2823 if (packageName.equals(serviceInfo.component.getPackageName())) {
2824 return true;
2825 }
2826 }
2827 }
2828 return false;
2829 }
Kenny Guya263e4e2014-03-03 18:24:03 +00002830 }
John Spurlock25e2d242014-06-27 13:58:23 -04002831
2832 public static final class DumpFilter {
2833 public String pkgFilter;
John Spurlock50806fc2014-07-15 10:22:02 -04002834 public boolean zen;
John Spurlock25e2d242014-06-27 13:58:23 -04002835
2836 public static DumpFilter parseFromArguments(String[] args) {
John Spurlock50806fc2014-07-15 10:22:02 -04002837 if (args != null && args.length == 2 && "p".equals(args[0])
2838 && args[1] != null && !args[1].trim().isEmpty()) {
2839 final DumpFilter filter = new DumpFilter();
2840 filter.pkgFilter = args[1].trim().toLowerCase();
2841 return filter;
John Spurlock25e2d242014-06-27 13:58:23 -04002842 }
John Spurlock50806fc2014-07-15 10:22:02 -04002843 if (args != null && args.length == 1 && "zen".equals(args[0])) {
2844 final DumpFilter filter = new DumpFilter();
2845 filter.zen = true;
2846 return filter;
2847 }
2848 return null;
John Spurlock25e2d242014-06-27 13:58:23 -04002849 }
2850
2851 public boolean matches(StatusBarNotification sbn) {
John Spurlock50806fc2014-07-15 10:22:02 -04002852 return zen ? true : sbn != null
2853 && (matches(sbn.getPackageName()) || matches(sbn.getOpPkg()));
John Spurlock25e2d242014-06-27 13:58:23 -04002854 }
2855
2856 public boolean matches(ComponentName component) {
John Spurlock50806fc2014-07-15 10:22:02 -04002857 return zen ? true : component != null && matches(component.getPackageName());
John Spurlock25e2d242014-06-27 13:58:23 -04002858 }
2859
2860 public boolean matches(String pkg) {
John Spurlock50806fc2014-07-15 10:22:02 -04002861 return zen ? true : pkg != null && pkg.toLowerCase().contains(pkgFilter);
2862 }
2863
2864 @Override
2865 public String toString() {
2866 return zen ? "zen" : ('\'' + pkgFilter + '\'');
John Spurlock25e2d242014-06-27 13:58:23 -04002867 }
2868 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002869}