blob: 38b8dc69958b81fc01bf6b76524b34c0ce1654e3 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Adam Lesinski182f73f2013-12-05 16:48:06 -080017package com.android.server.notification;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080018
Jeff Sharkey098d5802012-04-26 17:30:34 -070019import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
20import static org.xmlpull.v1.XmlPullParser.END_TAG;
21import static org.xmlpull.v1.XmlPullParser.START_TAG;
svetoslavganov75986cf2009-05-14 22:28:01 -070022
Dianne Hackborn41203752012-08-31 14:05:51 -070023import android.app.ActivityManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080024import android.app.ActivityManagerNative;
Amith Yamasanif203aee2012-08-29 18:41:53 -070025import android.app.AppGlobals;
Daniel Sandler4a900ac2013-01-30 14:04:10 -050026import android.app.AppOpsManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080027import android.app.IActivityManager;
28import android.app.INotificationManager;
29import android.app.ITransientNotification;
30import android.app.Notification;
31import android.app.PendingIntent;
32import android.app.StatusBarManager;
33import android.content.BroadcastReceiver;
Daniel Sandler5feceeb2013-03-22 18:29:23 -070034import android.content.ComponentName;
Dianne Hackborn1dac2772009-06-26 18:16:48 -070035import android.content.ContentResolver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080036import android.content.Context;
37import android.content.Intent;
38import android.content.IntentFilter;
Daniel Sandler5feceeb2013-03-22 18:29:23 -070039import android.content.ServiceConnection;
Dianne Hackbornd8a43f62009-08-17 23:33:56 -070040import android.content.pm.ApplicationInfo;
Daniel Sandler4a900ac2013-01-30 14:04:10 -050041import android.content.pm.PackageInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080042import android.content.pm.PackageManager;
Chris Wrenae9bb572013-05-15 14:50:28 -040043import android.content.pm.ResolveInfo;
44import android.content.pm.ServiceInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080045import android.content.pm.PackageManager.NameNotFoundException;
46import android.content.res.Resources;
Dianne Hackborn1dac2772009-06-26 18:16:48 -070047import android.database.ContentObserver;
Daniel Sandlerf45564e2013-04-15 15:05:08 -040048import android.graphics.Bitmap;
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;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080053import android.os.Handler;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080054import android.os.IBinder;
55import android.os.Message;
Dianne Hackbornd8a43f62009-08-17 23:33:56 -070056import android.os.Process;
svetoslavganov75986cf2009-05-14 22:28:01 -070057import android.os.RemoteException;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -070058import android.os.UserHandle;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080059import android.os.Vibrator;
60import android.provider.Settings;
Daniel Sandler5feceeb2013-03-22 18:29:23 -070061import android.service.notification.INotificationListener;
62import android.service.notification.NotificationListenerService;
63import android.service.notification.StatusBarNotification;
Daniel Sandlere96ffb12010-03-11 13:38:06 -050064import android.telephony.TelephonyManager;
svetoslavganov75986cf2009-05-14 22:28:01 -070065import android.text.TextUtils;
John Spurlock5579ec32014-03-28 09:42:05 -040066import android.util.ArrayMap;
Dianne Hackborn39606a02012-07-31 17:54:35 -070067import android.util.AtomicFile;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080068import android.util.EventLog;
69import android.util.Log;
Andy Stadler110988c2010-12-03 14:29:16 -080070import android.util.Slog;
Daniel Sandler0da673f2012-04-11 12:33:16 -040071import android.util.Xml;
svetoslavganov75986cf2009-05-14 22:28:01 -070072import android.view.accessibility.AccessibilityEvent;
73import android.view.accessibility.AccessibilityManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080074import android.widget.Toast;
75
Scott Greenwald9a05b312013-06-28 00:37:54 -040076import com.android.internal.R;
77
78import com.android.internal.notification.NotificationScorer;
Adam Lesinski182f73f2013-12-05 16:48:06 -080079import com.android.server.EventLogTags;
80import com.android.server.statusbar.StatusBarManagerInternal;
81import com.android.server.SystemService;
82import com.android.server.lights.Light;
83import com.android.server.lights.LightsManager;
84
Jeff Sharkey098d5802012-04-26 17:30:34 -070085import org.xmlpull.v1.XmlPullParser;
86import org.xmlpull.v1.XmlPullParserException;
Jeff Sharkey098d5802012-04-26 17:30:34 -070087
Daniel Sandler0da673f2012-04-11 12:33:16 -040088import java.io.File;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080089import java.io.FileDescriptor;
Daniel Sandler0da673f2012-04-11 12:33:16 -040090import java.io.FileInputStream;
91import java.io.FileNotFoundException;
Daniel Sandler0da673f2012-04-11 12:33:16 -040092import java.io.IOException;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080093import java.io.PrintWriter;
Daniel Sandlerf45564e2013-04-15 15:05:08 -040094import java.lang.reflect.Array;
Daniel Sandlerfde19b12013-01-17 00:21:05 -050095import java.util.ArrayDeque;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080096import java.util.ArrayList;
97import java.util.Arrays;
Daniel Sandler0da673f2012-04-11 12:33:16 -040098import java.util.HashSet;
Daniel Sandlerfde19b12013-01-17 00:21:05 -050099import java.util.Iterator;
Chris Wrenae9bb572013-05-15 14:50:28 -0400100import java.util.List;
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500101import java.util.NoSuchElementException;
Chris Wrenae9bb572013-05-15 14:50:28 -0400102import java.util.Set;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400103
104import libcore.io.IoUtils;
105
Daniel Sandlerd0a2f862010-08-03 15:29:31 -0400106/** {@hide} */
Adam Lesinski182f73f2013-12-05 16:48:06 -0800107public class NotificationManagerService extends SystemService {
108 static final String TAG = "NotificationService";
109 static final boolean DBG = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800110
Adam Lesinski182f73f2013-12-05 16:48:06 -0800111 static final int MAX_PACKAGE_NOTIFICATIONS = 50;
Joe Onoratobd73d012010-06-04 11:44:54 -0700112
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800113 // message codes
Adam Lesinski182f73f2013-12-05 16:48:06 -0800114 static final int MESSAGE_TIMEOUT = 2;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800115
Adam Lesinski182f73f2013-12-05 16:48:06 -0800116 static final int LONG_DELAY = 3500; // 3.5 seconds
117 static final int SHORT_DELAY = 2000; // 2 seconds
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800118
Adam Lesinski182f73f2013-12-05 16:48:06 -0800119 static final long[] DEFAULT_VIBRATE_PATTERN = {0, 250, 250, 250};
120 static final int VIBRATE_PATTERN_MAXLEN = 8 * 2 + 1; // up to eight bumps
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800121
Adam Lesinski182f73f2013-12-05 16:48:06 -0800122 static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_NOTIFICATION;
123 static final boolean SCORE_ONGOING_HIGHER = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800124
Adam Lesinski182f73f2013-12-05 16:48:06 -0800125 static final int JUNK_SCORE = -1000;
126 static final int NOTIFICATION_PRIORITY_MULTIPLIER = 10;
127 static final int SCORE_DISPLAY_THRESHOLD = Notification.PRIORITY_MIN * NOTIFICATION_PRIORITY_MULTIPLIER;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400128
Daniel Sandler526fa0e2012-12-04 14:51:50 -0500129 // Notifications with scores below this will not interrupt the user, either via LED or
130 // sound or vibration
Adam Lesinski182f73f2013-12-05 16:48:06 -0800131 static final int SCORE_INTERRUPTION_THRESHOLD =
Daniel Sandler526fa0e2012-12-04 14:51:50 -0500132 Notification.PRIORITY_LOW * NOTIFICATION_PRIORITY_MULTIPLIER;
133
Adam Lesinski182f73f2013-12-05 16:48:06 -0800134 static final boolean ENABLE_BLOCKED_NOTIFICATIONS = true;
135 static final boolean ENABLE_BLOCKED_TOASTS = true;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400136
Adam Lesinski182f73f2013-12-05 16:48:06 -0800137 static final String ENABLED_NOTIFICATION_LISTENERS_SEPARATOR = ":";
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700138
Adam Lesinski182f73f2013-12-05 16:48:06 -0800139 private IActivityManager mAm;
140 AudioManager mAudioManager;
141 StatusBarManagerInternal mStatusBar;
142 Vibrator mVibrator;
143
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800144 final IBinder mForegroundToken = new Binder();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800145 private WorkerHandler mHandler;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800146
Adam Lesinski182f73f2013-12-05 16:48:06 -0800147 private Light mNotificationLight;
148 Light mAttentionLight;
Mike Lockwood670f9322010-01-20 12:13:36 -0500149 private int mDefaultNotificationColor;
150 private int mDefaultNotificationLedOn;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800151
Mike Lockwood670f9322010-01-20 12:13:36 -0500152 private int mDefaultNotificationLedOff;
Daniel Sandleredbb3802012-11-13 20:49:47 -0800153 private long[] mDefaultVibrationPattern;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800154
Daniel Sandleredbb3802012-11-13 20:49:47 -0800155 private long[] mFallbackVibrationPattern;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800156 boolean mSystemReady;
Daniel Sandleredbb3802012-11-13 20:49:47 -0800157
Adam Lesinski182f73f2013-12-05 16:48:06 -0800158 int mDisabledNotifications;
159 NotificationRecord mSoundNotification;
160 NotificationRecord mVibrateNotification;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800161
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500162 // for enabling and disabling notification pulse behavior
Mike Lockwood63b5ad92011-08-30 09:55:30 -0400163 private boolean mScreenOn = true;
Daniel Sandlere96ffb12010-03-11 13:38:06 -0500164 private boolean mInCall = false;
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500165 private boolean mNotificationPulseEnabled;
166
Daniel Sandler09a247e2013-02-14 10:24:17 -0500167 // used as a mutex for access to all active notifications & listeners
Adam Lesinski182f73f2013-12-05 16:48:06 -0800168 final ArrayList<NotificationRecord> mNotificationList =
Fred Quintana6ecaff12009-09-25 14:23:13 -0700169 new ArrayList<NotificationRecord>();
John Spurlock5579ec32014-03-28 09:42:05 -0400170 final ArrayMap<String, NotificationRecord> mNotificationsByKey =
171 new ArrayMap<String, NotificationRecord>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800172
Adam Lesinski182f73f2013-12-05 16:48:06 -0800173 final ArrayList<ToastRecord> mToastQueue = new ArrayList<ToastRecord>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800174
Adam Lesinski182f73f2013-12-05 16:48:06 -0800175 ArrayList<NotificationRecord> mLights = new ArrayList<NotificationRecord>();
176 NotificationRecord mLedNotification;
svetoslavganov75986cf2009-05-14 22:28:01 -0700177
Adam Lesinski182f73f2013-12-05 16:48:06 -0800178 private AppOpsManager mAppOps;
Daniel Sandler4a900ac2013-01-30 14:04:10 -0500179
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700180 // contains connections to all connected listeners, including app services
181 // and system listeners
182 private ArrayList<NotificationListenerInfo> mListeners
183 = new ArrayList<NotificationListenerInfo>();
184 // things that will be put into mListeners as soon as they're ready
185 private ArrayList<String> mServicesBinding = new ArrayList<String>();
186 // lists the component names of all enabled (and therefore connected) listener
187 // app services for the current user only
188 private HashSet<ComponentName> mEnabledListenersForCurrentUser
189 = new HashSet<ComponentName>();
190 // Just the packages from mEnabledListenersForCurrentUser
191 private HashSet<String> mEnabledListenerPackageNames = new HashSet<String>();
Daniel Sandler09a247e2013-02-14 10:24:17 -0500192
Daniel Sandler0da673f2012-04-11 12:33:16 -0400193 // Notification control database. For now just contains disabled packages.
194 private AtomicFile mPolicyFile;
195 private HashSet<String> mBlockedPackages = new HashSet<String>();
196
197 private static final int DB_VERSION = 1;
198
199 private static final String TAG_BODY = "notification-policy";
200 private static final String ATTR_VERSION = "version";
201
202 private static final String TAG_BLOCKED_PKGS = "blocked-packages";
203 private static final String TAG_PACKAGE = "package";
204 private static final String ATTR_NAME = "name";
205
Adam Lesinski182f73f2013-12-05 16:48:06 -0800206 final ArrayList<NotificationScorer> mScorers = new ArrayList<NotificationScorer>();
Scott Greenwald9a05b312013-06-28 00:37:54 -0400207
Adam Lesinski182f73f2013-12-05 16:48:06 -0800208 private class NotificationListenerInfo implements IBinder.DeathRecipient {
Daniel Sandler09a247e2013-02-14 10:24:17 -0500209 INotificationListener listener;
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700210 ComponentName component;
Daniel Sandler09a247e2013-02-14 10:24:17 -0500211 int userid;
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400212 boolean isSystem;
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700213 ServiceConnection connection;
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400214
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700215 public NotificationListenerInfo(INotificationListener listener, ComponentName component,
216 int userid, boolean isSystem) {
Daniel Sandler09a247e2013-02-14 10:24:17 -0500217 this.listener = listener;
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700218 this.component = component;
Daniel Sandler09a247e2013-02-14 10:24:17 -0500219 this.userid = userid;
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400220 this.isSystem = isSystem;
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700221 this.connection = null;
222 }
223
224 public NotificationListenerInfo(INotificationListener listener, ComponentName component,
225 int userid, ServiceConnection connection) {
226 this.listener = listener;
227 this.component = component;
228 this.userid = userid;
229 this.isSystem = false;
230 this.connection = connection;
Daniel Sandler09a247e2013-02-14 10:24:17 -0500231 }
232
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400233 boolean enabledAndUserMatches(StatusBarNotification sbn) {
234 final int nid = sbn.getUserId();
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700235 if (!isEnabledForCurrentUser()) {
236 return false;
237 }
Daniel Sandler21ca44d2013-03-07 13:58:00 -0500238 if (this.userid == UserHandle.USER_ALL) return true;
Daniel Sandler21ca44d2013-03-07 13:58:00 -0500239 return (nid == UserHandle.USER_ALL || nid == this.userid);
240 }
241
Daniel Sandler09a247e2013-02-14 10:24:17 -0500242 public void notifyPostedIfUserMatch(StatusBarNotification sbn) {
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700243 if (!enabledAndUserMatches(sbn)) {
244 return;
245 }
Daniel Sandler09a247e2013-02-14 10:24:17 -0500246 try {
247 listener.onNotificationPosted(sbn);
248 } catch (RemoteException ex) {
Daniel Sandler1a497d32013-04-18 14:52:45 -0400249 Log.e(TAG, "unable to notify listener (posted): " + listener, ex);
Daniel Sandler09a247e2013-02-14 10:24:17 -0500250 }
251 }
252
253 public void notifyRemovedIfUserMatch(StatusBarNotification sbn) {
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400254 if (!enabledAndUserMatches(sbn)) return;
Daniel Sandler09a247e2013-02-14 10:24:17 -0500255 try {
256 listener.onNotificationRemoved(sbn);
257 } catch (RemoteException ex) {
Daniel Sandler1a497d32013-04-18 14:52:45 -0400258 Log.e(TAG, "unable to notify listener (removed): " + listener, ex);
Daniel Sandler09a247e2013-02-14 10:24:17 -0500259 }
260 }
261
262 @Override
263 public void binderDied() {
Will Haldean Brown866b2542014-02-28 11:04:41 -0800264 // Remove the listener, but don't unbind from the service. The system will bring the
265 // service back up, and the onServiceConnected handler will readd the listener with the
266 // new binding. If this isn't a bound service, and is just a registered
267 // INotificationListener, just removing it from the list is all we need to do anyway.
268 removeListenerImpl(this.listener, this.userid);
Daniel Sandler09a247e2013-02-14 10:24:17 -0500269 }
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400270
271 /** convenience method for looking in mEnabledListenersForCurrentUser */
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700272 public boolean isEnabledForCurrentUser() {
273 if (this.isSystem) return true;
274 if (this.connection == null) return false;
275 return mEnabledListenersForCurrentUser.contains(this.component);
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400276 }
Daniel Sandler09a247e2013-02-14 10:24:17 -0500277 }
278
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500279 private static class Archive {
Daniel Sandler5e62e3a2013-04-15 20:57:02 -0400280 static final int BUFFER_SIZE = 250;
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500281 ArrayDeque<StatusBarNotification> mBuffer = new ArrayDeque<StatusBarNotification>(BUFFER_SIZE);
282
283 public Archive() {
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500284 }
Jeff Sharkey0c1baf92013-04-03 13:08:52 -0700285
Daniel Sandler5e62e3a2013-04-15 20:57:02 -0400286 public String toString() {
287 final StringBuilder sb = new StringBuilder();
288 final int N = mBuffer.size();
289 sb.append("Archive (");
290 sb.append(N);
291 sb.append(" notification");
292 sb.append((N==1)?")":"s)");
293 return sb.toString();
294 }
295
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500296 public void record(StatusBarNotification nr) {
297 if (mBuffer.size() == BUFFER_SIZE) {
298 mBuffer.removeFirst();
299 }
Daniel Sandler26b81d52013-05-20 20:56:43 -0400300
301 // We don't want to store the heavy bits of the notification in the archive,
302 // but other clients in the system process might be using the object, so we
303 // store a (lightened) copy.
304 mBuffer.addLast(nr.cloneLight());
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500305 }
306
Daniel Sandler5e62e3a2013-04-15 20:57:02 -0400307
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500308 public void clear() {
309 mBuffer.clear();
310 }
311
312 public Iterator<StatusBarNotification> descendingIterator() {
313 return mBuffer.descendingIterator();
314 }
315 public Iterator<StatusBarNotification> ascendingIterator() {
316 return mBuffer.iterator();
317 }
318 public Iterator<StatusBarNotification> filter(
319 final Iterator<StatusBarNotification> iter, final String pkg, final int userId) {
320 return new Iterator<StatusBarNotification>() {
321 StatusBarNotification mNext = findNext();
322
323 private StatusBarNotification findNext() {
324 while (iter.hasNext()) {
325 StatusBarNotification nr = iter.next();
Daniel Sandler4f91efd2013-04-25 16:38:41 -0400326 if ((pkg == null || nr.getPackageName() == pkg)
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500327 && (userId == UserHandle.USER_ALL || nr.getUserId() == userId)) {
328 return nr;
329 }
330 }
331 return null;
332 }
333
334 @Override
335 public boolean hasNext() {
336 return mNext == null;
337 }
338
339 @Override
340 public StatusBarNotification next() {
341 StatusBarNotification next = mNext;
342 if (next == null) {
343 throw new NoSuchElementException();
344 }
345 mNext = findNext();
346 return next;
347 }
348
349 @Override
350 public void remove() {
351 iter.remove();
352 }
353 };
354 }
Daniel Sandler78d0d252013-02-12 08:14:52 -0500355
356 public StatusBarNotification[] getArray(int count) {
357 if (count == 0) count = Archive.BUFFER_SIZE;
358 final StatusBarNotification[] a
359 = new StatusBarNotification[Math.min(count, mBuffer.size())];
360 Iterator<StatusBarNotification> iter = descendingIterator();
361 int i=0;
362 while (iter.hasNext() && i < count) {
363 a[i++] = iter.next();
364 }
365 return a;
366 }
367
368 public StatusBarNotification[] getArray(int count, String pkg, int userId) {
369 if (count == 0) count = Archive.BUFFER_SIZE;
370 final StatusBarNotification[] a
371 = new StatusBarNotification[Math.min(count, mBuffer.size())];
372 Iterator<StatusBarNotification> iter = filter(descendingIterator(), pkg, userId);
373 int i=0;
374 while (iter.hasNext() && i < count) {
375 a[i++] = iter.next();
376 }
377 return a;
378 }
379
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500380 }
381
382 Archive mArchive = new Archive();
383
Daniel Sandler0da673f2012-04-11 12:33:16 -0400384 private void loadBlockDb() {
385 synchronized(mBlockedPackages) {
386 if (mPolicyFile == null) {
387 File dir = new File("/data/system");
388 mPolicyFile = new AtomicFile(new File(dir, "notification_policy.xml"));
389
390 mBlockedPackages.clear();
391
392 FileInputStream infile = null;
393 try {
394 infile = mPolicyFile.openRead();
395 final XmlPullParser parser = Xml.newPullParser();
396 parser.setInput(infile, null);
397
398 int type;
399 String tag;
400 int version = DB_VERSION;
401 while ((type = parser.next()) != END_DOCUMENT) {
402 tag = parser.getName();
403 if (type == START_TAG) {
404 if (TAG_BODY.equals(tag)) {
Adam Lesinski182f73f2013-12-05 16:48:06 -0800405 version = Integer.parseInt(
406 parser.getAttributeValue(null, ATTR_VERSION));
Daniel Sandler0da673f2012-04-11 12:33:16 -0400407 } else if (TAG_BLOCKED_PKGS.equals(tag)) {
408 while ((type = parser.next()) != END_DOCUMENT) {
409 tag = parser.getName();
410 if (TAG_PACKAGE.equals(tag)) {
Adam Lesinski182f73f2013-12-05 16:48:06 -0800411 mBlockedPackages.add(
412 parser.getAttributeValue(null, ATTR_NAME));
Daniel Sandler0da673f2012-04-11 12:33:16 -0400413 } else if (TAG_BLOCKED_PKGS.equals(tag) && type == END_TAG) {
414 break;
415 }
416 }
417 }
418 }
419 }
420 } catch (FileNotFoundException e) {
421 // No data yet
422 } catch (IOException e) {
423 Log.wtf(TAG, "Unable to read blocked notifications database", e);
424 } catch (NumberFormatException e) {
425 Log.wtf(TAG, "Unable to parse blocked notifications database", e);
426 } catch (XmlPullParserException e) {
427 Log.wtf(TAG, "Unable to parse blocked notifications database", e);
428 } finally {
429 IoUtils.closeQuietly(infile);
430 }
431 }
432 }
433 }
434
Daniel Sandler4a900ac2013-01-30 14:04:10 -0500435 /** Use this when you actually want to post a notification or toast.
436 *
437 * Unchecked. Not exposed via Binder, but can be called in the course of enqueue*().
438 */
439 private boolean noteNotificationOp(String pkg, int uid) {
440 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
441 != AppOpsManager.MODE_ALLOWED) {
442 Slog.v(TAG, "notifications are disabled by AppOps for " + pkg);
443 return false;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400444 }
Daniel Sandler4a900ac2013-01-30 14:04:10 -0500445 return true;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400446 }
447
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800448 private static String idDebugString(Context baseContext, String packageName, int id) {
449 Context c = null;
450
451 if (packageName != null) {
452 try {
453 c = baseContext.createPackageContext(packageName, 0);
454 } catch (NameNotFoundException e) {
455 c = baseContext;
456 }
457 } else {
458 c = baseContext;
459 }
460
461 String pkg;
462 String type;
463 String name;
464
465 Resources r = c.getResources();
466 try {
467 return r.getResourceName(id);
468 } catch (Resources.NotFoundException e) {
469 return "<name unknown>";
470 }
471 }
472
Daniel Sandler78d0d252013-02-12 08:14:52 -0500473
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700474 /**
Chris Wrenae9bb572013-05-15 14:50:28 -0400475 * Remove notification access for any services that no longer exist.
476 */
477 void disableNonexistentListeners() {
478 int currentUser = ActivityManager.getCurrentUser();
479 String flatIn = Settings.Secure.getStringForUser(
Adam Lesinski182f73f2013-12-05 16:48:06 -0800480 getContext().getContentResolver(),
Chris Wrenae9bb572013-05-15 14:50:28 -0400481 Settings.Secure.ENABLED_NOTIFICATION_LISTENERS,
482 currentUser);
483 if (!TextUtils.isEmpty(flatIn)) {
484 if (DBG) Slog.v(TAG, "flat before: " + flatIn);
Adam Lesinski182f73f2013-12-05 16:48:06 -0800485 PackageManager pm = getContext().getPackageManager();
Chris Wrenae9bb572013-05-15 14:50:28 -0400486 List<ResolveInfo> installedServices = pm.queryIntentServicesAsUser(
487 new Intent(NotificationListenerService.SERVICE_INTERFACE),
488 PackageManager.GET_SERVICES | PackageManager.GET_META_DATA,
489 currentUser);
490
491 Set<ComponentName> installed = new HashSet<ComponentName>();
492 for (int i = 0, count = installedServices.size(); i < count; i++) {
493 ResolveInfo resolveInfo = installedServices.get(i);
494 ServiceInfo info = resolveInfo.serviceInfo;
495
496 if (!android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE.equals(
497 info.permission)) {
498 Slog.w(TAG, "Skipping notification listener service "
499 + info.packageName + "/" + info.name
500 + ": it does not require the permission "
501 + android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE);
502 continue;
503 }
504 installed.add(new ComponentName(info.packageName, info.name));
505 }
506
507 String flatOut = "";
508 if (!installed.isEmpty()) {
509 String[] enabled = flatIn.split(ENABLED_NOTIFICATION_LISTENERS_SEPARATOR);
510 ArrayList<String> remaining = new ArrayList<String>(enabled.length);
511 for (int i = 0; i < enabled.length; i++) {
512 ComponentName enabledComponent = ComponentName.unflattenFromString(enabled[i]);
513 if (installed.contains(enabledComponent)) {
514 remaining.add(enabled[i]);
515 }
516 }
517 flatOut = TextUtils.join(ENABLED_NOTIFICATION_LISTENERS_SEPARATOR, remaining);
518 }
519 if (DBG) Slog.v(TAG, "flat after: " + flatOut);
520 if (!flatIn.equals(flatOut)) {
Adam Lesinski182f73f2013-12-05 16:48:06 -0800521 Settings.Secure.putStringForUser(getContext().getContentResolver(),
Chris Wrenae9bb572013-05-15 14:50:28 -0400522 Settings.Secure.ENABLED_NOTIFICATION_LISTENERS,
523 flatOut, currentUser);
524 }
525 }
526 }
527
528 /**
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700529 * Called whenever packages change, the user switches, or ENABLED_NOTIFICATION_LISTENERS
530 * is altered. (For example in response to USER_SWITCHED in our broadcast receiver)
531 */
532 void rebindListenerServices() {
John Spurlock3ec4e702013-05-16 13:42:38 -0400533 final int currentUser = ActivityManager.getCurrentUser();
534 String flat = Settings.Secure.getStringForUser(
Adam Lesinski182f73f2013-12-05 16:48:06 -0800535 getContext().getContentResolver(),
John Spurlock3ec4e702013-05-16 13:42:38 -0400536 Settings.Secure.ENABLED_NOTIFICATION_LISTENERS,
537 currentUser);
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700538
539 NotificationListenerInfo[] toRemove = new NotificationListenerInfo[mListeners.size()];
540 final ArrayList<ComponentName> toAdd;
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700541
542 synchronized (mNotificationList) {
543 // unbind and remove all existing listeners
544 toRemove = mListeners.toArray(toRemove);
545
546 toAdd = new ArrayList<ComponentName>();
547 final HashSet<ComponentName> newEnabled = new HashSet<ComponentName>();
548 final HashSet<String> newPackages = new HashSet<String>();
549
550 // decode the list of components
551 if (flat != null) {
552 String[] components = flat.split(ENABLED_NOTIFICATION_LISTENERS_SEPARATOR);
553 for (int i=0; i<components.length; i++) {
554 final ComponentName component
555 = ComponentName.unflattenFromString(components[i]);
556 if (component != null) {
557 newEnabled.add(component);
558 toAdd.add(component);
559 newPackages.add(component.getPackageName());
560 }
561 }
562
563 mEnabledListenersForCurrentUser = newEnabled;
564 mEnabledListenerPackageNames = newPackages;
565 }
566 }
567
568 for (NotificationListenerInfo info : toRemove) {
569 final ComponentName component = info.component;
570 final int oldUser = info.userid;
571 Slog.v(TAG, "disabling notification listener for user " + oldUser + ": " + component);
572 unregisterListenerService(component, info.userid);
573 }
574
575 final int N = toAdd.size();
576 for (int i=0; i<N; i++) {
577 final ComponentName component = toAdd.get(i);
578 Slog.v(TAG, "enabling notification listener for user " + currentUser + ": "
579 + component);
580 registerListenerService(component, currentUser);
581 }
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400582 }
583
Daniel Sandler09a247e2013-02-14 10:24:17 -0500584
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700585 /**
586 * Version of registerListener that takes the name of a
587 * {@link android.service.notification.NotificationListenerService} to bind to.
588 *
589 * This is the mechanism by which third parties may subscribe to notifications.
590 */
591 private void registerListenerService(final ComponentName name, final int userid) {
592 checkCallerIsSystem();
593
594 if (DBG) Slog.v(TAG, "registerListenerService: " + name + " u=" + userid);
595
596 synchronized (mNotificationList) {
597 final String servicesBindingTag = name.toString() + "/" + userid;
598 if (mServicesBinding.contains(servicesBindingTag)) {
599 // stop registering this thing already! we're working on it
600 return;
601 }
602 mServicesBinding.add(servicesBindingTag);
603
604 final int N = mListeners.size();
605 for (int i=N-1; i>=0; i--) {
606 final NotificationListenerInfo info = mListeners.get(i);
607 if (name.equals(info.component)
608 && info.userid == userid) {
609 // cut old connections
610 if (DBG) Slog.v(TAG, " disconnecting old listener: " + info.listener);
611 mListeners.remove(i);
612 if (info.connection != null) {
Adam Lesinski182f73f2013-12-05 16:48:06 -0800613 getContext().unbindService(info.connection);
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700614 }
615 }
616 }
617
618 Intent intent = new Intent(NotificationListenerService.SERVICE_INTERFACE);
619 intent.setComponent(name);
620
621 intent.putExtra(Intent.EXTRA_CLIENT_LABEL,
Scott Greenwald9a05b312013-06-28 00:37:54 -0400622 R.string.notification_listener_binding_label);
Adam Lesinski182f73f2013-12-05 16:48:06 -0800623
624 final PendingIntent pendingIntent = PendingIntent.getActivity(
625 getContext(), 0, new Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS), 0);
626 intent.putExtra(Intent.EXTRA_CLIENT_INTENT, pendingIntent);
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700627
628 try {
629 if (DBG) Slog.v(TAG, "binding: " + intent);
Adam Lesinski182f73f2013-12-05 16:48:06 -0800630 if (!getContext().bindServiceAsUser(intent,
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700631 new ServiceConnection() {
632 INotificationListener mListener;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800633
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700634 @Override
635 public void onServiceConnected(ComponentName name, IBinder service) {
John Spurlock5579ec32014-03-28 09:42:05 -0400636 boolean added = false;
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700637 synchronized (mNotificationList) {
638 mServicesBinding.remove(servicesBindingTag);
639 try {
640 mListener = INotificationListener.Stub.asInterface(service);
Adam Lesinski182f73f2013-12-05 16:48:06 -0800641 NotificationListenerInfo info
642 = new NotificationListenerInfo(
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700643 mListener, name, userid, this);
644 service.linkToDeath(info, 0);
John Spurlock5579ec32014-03-28 09:42:05 -0400645 added = mListeners.add(info);
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700646 } catch (RemoteException e) {
647 // already dead
648 }
649 }
John Spurlock5579ec32014-03-28 09:42:05 -0400650 if (added) {
651 final String[] keys =
652 getActiveNotificationKeysFromListener(mListener);
653 try {
654 mListener.onListenerConnected(keys);
655 } catch (RemoteException e) {
656 // we tried
657 }
658 }
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700659 }
660
661 @Override
662 public void onServiceDisconnected(ComponentName name) {
663 Slog.v(TAG, "notification listener connection lost: " + name);
664 }
665 },
666 Context.BIND_AUTO_CREATE,
667 new UserHandle(userid)))
668 {
669 mServicesBinding.remove(servicesBindingTag);
670 Slog.w(TAG, "Unable to bind listener service: " + intent);
671 return;
672 }
673 } catch (SecurityException ex) {
674 Slog.e(TAG, "Unable to bind listener service: " + intent, ex);
675 return;
676 }
677 }
678 }
679
Daniel Sandler09a247e2013-02-14 10:24:17 -0500680
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700681 /**
682 * Remove a listener service for the given user by ComponentName
683 */
684 private void unregisterListenerService(ComponentName name, int userid) {
685 checkCallerIsSystem();
686
687 synchronized (mNotificationList) {
688 final int N = mListeners.size();
689 for (int i=N-1; i>=0; i--) {
690 final NotificationListenerInfo info = mListeners.get(i);
691 if (name.equals(info.component)
692 && info.userid == userid) {
693 mListeners.remove(i);
694 if (info.connection != null) {
Daniel Sandlerc1b49bd2013-05-07 13:53:47 -0400695 try {
Adam Lesinski182f73f2013-12-05 16:48:06 -0800696 getContext().unbindService(info.connection);
Daniel Sandlerc1b49bd2013-05-07 13:53:47 -0400697 } catch (IllegalArgumentException ex) {
698 // something happened to the service: we think we have a connection
699 // but it's bogus.
700 Slog.e(TAG, "Listener " + name + " could not be unbound: " + ex);
701 }
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700702 }
703 }
704 }
705 }
706 }
707
708 /**
709 * asynchronously notify all listeners about a new notification
710 */
Adam Lesinski182f73f2013-12-05 16:48:06 -0800711 void notifyPostedLocked(NotificationRecord n) {
Daniel Sandler1a497d32013-04-18 14:52:45 -0400712 // make a copy in case changes are made to the underlying Notification object
713 final StatusBarNotification sbn = n.sbn.clone();
Daniel Sandler09a247e2013-02-14 10:24:17 -0500714 for (final NotificationListenerInfo info : mListeners) {
715 mHandler.post(new Runnable() {
716 @Override
717 public void run() {
718 info.notifyPostedIfUserMatch(sbn);
719 }});
720 }
721 }
722
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700723 /**
724 * asynchronously notify all listeners about a removed notification
725 */
Adam Lesinski182f73f2013-12-05 16:48:06 -0800726 void notifyRemovedLocked(NotificationRecord n) {
Daniel Sandler1a497d32013-04-18 14:52:45 -0400727 // make a copy in case changes are made to the underlying Notification object
728 // NOTE: this copy is lightweight: it doesn't include heavyweight parts of the notification
729 final StatusBarNotification sbn_light = n.sbn.cloneLight();
730
Daniel Sandler09a247e2013-02-14 10:24:17 -0500731 for (final NotificationListenerInfo info : mListeners) {
732 mHandler.post(new Runnable() {
733 @Override
734 public void run() {
Daniel Sandler1a497d32013-04-18 14:52:45 -0400735 info.notifyRemovedIfUserMatch(sbn_light);
Daniel Sandler09a247e2013-02-14 10:24:17 -0500736 }});
737 }
738 }
739
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700740 // -- APIs to support listeners clicking/clearing notifications --
741
John Spurlockc4aee982014-02-12 12:12:26 -0500742 private void checkNullListener(INotificationListener listener) {
743 if (listener == null) {
744 throw new IllegalArgumentException("Listener must not be null");
745 }
746 }
747
Adam Lesinskie8240262014-03-26 16:01:00 -0700748 private NotificationListenerInfo checkListenerTokenLocked(INotificationListener listener) {
John Spurlockc4aee982014-02-12 12:12:26 -0500749 checkNullListener(listener);
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700750 final IBinder token = listener.asBinder();
751 final int N = mListeners.size();
752 for (int i=0; i<N; i++) {
753 final NotificationListenerInfo info = mListeners.get(i);
754 if (info.listener.asBinder() == token) return info;
755 }
756 throw new SecurityException("Disallowed call from unknown listener: " + listener);
757 }
758
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700759
Daniel Sandler25cf8ce2013-04-24 15:34:57 -0400760
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700761 // -- end of listener APIs --
762
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500763 public static final class NotificationRecord
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800764 {
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500765 final StatusBarNotification sbn;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800766 IBinder statusBarKey;
767
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500768 NotificationRecord(StatusBarNotification sbn)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800769 {
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500770 this.sbn = sbn;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800771 }
Fred Quintana6ecaff12009-09-25 14:23:13 -0700772
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -0400773 public Notification getNotification() { return sbn.getNotification(); }
774 public int getFlags() { return sbn.getNotification().flags; }
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500775 public int getUserId() { return sbn.getUserId(); }
776
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800777 void dump(PrintWriter pw, String prefix, Context baseContext) {
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -0400778 final Notification notification = sbn.getNotification();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800779 pw.println(prefix + this);
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -0400780 pw.println(prefix + " uid=" + sbn.getUid() + " userId=" + sbn.getUserId());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800781 pw.println(prefix + " icon=0x" + Integer.toHexString(notification.icon)
Daniel Sandler4f91efd2013-04-25 16:38:41 -0400782 + " / " + idDebugString(baseContext, sbn.getPackageName(), notification.icon));
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -0400783 pw.println(prefix + " pri=" + notification.priority + " score=" + sbn.getScore());
John Spurlock5579ec32014-03-28 09:42:05 -0400784 pw.println(prefix + " key=" + sbn.getKey());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800785 pw.println(prefix + " contentIntent=" + notification.contentIntent);
786 pw.println(prefix + " deleteIntent=" + notification.deleteIntent);
787 pw.println(prefix + " tickerText=" + notification.tickerText);
788 pw.println(prefix + " contentView=" + notification.contentView);
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400789 pw.println(prefix + String.format(" defaults=0x%08x flags=0x%08x",
790 notification.defaults, notification.flags));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800791 pw.println(prefix + " sound=" + notification.sound);
792 pw.println(prefix + " vibrate=" + Arrays.toString(notification.vibrate));
Daniel Sandlerf45564e2013-04-15 15:05:08 -0400793 pw.println(prefix + String.format(" led=0x%08x onMs=%d offMs=%d",
794 notification.ledARGB, notification.ledOnMS, notification.ledOffMS));
795 if (notification.actions != null && notification.actions.length > 0) {
796 pw.println(prefix + " actions={");
797 final int N = notification.actions.length;
798 for (int i=0; i<N; i++) {
799 final Notification.Action action = notification.actions[i];
800 pw.println(String.format("%s [%d] \"%s\" -> %s",
801 prefix,
802 i,
803 action.title,
804 action.actionIntent.toString()
805 ));
806 }
807 pw.println(prefix + " }");
808 }
809 if (notification.extras != null && notification.extras.size() > 0) {
810 pw.println(prefix + " extras={");
811 for (String key : notification.extras.keySet()) {
812 pw.print(prefix + " " + key + "=");
813 Object val = notification.extras.get(key);
814 if (val == null) {
815 pw.println("null");
816 } else {
817 pw.print(val.toString());
818 if (val instanceof Bitmap) {
819 pw.print(String.format(" (%dx%d)",
820 ((Bitmap) val).getWidth(),
821 ((Bitmap) val).getHeight()));
822 } else if (val.getClass().isArray()) {
823 pw.println(" {");
824 final int N = Array.getLength(val);
825 for (int i=0; i<N; i++) {
826 if (i > 0) pw.println(",");
827 pw.print(prefix + " " + Array.get(val, i));
828 }
829 pw.print("\n" + prefix + " }");
830 }
831 pw.println();
832 }
833 }
834 pw.println(prefix + " }");
835 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800836 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800837
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800838 @Override
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500839 public final String toString() {
840 return String.format(
John Spurlock5579ec32014-03-28 09:42:05 -0400841 "NotificationRecord(0x%08x: pkg=%s user=%s id=%d tag=%s score=%d key=%s: %s)",
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500842 System.identityHashCode(this),
Adam Lesinski182f73f2013-12-05 16:48:06 -0800843 this.sbn.getPackageName(), this.sbn.getUser(), this.sbn.getId(),
John Spurlock5579ec32014-03-28 09:42:05 -0400844 this.sbn.getTag(), this.sbn.getScore(), this.sbn.getKey(),
845 this.sbn.getNotification());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800846 }
847 }
848
849 private static final class ToastRecord
850 {
851 final int pid;
852 final String pkg;
853 final ITransientNotification callback;
854 int duration;
855
856 ToastRecord(int pid, String pkg, ITransientNotification callback, int duration)
857 {
858 this.pid = pid;
859 this.pkg = pkg;
860 this.callback = callback;
861 this.duration = duration;
862 }
863
864 void update(int duration) {
865 this.duration = duration;
866 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800867
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800868 void dump(PrintWriter pw, String prefix) {
869 pw.println(prefix + this);
870 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800871
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800872 @Override
873 public final String toString()
874 {
875 return "ToastRecord{"
876 + Integer.toHexString(System.identityHashCode(this))
877 + " pkg=" + pkg
878 + " callback=" + callback
879 + " duration=" + duration;
880 }
881 }
882
Adam Lesinski182f73f2013-12-05 16:48:06 -0800883 private final NotificationDelegate mNotificationDelegate = new NotificationDelegate() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800884
Adam Lesinski182f73f2013-12-05 16:48:06 -0800885 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800886 public void onSetDisabled(int status) {
887 synchronized (mNotificationList) {
888 mDisabledNotifications = status;
889 if ((mDisabledNotifications & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) {
890 // cancel whatever's going on
891 long identity = Binder.clearCallingIdentity();
892 try {
Adam Lesinski182f73f2013-12-05 16:48:06 -0800893 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
Jeff Sharkey098d5802012-04-26 17:30:34 -0700894 if (player != null) {
895 player.stopAsync();
896 }
897 } catch (RemoteException e) {
898 } finally {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800899 Binder.restoreCallingIdentity(identity);
900 }
901
902 identity = Binder.clearCallingIdentity();
903 try {
904 mVibrator.cancel();
Jeff Sharkey098d5802012-04-26 17:30:34 -0700905 } finally {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800906 Binder.restoreCallingIdentity(identity);
907 }
908 }
909 }
910 }
911
Adam Lesinski182f73f2013-12-05 16:48:06 -0800912 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800913 public void onClearAll() {
Dianne Hackborn41203752012-08-31 14:05:51 -0700914 // XXX to be totally correct, the caller should tell us which user
915 // this is for.
Adam Lesinskie8240262014-03-26 16:01:00 -0700916 int currentUser = ActivityManager.getCurrentUser();
917 synchronized (mNotificationList) {
918 cancelAllLocked(currentUser);
919 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800920 }
921
Adam Lesinski182f73f2013-12-05 16:48:06 -0800922 @Override
Fred Quintana6ecaff12009-09-25 14:23:13 -0700923 public void onNotificationClick(String pkg, String tag, int id) {
Dianne Hackborn41203752012-08-31 14:05:51 -0700924 // XXX to be totally correct, the caller should tell us which user
925 // this is for.
Fred Quintana6ecaff12009-09-25 14:23:13 -0700926 cancelNotification(pkg, tag, id, Notification.FLAG_AUTO_CANCEL,
Dianne Hackborn41203752012-08-31 14:05:51 -0700927 Notification.FLAG_FOREGROUND_SERVICE, false,
928 ActivityManager.getCurrentUser());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800929 }
930
Adam Lesinski182f73f2013-12-05 16:48:06 -0800931 @Override
Daniel Sandler0f0b11c2010-08-04 15:54:58 -0400932 public void onNotificationClear(String pkg, String tag, int id) {
Dianne Hackborn41203752012-08-31 14:05:51 -0700933 // XXX to be totally correct, the caller should tell us which user
934 // this is for.
Joe Onorato46439ce2010-11-19 13:56:21 -0800935 cancelNotification(pkg, tag, id, 0,
936 Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
Dianne Hackborn41203752012-08-31 14:05:51 -0700937 true, ActivityManager.getCurrentUser());
Daniel Sandler0f0b11c2010-08-04 15:54:58 -0400938 }
939
Adam Lesinski182f73f2013-12-05 16:48:06 -0800940 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800941 public void onPanelRevealed() {
942 synchronized (mNotificationList) {
943 // sound
944 mSoundNotification = null;
Jeff Sharkey098d5802012-04-26 17:30:34 -0700945
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800946 long identity = Binder.clearCallingIdentity();
947 try {
Adam Lesinski182f73f2013-12-05 16:48:06 -0800948 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
Jeff Sharkey098d5802012-04-26 17:30:34 -0700949 if (player != null) {
950 player.stopAsync();
951 }
952 } catch (RemoteException e) {
953 } finally {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800954 Binder.restoreCallingIdentity(identity);
955 }
956
957 // vibrate
958 mVibrateNotification = null;
959 identity = Binder.clearCallingIdentity();
960 try {
961 mVibrator.cancel();
Jeff Sharkey098d5802012-04-26 17:30:34 -0700962 } finally {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800963 Binder.restoreCallingIdentity(identity);
964 }
965
966 // light
967 mLights.clear();
968 mLedNotification = null;
969 updateLightsLocked();
970 }
971 }
Joe Onorato005847b2010-06-04 16:08:02 -0400972
Adam Lesinski182f73f2013-12-05 16:48:06 -0800973 @Override
Dianne Hackborn9d39d0c2010-06-24 15:57:42 -0700974 public void onNotificationError(String pkg, String tag, int id,
975 int uid, int initialPid, String message) {
Daniel Sandlerd0a2f862010-08-03 15:29:31 -0400976 Slog.d(TAG, "onNotification error pkg=" + pkg + " tag=" + tag + " id=" + id
977 + "; will crashApplication(uid=" + uid + ", pid=" + initialPid + ")");
Dianne Hackborn41203752012-08-31 14:05:51 -0700978 // XXX to be totally correct, the caller should tell us which user
979 // this is for.
980 cancelNotification(pkg, tag, id, 0, 0, false, UserHandle.getUserId(uid));
Dianne Hackborn9d39d0c2010-06-24 15:57:42 -0700981 long ident = Binder.clearCallingIdentity();
982 try {
983 ActivityManagerNative.getDefault().crashApplication(uid, initialPid, pkg,
984 "Bad notification posted from package " + pkg
985 + ": " + message);
986 } catch (RemoteException e) {
987 }
988 Binder.restoreCallingIdentity(ident);
Joe Onorato005847b2010-06-04 16:08:02 -0400989 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800990 };
991
992 private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
993 @Override
994 public void onReceive(Context context, Intent intent) {
995 String action = intent.getAction();
996
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800997 boolean queryRestart = false;
Chris Wrenae9bb572013-05-15 14:50:28 -0400998 boolean queryRemove = false;
Daniel Sandler26ece572012-06-01 15:38:46 -0400999 boolean packageChanged = false;
John Spurlock79f78922013-05-16 09:10:05 -04001000 boolean cancelNotifications = true;
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08001001
Chris Wren3da73022013-05-10 14:41:21 -04001002 if (action.equals(Intent.ACTION_PACKAGE_ADDED)
Chris Wrenae9bb572013-05-15 14:50:28 -04001003 || (queryRemove=action.equals(Intent.ACTION_PACKAGE_REMOVED))
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001004 || action.equals(Intent.ACTION_PACKAGE_RESTARTED)
Daniel Sandler26ece572012-06-01 15:38:46 -04001005 || (packageChanged=action.equals(Intent.ACTION_PACKAGE_CHANGED))
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08001006 || (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART))
Suchi Amalapurapub56ae202010-02-04 22:51:07 -08001007 || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001008 String pkgList[] = null;
Chris Wrenae9bb572013-05-15 14:50:28 -04001009 boolean queryReplace = queryRemove &&
1010 intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
1011 if (DBG) Slog.i(TAG, "queryReplace=" + queryReplace);
Suchi Amalapurapub56ae202010-02-04 22:51:07 -08001012 if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001013 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08001014 } else if (queryRestart) {
1015 pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001016 } else {
1017 Uri uri = intent.getData();
1018 if (uri == null) {
1019 return;
1020 }
1021 String pkgName = uri.getSchemeSpecificPart();
1022 if (pkgName == null) {
1023 return;
1024 }
Daniel Sandler26ece572012-06-01 15:38:46 -04001025 if (packageChanged) {
1026 // We cancel notifications for packages which have just been disabled
Christopher Tate06e5fed2013-10-09 14:39:15 -07001027 try {
Adam Lesinski182f73f2013-12-05 16:48:06 -08001028 final int enabled = getContext().getPackageManager()
Christopher Tate06e5fed2013-10-09 14:39:15 -07001029 .getApplicationEnabledSetting(pkgName);
1030 if (enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED
1031 || enabled == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
1032 cancelNotifications = false;
1033 }
1034 } catch (IllegalArgumentException e) {
1035 // Package doesn't exist; probably racing with uninstall.
1036 // cancelNotifications is already true, so nothing to do here.
1037 if (DBG) {
1038 Slog.i(TAG, "Exception trying to look up app enabled setting", e);
1039 }
Daniel Sandler26ece572012-06-01 15:38:46 -04001040 }
1041 }
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001042 pkgList = new String[]{pkgName};
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001043 }
Daniel Sandler5feceeb2013-03-22 18:29:23 -07001044
1045 boolean anyListenersInvolved = false;
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001046 if (pkgList != null && (pkgList.length > 0)) {
1047 for (String pkgName : pkgList) {
John Spurlock79f78922013-05-16 09:10:05 -04001048 if (cancelNotifications) {
1049 cancelAllNotificationsInt(pkgName, 0, 0, !queryRestart,
1050 UserHandle.USER_ALL);
1051 }
Daniel Sandler5feceeb2013-03-22 18:29:23 -07001052 if (mEnabledListenerPackageNames.contains(pkgName)) {
1053 anyListenersInvolved = true;
1054 }
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001055 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001056 }
Daniel Sandler5feceeb2013-03-22 18:29:23 -07001057
1058 if (anyListenersInvolved) {
Chris Wrenae9bb572013-05-15 14:50:28 -04001059 // if we're not replacing a package, clean up orphaned bits
1060 if (!queryReplace) {
1061 disableNonexistentListeners();
1062 }
Daniel Sandler5feceeb2013-03-22 18:29:23 -07001063 // make sure we're still bound to any of our
1064 // listeners who may have just upgraded
1065 rebindListenerServices();
1066 }
Mike Lockwood63b5ad92011-08-30 09:55:30 -04001067 } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
1068 // Keep track of screen on/off state, but do not turn off the notification light
1069 // until user passes through the lock screen or views the notification.
1070 mScreenOn = true;
1071 } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
1072 mScreenOn = false;
Daniel Sandlere96ffb12010-03-11 13:38:06 -05001073 } else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) {
Mike Lockwood63b5ad92011-08-30 09:55:30 -04001074 mInCall = (intent.getStringExtra(TelephonyManager.EXTRA_STATE).equals(
1075 TelephonyManager.EXTRA_STATE_OFFHOOK));
Daniel Sandlere96ffb12010-03-11 13:38:06 -05001076 updateNotificationPulse();
Dianne Hackborn80a4af22012-08-27 19:18:31 -07001077 } else if (action.equals(Intent.ACTION_USER_STOPPED)) {
1078 int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
1079 if (userHandle >= 0) {
Dianne Hackborn41203752012-08-31 14:05:51 -07001080 cancelAllNotificationsInt(null, 0, 0, true, userHandle);
Dianne Hackborn80a4af22012-08-27 19:18:31 -07001081 }
Mike Lockwood63b5ad92011-08-30 09:55:30 -04001082 } else if (action.equals(Intent.ACTION_USER_PRESENT)) {
1083 // turn off LED when user passes through lock screen
1084 mNotificationLight.turnOff();
Daniel Sandler4b749ef2013-03-18 21:53:04 -04001085 } else if (action.equals(Intent.ACTION_USER_SWITCHED)) {
1086 // reload per-user settings
1087 mSettingsObserver.update(null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001088 }
1089 }
1090 };
1091
Dianne Hackborn1dac2772009-06-26 18:16:48 -07001092 class SettingsObserver extends ContentObserver {
Daniel Sandler4b749ef2013-03-18 21:53:04 -04001093 private final Uri NOTIFICATION_LIGHT_PULSE_URI
1094 = Settings.System.getUriFor(Settings.System.NOTIFICATION_LIGHT_PULSE);
1095
1096 private final Uri ENABLED_NOTIFICATION_LISTENERS_URI
Daniel Sandler5feceeb2013-03-22 18:29:23 -07001097 = Settings.Secure.getUriFor(Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
Daniel Sandler4b749ef2013-03-18 21:53:04 -04001098
Dianne Hackborn1dac2772009-06-26 18:16:48 -07001099 SettingsObserver(Handler handler) {
1100 super(handler);
1101 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001102
Dianne Hackborn1dac2772009-06-26 18:16:48 -07001103 void observe() {
Adam Lesinski182f73f2013-12-05 16:48:06 -08001104 ContentResolver resolver = getContext().getContentResolver();
Daniel Sandler4b749ef2013-03-18 21:53:04 -04001105 resolver.registerContentObserver(NOTIFICATION_LIGHT_PULSE_URI,
Daniel Sandler5feceeb2013-03-22 18:29:23 -07001106 false, this, UserHandle.USER_ALL);
Daniel Sandler4b749ef2013-03-18 21:53:04 -04001107 resolver.registerContentObserver(ENABLED_NOTIFICATION_LISTENERS_URI,
Daniel Sandler5feceeb2013-03-22 18:29:23 -07001108 false, this, UserHandle.USER_ALL);
Daniel Sandler4b749ef2013-03-18 21:53:04 -04001109 update(null);
Dianne Hackborn1dac2772009-06-26 18:16:48 -07001110 }
1111
Daniel Sandler4b749ef2013-03-18 21:53:04 -04001112 @Override public void onChange(boolean selfChange, Uri uri) {
1113 update(uri);
Dianne Hackborn1dac2772009-06-26 18:16:48 -07001114 }
1115
Daniel Sandler4b749ef2013-03-18 21:53:04 -04001116 public void update(Uri uri) {
Adam Lesinski182f73f2013-12-05 16:48:06 -08001117 ContentResolver resolver = getContext().getContentResolver();
Daniel Sandler4b749ef2013-03-18 21:53:04 -04001118 if (uri == null || NOTIFICATION_LIGHT_PULSE_URI.equals(uri)) {
1119 boolean pulseEnabled = Settings.System.getInt(resolver,
1120 Settings.System.NOTIFICATION_LIGHT_PULSE, 0) != 0;
1121 if (mNotificationPulseEnabled != pulseEnabled) {
1122 mNotificationPulseEnabled = pulseEnabled;
1123 updateNotificationPulse();
1124 }
1125 }
1126 if (uri == null || ENABLED_NOTIFICATION_LISTENERS_URI.equals(uri)) {
Daniel Sandler5feceeb2013-03-22 18:29:23 -07001127 rebindListenerServices();
Mike Lockwoodc22404a2009-12-02 11:15:02 -05001128 }
Dianne Hackborn1dac2772009-06-26 18:16:48 -07001129 }
1130 }
Mike Lockwoodc22404a2009-12-02 11:15:02 -05001131
Daniel Sandler4b749ef2013-03-18 21:53:04 -04001132 private SettingsObserver mSettingsObserver;
1133
Daniel Sandleredbb3802012-11-13 20:49:47 -08001134 static long[] getLongArray(Resources r, int resid, int maxlen, long[] def) {
1135 int[] ar = r.getIntArray(resid);
1136 if (ar == null) {
1137 return def;
1138 }
1139 final int len = ar.length > maxlen ? maxlen : ar.length;
1140 long[] out = new long[len];
1141 for (int i=0; i<len; i++) {
1142 out[i] = ar[i];
1143 }
1144 return out;
1145 }
1146
Jeff Brownb880d882014-02-10 19:47:07 -08001147 public NotificationManagerService(Context context) {
1148 super(context);
1149 }
1150
Adam Lesinski182f73f2013-12-05 16:48:06 -08001151 @Override
1152 public void onStart() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001153 mAm = ActivityManagerNative.getDefault();
Adam Lesinski182f73f2013-12-05 16:48:06 -08001154 mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
1155 mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
San Mehat3ee13172010-02-04 20:54:43 -08001156
Adam Lesinski182f73f2013-12-05 16:48:06 -08001157 mHandler = new WorkerHandler();
Daniel Sandler4a900ac2013-01-30 14:04:10 -05001158
1159 importOldBlockDb();
Daniel Sandler0da673f2012-04-11 12:33:16 -04001160
Adam Lesinski182f73f2013-12-05 16:48:06 -08001161 mStatusBar = getLocalService(StatusBarManagerInternal.class);
1162 mStatusBar.setNotificationDelegate(mNotificationDelegate);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001163
Adam Lesinski182f73f2013-12-05 16:48:06 -08001164 final LightsManager lights = getLocalService(LightsManager.class);
1165 mNotificationLight = lights.getLight(LightsManager.LIGHT_ID_NOTIFICATIONS);
1166 mAttentionLight = lights.getLight(LightsManager.LIGHT_ID_ATTENTION);
Mike Lockwood3cb67a32009-11-27 14:25:58 -05001167
Adam Lesinski182f73f2013-12-05 16:48:06 -08001168 Resources resources = getContext().getResources();
Mike Lockwood670f9322010-01-20 12:13:36 -05001169 mDefaultNotificationColor = resources.getColor(
Scott Greenwald9a05b312013-06-28 00:37:54 -04001170 R.color.config_defaultNotificationColor);
Mike Lockwood670f9322010-01-20 12:13:36 -05001171 mDefaultNotificationLedOn = resources.getInteger(
Scott Greenwald9a05b312013-06-28 00:37:54 -04001172 R.integer.config_defaultNotificationLedOn);
Mike Lockwood670f9322010-01-20 12:13:36 -05001173 mDefaultNotificationLedOff = resources.getInteger(
Scott Greenwald9a05b312013-06-28 00:37:54 -04001174 R.integer.config_defaultNotificationLedOff);
Mike Lockwood670f9322010-01-20 12:13:36 -05001175
Daniel Sandleredbb3802012-11-13 20:49:47 -08001176 mDefaultVibrationPattern = getLongArray(resources,
Scott Greenwald9a05b312013-06-28 00:37:54 -04001177 R.array.config_defaultNotificationVibePattern,
Daniel Sandleredbb3802012-11-13 20:49:47 -08001178 VIBRATE_PATTERN_MAXLEN,
1179 DEFAULT_VIBRATE_PATTERN);
1180
1181 mFallbackVibrationPattern = getLongArray(resources,
Scott Greenwald9a05b312013-06-28 00:37:54 -04001182 R.array.config_notificationFallbackVibePattern,
Daniel Sandleredbb3802012-11-13 20:49:47 -08001183 VIBRATE_PATTERN_MAXLEN,
1184 DEFAULT_VIBRATE_PATTERN);
1185
Joe Onorato39f5b6a2009-07-23 12:29:19 -04001186 // Don't start allowing notifications until the setup wizard has run once.
1187 // After that, including subsequent boots, init with notifications turned on.
1188 // This works on the first boot because the setup wizard will toggle this
1189 // flag at least once and we'll go back to 0 after that.
Adam Lesinski182f73f2013-12-05 16:48:06 -08001190 if (0 == Settings.Global.getInt(getContext().getContentResolver(),
Jeff Brownbf6f6f92012-09-25 15:03:20 -07001191 Settings.Global.DEVICE_PROVISIONED, 0)) {
Joe Onorato39f5b6a2009-07-23 12:29:19 -04001192 mDisabledNotifications = StatusBarManager.DISABLE_NOTIFICATION_ALERTS;
1193 }
1194
Mike Lockwood35e16bf2010-11-30 19:53:36 -05001195 // register for various Intents
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001196 IntentFilter filter = new IntentFilter();
Mike Lockwoodc22404a2009-12-02 11:15:02 -05001197 filter.addAction(Intent.ACTION_SCREEN_ON);
1198 filter.addAction(Intent.ACTION_SCREEN_OFF);
Daniel Sandlere96ffb12010-03-11 13:38:06 -05001199 filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
Mike Lockwood63b5ad92011-08-30 09:55:30 -04001200 filter.addAction(Intent.ACTION_USER_PRESENT);
Dianne Hackborn80a4af22012-08-27 19:18:31 -07001201 filter.addAction(Intent.ACTION_USER_STOPPED);
Daniel Sandler4b749ef2013-03-18 21:53:04 -04001202 filter.addAction(Intent.ACTION_USER_SWITCHED);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001203 getContext().registerReceiver(mIntentReceiver, filter);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08001204 IntentFilter pkgFilter = new IntentFilter();
Chris Wren3da73022013-05-10 14:41:21 -04001205 pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08001206 pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
Daniel Sandleraac0eb02011-08-06 22:51:56 -04001207 pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08001208 pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
1209 pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
1210 pkgFilter.addDataScheme("package");
Adam Lesinski182f73f2013-12-05 16:48:06 -08001211 getContext().registerReceiver(mIntentReceiver, pkgFilter);
Suchi Amalapurapub56ae202010-02-04 22:51:07 -08001212 IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001213 getContext().registerReceiver(mIntentReceiver, sdFilter);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001214
Daniel Sandler4b749ef2013-03-18 21:53:04 -04001215 mSettingsObserver = new SettingsObserver(mHandler);
Scott Greenwald9a05b312013-06-28 00:37:54 -04001216
1217 // spin up NotificationScorers
1218 String[] notificationScorerNames = resources.getStringArray(
1219 R.array.config_notificationScorers);
1220 for (String scorerName : notificationScorerNames) {
1221 try {
Adam Lesinski182f73f2013-12-05 16:48:06 -08001222 Class<?> scorerClass = getContext().getClassLoader().loadClass(scorerName);
Scott Greenwald9a05b312013-06-28 00:37:54 -04001223 NotificationScorer scorer = (NotificationScorer) scorerClass.newInstance();
Adam Lesinski182f73f2013-12-05 16:48:06 -08001224 scorer.initialize(getContext());
Scott Greenwald9a05b312013-06-28 00:37:54 -04001225 mScorers.add(scorer);
1226 } catch (ClassNotFoundException e) {
1227 Slog.w(TAG, "Couldn't find scorer " + scorerName + ".", e);
1228 } catch (InstantiationException e) {
1229 Slog.w(TAG, "Couldn't instantiate scorer " + scorerName + ".", e);
1230 } catch (IllegalAccessException e) {
1231 Slog.w(TAG, "Problem accessing scorer " + scorerName + ".", e);
1232 }
1233 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001234
1235 publishBinderService(Context.NOTIFICATION_SERVICE, mService);
1236 publishLocalService(NotificationManagerInternal.class, mInternalService);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001237 }
1238
Daniel Sandler4a900ac2013-01-30 14:04:10 -05001239 /**
1240 * Read the old XML-based app block database and import those blockages into the AppOps system.
1241 */
1242 private void importOldBlockDb() {
1243 loadBlockDb();
1244
Adam Lesinski182f73f2013-12-05 16:48:06 -08001245 PackageManager pm = getContext().getPackageManager();
Daniel Sandler4a900ac2013-01-30 14:04:10 -05001246 for (String pkg : mBlockedPackages) {
1247 PackageInfo info = null;
1248 try {
1249 info = pm.getPackageInfo(pkg, 0);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001250 setNotificationsEnabledForPackageImpl(pkg, info.applicationInfo.uid, false);
Daniel Sandler4a900ac2013-01-30 14:04:10 -05001251 } catch (NameNotFoundException e) {
1252 // forget you
1253 }
1254 }
1255 mBlockedPackages.clear();
1256 if (mPolicyFile != null) {
1257 mPolicyFile.delete();
1258 }
1259 }
1260
Adam Lesinski182f73f2013-12-05 16:48:06 -08001261 @Override
1262 public void onBootPhase(int phase) {
1263 if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
1264 // no beeping until we're basically done booting
1265 mSystemReady = true;
Jeff Sharkey098d5802012-04-26 17:30:34 -07001266
Adam Lesinski182f73f2013-12-05 16:48:06 -08001267 // Grab our optional AudioService
1268 mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
Daniel Sandler5feceeb2013-03-22 18:29:23 -07001269
Adam Lesinskia6db4ab2014-03-24 12:31:45 -07001270 } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
1271 // This observer will force an update when observe is called, causing us to
1272 // bind to listener services.
1273 mSettingsObserver.observe();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001274 }
1275 }
1276
Adam Lesinski182f73f2013-12-05 16:48:06 -08001277 void setNotificationsEnabledForPackageImpl(String pkg, int uid, boolean enabled) {
1278 Slog.v(TAG, (enabled?"en":"dis") + "abling notifications for " + pkg);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001279
Adam Lesinski182f73f2013-12-05 16:48:06 -08001280 mAppOps.setMode(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg,
1281 enabled ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001282
Adam Lesinski182f73f2013-12-05 16:48:06 -08001283 // Now, cancel any outstanding notifications that are part of a just-disabled app
1284 if (ENABLE_BLOCKED_NOTIFICATIONS && !enabled) {
1285 cancelAllNotificationsInt(pkg, 0, 0, true, UserHandle.getUserId(uid));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001286 }
1287 }
1288
Adam Lesinski182f73f2013-12-05 16:48:06 -08001289 private final IBinder mService = new INotificationManager.Stub() {
1290 // Toasts
1291 // ============================================================================
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001292
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001293 @Override
Adam Lesinski182f73f2013-12-05 16:48:06 -08001294 public void enqueueToast(String pkg, ITransientNotification callback, int duration)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001295 {
Adam Lesinski182f73f2013-12-05 16:48:06 -08001296 if (DBG) {
1297 Slog.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback
1298 + " duration=" + duration);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001299 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001300
1301 if (pkg == null || callback == null) {
1302 Slog.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback);
1303 return ;
1304 }
1305
1306 final boolean isSystemToast = isCallerSystem() || ("android".equals(pkg));
1307
1308 if (ENABLE_BLOCKED_TOASTS && !noteNotificationOp(pkg, Binder.getCallingUid())) {
1309 if (!isSystemToast) {
1310 Slog.e(TAG, "Suppressing toast from package " + pkg + " by user request.");
1311 return;
1312 }
1313 }
1314
1315 synchronized (mToastQueue) {
1316 int callingPid = Binder.getCallingPid();
1317 long callingId = Binder.clearCallingIdentity();
1318 try {
1319 ToastRecord record;
1320 int index = indexOfToastLocked(pkg, callback);
1321 // If it's already in the queue, we update it in place, we don't
1322 // move it to the end of the queue.
1323 if (index >= 0) {
1324 record = mToastQueue.get(index);
1325 record.update(duration);
1326 } else {
1327 // Limit the number of toasts that any given package except the android
1328 // package can enqueue. Prevents DOS attacks and deals with leaks.
1329 if (!isSystemToast) {
1330 int count = 0;
1331 final int N = mToastQueue.size();
1332 for (int i=0; i<N; i++) {
1333 final ToastRecord r = mToastQueue.get(i);
1334 if (r.pkg.equals(pkg)) {
1335 count++;
1336 if (count >= MAX_PACKAGE_NOTIFICATIONS) {
1337 Slog.e(TAG, "Package has already posted " + count
1338 + " toasts. Not showing more. Package=" + pkg);
1339 return;
1340 }
1341 }
1342 }
1343 }
1344
1345 record = new ToastRecord(callingPid, pkg, callback, duration);
1346 mToastQueue.add(record);
1347 index = mToastQueue.size() - 1;
1348 keepProcessAliveLocked(callingPid);
1349 }
1350 // If it's at index 0, it's the current toast. It doesn't matter if it's
1351 // new or just been updated. Call back and tell it to show itself.
1352 // If the callback fails, this will remove it from the list, so don't
1353 // assume that it's valid after this.
1354 if (index == 0) {
1355 showNextToastLocked();
1356 }
1357 } finally {
1358 Binder.restoreCallingIdentity(callingId);
1359 }
1360 }
1361 }
1362
1363 @Override
1364 public void cancelToast(String pkg, ITransientNotification callback) {
1365 Slog.i(TAG, "cancelToast pkg=" + pkg + " callback=" + callback);
1366
1367 if (pkg == null || callback == null) {
1368 Slog.e(TAG, "Not cancelling notification. pkg=" + pkg + " callback=" + callback);
1369 return ;
1370 }
1371
1372 synchronized (mToastQueue) {
1373 long callingId = Binder.clearCallingIdentity();
1374 try {
1375 int index = indexOfToastLocked(pkg, callback);
1376 if (index >= 0) {
1377 cancelToastLocked(index);
1378 } else {
1379 Slog.w(TAG, "Toast already cancelled. pkg=" + pkg
1380 + " callback=" + callback);
1381 }
1382 } finally {
1383 Binder.restoreCallingIdentity(callingId);
1384 }
1385 }
1386 }
1387
1388 @Override
1389 public void enqueueNotificationWithTag(String pkg, String basePkg, String tag, int id,
1390 Notification notification, int[] idOut, int userId) throws RemoteException {
1391 enqueueNotificationInternal(pkg, basePkg, Binder.getCallingUid(),
1392 Binder.getCallingPid(), tag, id, notification, idOut, userId);
1393 }
1394
1395 @Override
1396 public void cancelNotificationWithTag(String pkg, String tag, int id, int userId) {
1397 checkCallerIsSystemOrSameApp(pkg);
1398 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1399 Binder.getCallingUid(), userId, true, false, "cancelNotificationWithTag", pkg);
1400 // Don't allow client applications to cancel foreground service notis.
1401 cancelNotification(pkg, tag, id, 0,
1402 Binder.getCallingUid() == Process.SYSTEM_UID
1403 ? 0 : Notification.FLAG_FOREGROUND_SERVICE, false, userId);
1404 }
1405
1406 @Override
1407 public void cancelAllNotifications(String pkg, int userId) {
1408 checkCallerIsSystemOrSameApp(pkg);
1409
1410 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1411 Binder.getCallingUid(), userId, true, false, "cancelAllNotifications", pkg);
1412
1413 // Calling from user space, don't allow the canceling of actively
1414 // running foreground services.
1415 cancelAllNotificationsInt(pkg, 0, Notification.FLAG_FOREGROUND_SERVICE, true, userId);
1416 }
1417
1418 @Override
1419 public void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) {
1420 checkCallerIsSystem();
1421
1422 setNotificationsEnabledForPackageImpl(pkg, uid, enabled);
1423 }
1424
1425 /**
1426 * Use this when you just want to know if notifications are OK for this package.
1427 */
1428 @Override
1429 public boolean areNotificationsEnabledForPackage(String pkg, int uid) {
1430 checkCallerIsSystem();
1431 return (mAppOps.checkOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
1432 == AppOpsManager.MODE_ALLOWED);
1433 }
1434
1435 /**
1436 * System-only API for getting a list of current (i.e. not cleared) notifications.
1437 *
1438 * Requires ACCESS_NOTIFICATIONS which is signature|system.
1439 */
1440 @Override
1441 public StatusBarNotification[] getActiveNotifications(String callingPkg) {
1442 // enforce() will ensure the calling uid has the correct permission
1443 getContext().enforceCallingOrSelfPermission(
1444 android.Manifest.permission.ACCESS_NOTIFICATIONS,
1445 "NotificationManagerService.getActiveNotifications");
1446
1447 StatusBarNotification[] tmp = null;
1448 int uid = Binder.getCallingUid();
1449
1450 // noteOp will check to make sure the callingPkg matches the uid
1451 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
1452 == AppOpsManager.MODE_ALLOWED) {
1453 synchronized (mNotificationList) {
1454 tmp = new StatusBarNotification[mNotificationList.size()];
1455 final int N = mNotificationList.size();
1456 for (int i=0; i<N; i++) {
1457 tmp[i] = mNotificationList.get(i).sbn;
1458 }
1459 }
1460 }
1461 return tmp;
1462 }
1463
1464 /**
1465 * System-only API for getting a list of recent (cleared, no longer shown) notifications.
1466 *
1467 * Requires ACCESS_NOTIFICATIONS which is signature|system.
1468 */
1469 @Override
1470 public StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count) {
1471 // enforce() will ensure the calling uid has the correct permission
1472 getContext().enforceCallingOrSelfPermission(
1473 android.Manifest.permission.ACCESS_NOTIFICATIONS,
1474 "NotificationManagerService.getHistoricalNotifications");
1475
1476 StatusBarNotification[] tmp = null;
1477 int uid = Binder.getCallingUid();
1478
1479 // noteOp will check to make sure the callingPkg matches the uid
1480 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
1481 == AppOpsManager.MODE_ALLOWED) {
1482 synchronized (mArchive) {
1483 tmp = mArchive.getArray(count);
1484 }
1485 }
1486 return tmp;
1487 }
1488
1489 /**
1490 * Register a listener binder directly with the notification manager.
1491 *
1492 * Only works with system callers. Apps should extend
1493 * {@link android.service.notification.NotificationListenerService}.
1494 */
1495 @Override
1496 public void registerListener(final INotificationListener listener,
1497 final ComponentName component, final int userid) {
1498 checkCallerIsSystem();
John Spurlockc4aee982014-02-12 12:12:26 -05001499 checkNullListener(listener);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001500 registerListenerImpl(listener, component, userid);
1501 }
1502
1503 /**
1504 * Remove a listener binder directly
1505 */
1506 @Override
1507 public void unregisterListener(INotificationListener listener, int userid) {
John Spurlockc4aee982014-02-12 12:12:26 -05001508 checkNullListener(listener);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001509 // no need to check permissions; if your listener binder is in the list,
1510 // that's proof that you had permission to add it in the first place
1511 unregisterListenerImpl(listener, userid);
1512 }
1513
1514 /**
1515 * Allow an INotificationListener to simulate a "clear all" operation.
1516 *
1517 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onClearAllNotifications}
1518 *
1519 * @param token The binder for the listener, to check that the caller is allowed
1520 */
1521 @Override
John Spurlock5579ec32014-03-28 09:42:05 -04001522 public void cancelNotificationsFromListener(INotificationListener token, String[] keys) {
Adam Lesinski182f73f2013-12-05 16:48:06 -08001523 long identity = Binder.clearCallingIdentity();
1524 try {
Adam Lesinskie8240262014-03-26 16:01:00 -07001525 synchronized (mNotificationList) {
John Spurlock5579ec32014-03-28 09:42:05 -04001526 final NotificationListenerInfo info = checkListenerTokenLocked(token);
1527 if (keys != null) {
1528 final int N = keys.length;
1529 for (int i = 0; i < N; i++) {
1530 NotificationRecord r = mNotificationsByKey.get(keys[i]);
1531 if (r != null) {
1532 cancelNotificationFromListenerLocked(info,
1533 r.sbn.getPackageName(), r.sbn.getTag(), r.sbn.getId());
1534 }
1535 }
1536 } else {
1537 cancelAllLocked(info.userid);
1538 }
Adam Lesinskie8240262014-03-26 16:01:00 -07001539 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001540 } finally {
1541 Binder.restoreCallingIdentity(identity);
1542 }
1543 }
1544
John Spurlock5579ec32014-03-28 09:42:05 -04001545 private void cancelNotificationFromListenerLocked(NotificationListenerInfo info,
1546 String pkg, String tag, int id) {
1547 cancelNotification(pkg, tag, id, 0,
1548 Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
1549 true,
1550 info.userid);
1551 }
1552
Adam Lesinski182f73f2013-12-05 16:48:06 -08001553 /**
1554 * Allow an INotificationListener to simulate clearing (dismissing) a single notification.
1555 *
1556 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onNotificationClear}
1557 *
1558 * @param token The binder for the listener, to check that the caller is allowed
1559 */
1560 @Override
1561 public void cancelNotificationFromListener(INotificationListener token, String pkg,
1562 String tag, int id) {
Adam Lesinski182f73f2013-12-05 16:48:06 -08001563 long identity = Binder.clearCallingIdentity();
1564 try {
Adam Lesinskie8240262014-03-26 16:01:00 -07001565 synchronized (mNotificationList) {
John Spurlock5579ec32014-03-28 09:42:05 -04001566 final NotificationListenerInfo info = checkListenerTokenLocked(token);
1567 cancelNotificationFromListenerLocked(info,
1568 pkg, tag, id);
Adam Lesinskie8240262014-03-26 16:01:00 -07001569 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001570 } finally {
1571 Binder.restoreCallingIdentity(identity);
1572 }
1573 }
1574
1575 /**
1576 * Allow an INotificationListener to request the list of outstanding notifications seen by
1577 * the current user. Useful when starting up, after which point the listener callbacks
1578 * should be used.
1579 *
1580 * @param token The binder for the listener, to check that the caller is allowed
1581 */
1582 @Override
1583 public StatusBarNotification[] getActiveNotificationsFromListener(
John Spurlock5579ec32014-03-28 09:42:05 -04001584 INotificationListener token, String[] keys) {
Adam Lesinski182f73f2013-12-05 16:48:06 -08001585 synchronized (mNotificationList) {
John Spurlock5579ec32014-03-28 09:42:05 -04001586 final NotificationListenerInfo info = checkListenerTokenLocked(token);
1587 final ArrayList<StatusBarNotification> list
1588 = new ArrayList<StatusBarNotification>();
1589 if (keys == null) {
1590 final int N = mNotificationList.size();
1591 for (int i=0; i<N; i++) {
1592 StatusBarNotification sbn = mNotificationList.get(i).sbn;
1593 if (info.enabledAndUserMatches(sbn)) {
1594 list.add(sbn);
1595 }
1596 }
1597 } else {
1598 final int N = keys.length;
1599 for (int i=0; i<N; i++) {
1600 NotificationRecord r = mNotificationsByKey.get(keys[i]);
1601 if (r != null && info.enabledAndUserMatches(r.sbn)) {
1602 list.add(r.sbn);
1603 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001604 }
1605 }
John Spurlock5579ec32014-03-28 09:42:05 -04001606 return list.toArray(new StatusBarNotification[list.size()]);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001607 }
John Spurlock5579ec32014-03-28 09:42:05 -04001608 }
1609
1610 @Override
1611 public String[] getActiveNotificationKeysFromListener(INotificationListener token) {
1612 return NotificationManagerService.this.getActiveNotificationKeysFromListener(token);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001613 }
1614
1615 @Override
1616 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1617 if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
1618 != PackageManager.PERMISSION_GRANTED) {
1619 pw.println("Permission Denial: can't dump NotificationManager from from pid="
1620 + Binder.getCallingPid()
1621 + ", uid=" + Binder.getCallingUid());
1622 return;
1623 }
1624
1625 dumpImpl(pw);
1626 }
1627 };
1628
John Spurlock5579ec32014-03-28 09:42:05 -04001629 private String[] getActiveNotificationKeysFromListener(INotificationListener token) {
1630 synchronized (mNotificationList) {
1631 final NotificationListenerInfo info = checkListenerTokenLocked(token);
1632 final ArrayList<String> keys = new ArrayList<String>();
1633 final int N = mNotificationList.size();
1634 for (int i=0; i<N; i++) {
1635 final StatusBarNotification sbn = mNotificationList.get(i).sbn;
1636 if (info.enabledAndUserMatches(sbn)) {
1637 keys.add(sbn.getKey());
1638 }
1639 }
1640 return keys.toArray(new String[keys.size()]);
1641 }
1642 }
1643
Adam Lesinski182f73f2013-12-05 16:48:06 -08001644 void dumpImpl(PrintWriter pw) {
1645 pw.println("Current Notification Manager state:");
1646
1647 pw.println(" Listeners (" + mEnabledListenersForCurrentUser.size()
1648 + ") enabled for current user:");
1649 for (ComponentName cmpt : mEnabledListenersForCurrentUser) {
1650 pw.println(" " + cmpt);
1651 }
1652
1653 pw.println(" Live listeners (" + mListeners.size() + "):");
1654 for (NotificationListenerInfo info : mListeners) {
1655 pw.println(" " + info.component
1656 + " (user " + info.userid + "): " + info.listener
1657 + (info.isSystem?" SYSTEM":""));
1658 }
1659
1660 int N;
1661
1662 synchronized (mToastQueue) {
1663 N = mToastQueue.size();
1664 if (N > 0) {
1665 pw.println(" Toast Queue:");
1666 for (int i=0; i<N; i++) {
1667 mToastQueue.get(i).dump(pw, " ");
1668 }
1669 pw.println(" ");
1670 }
1671
1672 }
1673
1674 synchronized (mNotificationList) {
1675 N = mNotificationList.size();
1676 if (N > 0) {
1677 pw.println(" Notification List:");
1678 for (int i=0; i<N; i++) {
1679 mNotificationList.get(i).dump(pw, " ", getContext());
1680 }
1681 pw.println(" ");
1682 }
1683
1684 N = mLights.size();
1685 if (N > 0) {
1686 pw.println(" Lights List:");
1687 for (int i=0; i<N; i++) {
1688 pw.println(" " + mLights.get(i));
1689 }
1690 pw.println(" ");
1691 }
1692
1693 pw.println(" mSoundNotification=" + mSoundNotification);
1694 pw.println(" mVibrateNotification=" + mVibrateNotification);
1695 pw.println(" mDisabledNotifications=0x"
1696 + Integer.toHexString(mDisabledNotifications));
1697 pw.println(" mSystemReady=" + mSystemReady);
1698 pw.println(" mArchive=" + mArchive.toString());
1699 Iterator<StatusBarNotification> iter = mArchive.descendingIterator();
1700 int i=0;
1701 while (iter.hasNext()) {
1702 pw.println(" " + iter.next());
1703 if (++i >= 5) {
1704 if (iter.hasNext()) pw.println(" ...");
1705 break;
1706 }
1707 }
1708
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001709 }
1710 }
1711
Adam Lesinski182f73f2013-12-05 16:48:06 -08001712 /**
1713 * The private API only accessible to the system process.
1714 */
1715 private final NotificationManagerInternal mInternalService = new NotificationManagerInternal() {
1716 @Override
1717 public void enqueueNotification(String pkg, String basePkg, int callingUid, int callingPid,
1718 String tag, int id, Notification notification, int[] idReceived, int userId) {
1719 enqueueNotificationInternal(pkg, basePkg, callingUid, callingPid, tag, id, notification,
1720 idReceived, userId);
1721 }
1722 };
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001723
Adam Lesinski182f73f2013-12-05 16:48:06 -08001724 void enqueueNotificationInternal(final String pkg, String basePkg, final int callingUid,
Scott Greenwald9b05c612013-06-25 23:44:05 -04001725 final int callingPid, final String tag, final int id, final Notification notification,
Adam Lesinski182f73f2013-12-05 16:48:06 -08001726 int[] idOut, int incomingUserId) {
Daniel Sandler0da673f2012-04-11 12:33:16 -04001727 if (DBG) {
Adam Lesinski182f73f2013-12-05 16:48:06 -08001728 Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id
1729 + " notification=" + notification);
Daniel Sandler0da673f2012-04-11 12:33:16 -04001730 }
1731 checkCallerIsSystemOrSameApp(pkg);
Daniel Sandler9eb434c2013-07-31 20:21:07 -04001732 final boolean isSystemNotification = isUidSystem(callingUid) || ("android".equals(pkg));
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001733
Scott Greenwald9b05c612013-06-25 23:44:05 -04001734 final int userId = ActivityManager.handleIncomingUser(callingPid,
1735 callingUid, incomingUserId, true, false, "enqueueNotification", pkg);
Jeff Sharkey65c4a2b2012-09-25 17:22:27 -07001736 final UserHandle user = new UserHandle(userId);
Dianne Hackborn41203752012-08-31 14:05:51 -07001737
Joe Onoratobd73d012010-06-04 11:44:54 -07001738 // Limit the number of notifications that any given package except the android
1739 // package can enqueue. Prevents DOS attacks and deals with leaks.
Daniel Sandler0da673f2012-04-11 12:33:16 -04001740 if (!isSystemNotification) {
Joe Onoratobd73d012010-06-04 11:44:54 -07001741 synchronized (mNotificationList) {
1742 int count = 0;
1743 final int N = mNotificationList.size();
1744 for (int i=0; i<N; i++) {
1745 final NotificationRecord r = mNotificationList.get(i);
Daniel Sandler4f91efd2013-04-25 16:38:41 -04001746 if (r.sbn.getPackageName().equals(pkg) && r.sbn.getUserId() == userId) {
Joe Onoratobd73d012010-06-04 11:44:54 -07001747 count++;
1748 if (count >= MAX_PACKAGE_NOTIFICATIONS) {
1749 Slog.e(TAG, "Package has already posted " + count
1750 + " notifications. Not showing more. package=" + pkg);
1751 return;
1752 }
1753 }
1754 }
1755 }
1756 }
1757
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001758 // This conditional is a dirty hack to limit the logging done on
1759 // behalf of the download manager without affecting other apps.
1760 if (!pkg.equals("com.android.providers.downloads")
1761 || Log.isLoggable("DownloadManager", Log.VERBOSE)) {
Daniel Sandler321e9c52012-10-12 10:59:26 -07001762 EventLog.writeEvent(EventLogTags.NOTIFICATION_ENQUEUE, pkg, id, tag, userId,
Daniel Sandlerb64cb882011-11-29 23:48:29 -05001763 notification.toString());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001764 }
1765
1766 if (pkg == null || notification == null) {
1767 throw new IllegalArgumentException("null not allowed: pkg=" + pkg
1768 + " id=" + id + " notification=" + notification);
1769 }
1770 if (notification.icon != 0) {
1771 if (notification.contentView == null) {
1772 throw new IllegalArgumentException("contentView required: pkg=" + pkg
1773 + " id=" + id + " notification=" + notification);
1774 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001775 }
1776
Scott Greenwald9b05c612013-06-25 23:44:05 -04001777 mHandler.post(new Runnable() {
1778 @Override
1779 public void run() {
Daniel Sandler0da673f2012-04-11 12:33:16 -04001780
Scott Greenwald9b05c612013-06-25 23:44:05 -04001781 // === Scoring ===
Daniel Sandler0da673f2012-04-11 12:33:16 -04001782
Scott Greenwald9b05c612013-06-25 23:44:05 -04001783 // 0. Sanitize inputs
1784 notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN,
1785 Notification.PRIORITY_MAX);
1786 // Migrate notification flags to scores
1787 if (0 != (notification.flags & Notification.FLAG_HIGH_PRIORITY)) {
1788 if (notification.priority < Notification.PRIORITY_MAX) {
1789 notification.priority = Notification.PRIORITY_MAX;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001790 }
Scott Greenwald9b05c612013-06-25 23:44:05 -04001791 } else if (SCORE_ONGOING_HIGHER &&
1792 0 != (notification.flags & Notification.FLAG_ONGOING_EVENT)) {
1793 if (notification.priority < Notification.PRIORITY_HIGH) {
1794 notification.priority = Notification.PRIORITY_HIGH;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001795 }
1796 }
Daniel Sandlerfde19b12013-01-17 00:21:05 -05001797
Scott Greenwald9b05c612013-06-25 23:44:05 -04001798 // 1. initial score: buckets of 10, around the app
1799 int score = notification.priority * NOTIFICATION_PRIORITY_MULTIPLIER; //[-20..20]
1800
1801 // 2. Consult external heuristics (TBD)
1802
1803 // 3. Apply local rules
1804
Scott Greenwald9a05b312013-06-28 00:37:54 -04001805 int initialScore = score;
1806 if (!mScorers.isEmpty()) {
1807 if (DBG) Slog.v(TAG, "Initial score is " + score + ".");
1808 for (NotificationScorer scorer : mScorers) {
1809 try {
1810 score = scorer.getScore(notification, score);
1811 } catch (Throwable t) {
1812 Slog.w(TAG, "Scorer threw on .getScore.", t);
1813 }
1814 }
1815 if (DBG) Slog.v(TAG, "Final score is " + score + ".");
1816 }
1817
1818 // add extra to indicate score modified by NotificationScorer
1819 notification.extras.putBoolean(Notification.EXTRA_SCORE_MODIFIED,
1820 score != initialScore);
1821
Scott Greenwald9b05c612013-06-25 23:44:05 -04001822 // blocked apps
1823 if (ENABLE_BLOCKED_NOTIFICATIONS && !noteNotificationOp(pkg, callingUid)) {
1824 if (!isSystemNotification) {
1825 score = JUNK_SCORE;
1826 Slog.e(TAG, "Suppressing notification from package " + pkg
1827 + " by user request.");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001828 }
Daniel Sandlerd4d2de22012-11-14 11:25:46 -08001829 }
1830
Scott Greenwald9b05c612013-06-25 23:44:05 -04001831 if (DBG) {
1832 Slog.v(TAG, "Assigned score=" + score + " to " + notification);
1833 }
1834
1835 if (score < SCORE_DISPLAY_THRESHOLD) {
1836 // Notification will be blocked because the score is too low.
1837 return;
1838 }
1839
1840 // Should this notification make noise, vibe, or use the LED?
1841 final boolean canInterrupt = (score >= SCORE_INTERRUPTION_THRESHOLD);
1842
1843 synchronized (mNotificationList) {
1844 final StatusBarNotification n = new StatusBarNotification(
1845 pkg, id, tag, callingUid, callingPid, score, notification, user);
1846 NotificationRecord r = new NotificationRecord(n);
1847 NotificationRecord old = null;
1848
1849 int index = indexOfNotificationLocked(pkg, tag, id, userId);
1850 if (index < 0) {
1851 mNotificationList.add(r);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001852 } else {
Scott Greenwald9b05c612013-06-25 23:44:05 -04001853 old = mNotificationList.remove(index);
1854 mNotificationList.add(index, r);
1855 // Make sure we don't lose the foreground service state.
1856 if (old != null) {
1857 notification.flags |=
1858 old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;
1859 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001860 }
John Spurlock5579ec32014-03-28 09:42:05 -04001861 if (old != null) {
1862 mNotificationsByKey.remove(old.sbn.getKey());
1863 }
1864 mNotificationsByKey.put(n.getKey(), r);
Scott Greenwald9b05c612013-06-25 23:44:05 -04001865
1866 // Ensure if this is a foreground service that the proper additional
1867 // flags are set.
1868 if ((notification.flags&Notification.FLAG_FOREGROUND_SERVICE) != 0) {
1869 notification.flags |= Notification.FLAG_ONGOING_EVENT
1870 | Notification.FLAG_NO_CLEAR;
1871 }
1872
1873 final int currentUser;
1874 final long token = Binder.clearCallingIdentity();
1875 try {
1876 currentUser = ActivityManager.getCurrentUser();
1877 } finally {
1878 Binder.restoreCallingIdentity(token);
1879 }
1880
1881 if (notification.icon != 0) {
1882 if (old != null && old.statusBarKey != null) {
1883 r.statusBarKey = old.statusBarKey;
Adam Lesinski182f73f2013-12-05 16:48:06 -08001884 final long identity = Binder.clearCallingIdentity();
Scott Greenwald9b05c612013-06-25 23:44:05 -04001885 try {
1886 mStatusBar.updateNotification(r.statusBarKey, n);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001887 } finally {
Scott Greenwald9b05c612013-06-25 23:44:05 -04001888 Binder.restoreCallingIdentity(identity);
1889 }
1890 } else {
Adam Lesinski182f73f2013-12-05 16:48:06 -08001891 final long identity = Binder.clearCallingIdentity();
Scott Greenwald9b05c612013-06-25 23:44:05 -04001892 try {
1893 r.statusBarKey = mStatusBar.addNotification(n);
1894 if ((n.getNotification().flags & Notification.FLAG_SHOW_LIGHTS) != 0
1895 && canInterrupt) {
1896 mAttentionLight.pulse();
1897 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001898 } finally {
Scott Greenwald9b05c612013-06-25 23:44:05 -04001899 Binder.restoreCallingIdentity(identity);
1900 }
1901 }
1902 // Send accessibility events only for the current user.
1903 if (currentUser == userId) {
1904 sendAccessibilityEvent(notification, pkg);
1905 }
1906
1907 notifyPostedLocked(r);
1908 } else {
1909 Slog.e(TAG, "Not posting notification with icon==0: " + notification);
1910 if (old != null && old.statusBarKey != null) {
Adam Lesinski182f73f2013-12-05 16:48:06 -08001911 final long identity = Binder.clearCallingIdentity();
Scott Greenwald9b05c612013-06-25 23:44:05 -04001912 try {
1913 mStatusBar.removeNotification(old.statusBarKey);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001914 } finally {
Scott Greenwald9b05c612013-06-25 23:44:05 -04001915 Binder.restoreCallingIdentity(identity);
1916 }
1917
1918 notifyRemovedLocked(r);
1919 }
1920 // ATTENTION: in a future release we will bail out here
Adam Lesinski182f73f2013-12-05 16:48:06 -08001921 // so that we do not play sounds, show lights, etc. for invalid
1922 // notifications
Scott Greenwald9b05c612013-06-25 23:44:05 -04001923 Slog.e(TAG, "WARNING: In a future release this will crash the app: "
1924 + n.getPackageName());
1925 }
1926
1927 // If we're not supposed to beep, vibrate, etc. then don't.
Adam Lesinski182f73f2013-12-05 16:48:06 -08001928 if (((mDisabledNotifications
1929 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) == 0)
Scott Greenwald9b05c612013-06-25 23:44:05 -04001930 && (!(old != null
1931 && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 ))
1932 && (r.getUserId() == UserHandle.USER_ALL ||
1933 (r.getUserId() == userId && r.getUserId() == currentUser))
1934 && canInterrupt
Adam Lesinski182f73f2013-12-05 16:48:06 -08001935 && mSystemReady
1936 && mAudioManager != null) {
Scott Greenwald9b05c612013-06-25 23:44:05 -04001937
1938 // sound
1939
1940 // should we use the default notification sound? (indicated either by
1941 // DEFAULT_SOUND or because notification.sound is pointing at
1942 // Settings.System.NOTIFICATION_SOUND)
1943 final boolean useDefaultSound =
1944 (notification.defaults & Notification.DEFAULT_SOUND) != 0 ||
1945 Settings.System.DEFAULT_NOTIFICATION_URI
1946 .equals(notification.sound);
1947
1948 Uri soundUri = null;
1949 boolean hasValidSound = false;
1950
1951 if (useDefaultSound) {
1952 soundUri = Settings.System.DEFAULT_NOTIFICATION_URI;
1953
1954 // check to see if the default notification sound is silent
Adam Lesinski182f73f2013-12-05 16:48:06 -08001955 ContentResolver resolver = getContext().getContentResolver();
Scott Greenwald9b05c612013-06-25 23:44:05 -04001956 hasValidSound = Settings.System.getString(resolver,
1957 Settings.System.NOTIFICATION_SOUND) != null;
1958 } else if (notification.sound != null) {
1959 soundUri = notification.sound;
1960 hasValidSound = (soundUri != null);
1961 }
1962
1963 if (hasValidSound) {
Adam Lesinski182f73f2013-12-05 16:48:06 -08001964 boolean looping =
1965 (notification.flags & Notification.FLAG_INSISTENT) != 0;
Scott Greenwald9b05c612013-06-25 23:44:05 -04001966 int audioStreamType;
1967 if (notification.audioStreamType >= 0) {
1968 audioStreamType = notification.audioStreamType;
1969 } else {
1970 audioStreamType = DEFAULT_STREAM_TYPE;
1971 }
1972 mSoundNotification = r;
1973 // do not play notifications if stream volume is 0 (typically because
Jean-Michel Trivi23805662013-07-31 14:19:18 -07001974 // ringer mode is silent) or if there is a user of exclusive audio focus
Adam Lesinski182f73f2013-12-05 16:48:06 -08001975 if ((mAudioManager.getStreamVolume(audioStreamType) != 0)
1976 && !mAudioManager.isAudioFocusExclusive()) {
Scott Greenwald9b05c612013-06-25 23:44:05 -04001977 final long identity = Binder.clearCallingIdentity();
1978 try {
Adam Lesinski182f73f2013-12-05 16:48:06 -08001979 final IRingtonePlayer player =
1980 mAudioManager.getRingtonePlayer();
Scott Greenwald9b05c612013-06-25 23:44:05 -04001981 if (player != null) {
1982 player.playAsync(soundUri, user, looping, audioStreamType);
1983 }
1984 } catch (RemoteException e) {
1985 } finally {
1986 Binder.restoreCallingIdentity(identity);
1987 }
1988 }
1989 }
1990
1991 // vibrate
1992 // Does the notification want to specify its own vibration?
1993 final boolean hasCustomVibrate = notification.vibrate != null;
1994
1995 // new in 4.2: if there was supposed to be a sound and we're in vibrate
1996 // mode, and no other vibration is specified, we fall back to vibration
1997 final boolean convertSoundToVibration =
1998 !hasCustomVibrate
1999 && hasValidSound
Adam Lesinski182f73f2013-12-05 16:48:06 -08002000 && (mAudioManager.getRingerMode()
Scott Greenwald9b05c612013-06-25 23:44:05 -04002001 == AudioManager.RINGER_MODE_VIBRATE);
2002
2003 // The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback.
2004 final boolean useDefaultVibrate =
2005 (notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
2006
2007 if ((useDefaultVibrate || convertSoundToVibration || hasCustomVibrate)
Adam Lesinski182f73f2013-12-05 16:48:06 -08002008 && !(mAudioManager.getRingerMode()
Scott Greenwald9b05c612013-06-25 23:44:05 -04002009 == AudioManager.RINGER_MODE_SILENT)) {
2010 mVibrateNotification = r;
2011
2012 if (useDefaultVibrate || convertSoundToVibration) {
2013 // Escalate privileges so we can use the vibrator even if the
2014 // notifying app does not have the VIBRATE permission.
2015 long identity = Binder.clearCallingIdentity();
2016 try {
2017 mVibrator.vibrate(r.sbn.getUid(), r.sbn.getBasePkg(),
2018 useDefaultVibrate ? mDefaultVibrationPattern
2019 : mFallbackVibrationPattern,
2020 ((notification.flags & Notification.FLAG_INSISTENT) != 0)
2021 ? 0: -1);
2022 } finally {
2023 Binder.restoreCallingIdentity(identity);
2024 }
2025 } else if (notification.vibrate.length > 1) {
2026 // If you want your own vibration pattern, you need the VIBRATE
2027 // permission
2028 mVibrator.vibrate(r.sbn.getUid(), r.sbn.getBasePkg(),
2029 notification.vibrate,
2030 ((notification.flags & Notification.FLAG_INSISTENT) != 0)
2031 ? 0: -1);
2032 }
2033 }
2034 }
2035
2036 // light
2037 // the most recent thing gets the light
2038 mLights.remove(old);
2039 if (mLedNotification == old) {
2040 mLedNotification = null;
2041 }
2042 //Slog.i(TAG, "notification.lights="
2043 // + ((old.notification.lights.flags & Notification.FLAG_SHOW_LIGHTS)
2044 // != 0));
2045 if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0
2046 && canInterrupt) {
2047 mLights.add(r);
2048 updateLightsLocked();
2049 } else {
2050 if (old != null
2051 && ((old.getFlags() & Notification.FLAG_SHOW_LIGHTS) != 0)) {
2052 updateLightsLocked();
Eric Laurent524dc042009-11-27 05:07:55 -08002053 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002054 }
2055 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002056 }
Scott Greenwald9b05c612013-06-25 23:44:05 -04002057 });
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002058
2059 idOut[0] = id;
2060 }
2061
Adam Lesinski182f73f2013-12-05 16:48:06 -08002062 void registerListenerImpl(final INotificationListener listener,
2063 final ComponentName component, final int userid) {
2064 synchronized (mNotificationList) {
2065 try {
2066 NotificationListenerInfo info
2067 = new NotificationListenerInfo(listener, component, userid, true);
2068 listener.asBinder().linkToDeath(info, 0);
2069 mListeners.add(info);
2070 } catch (RemoteException e) {
2071 // already dead
2072 }
2073 }
2074 }
2075
Will Haldean Brown866b2542014-02-28 11:04:41 -08002076 /**
2077 * Removes a listener from the list and unbinds from its service.
2078 */
Adam Lesinski182f73f2013-12-05 16:48:06 -08002079 void unregisterListenerImpl(final INotificationListener listener, final int userid) {
Will Haldean Brown866b2542014-02-28 11:04:41 -08002080 NotificationListenerInfo info = removeListenerImpl(listener, userid);
2081 if (info != null && info.connection != null) {
2082 getContext().unbindService(info.connection);
2083 }
2084 }
2085
2086 /**
2087 * Removes a listener from the list but does not unbind from the listener's service.
2088 *
2089 * @return the removed listener.
2090 */
2091 NotificationListenerInfo removeListenerImpl(
2092 final INotificationListener listener, final int userid) {
2093 NotificationListenerInfo listenerInfo = null;
Adam Lesinski182f73f2013-12-05 16:48:06 -08002094 synchronized (mNotificationList) {
2095 final int N = mListeners.size();
2096 for (int i=N-1; i>=0; i--) {
2097 final NotificationListenerInfo info = mListeners.get(i);
2098 if (info.listener.asBinder() == listener.asBinder()
2099 && info.userid == userid) {
Will Haldean Brown866b2542014-02-28 11:04:41 -08002100 listenerInfo = mListeners.remove(i);
Adam Lesinski182f73f2013-12-05 16:48:06 -08002101 }
2102 }
2103 }
Will Haldean Brown866b2542014-02-28 11:04:41 -08002104 return listenerInfo;
Adam Lesinski182f73f2013-12-05 16:48:06 -08002105 }
2106
2107 void showNextToastLocked() {
2108 ToastRecord record = mToastQueue.get(0);
2109 while (record != null) {
2110 if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback);
2111 try {
2112 record.callback.show();
2113 scheduleTimeoutLocked(record);
2114 return;
2115 } catch (RemoteException e) {
2116 Slog.w(TAG, "Object died trying to show notification " + record.callback
2117 + " in package " + record.pkg);
2118 // remove it from the list and let the process die
2119 int index = mToastQueue.indexOf(record);
2120 if (index >= 0) {
2121 mToastQueue.remove(index);
2122 }
2123 keepProcessAliveLocked(record.pid);
2124 if (mToastQueue.size() > 0) {
2125 record = mToastQueue.get(0);
2126 } else {
2127 record = null;
2128 }
2129 }
2130 }
2131 }
2132
2133 void cancelToastLocked(int index) {
2134 ToastRecord record = mToastQueue.get(index);
2135 try {
2136 record.callback.hide();
2137 } catch (RemoteException e) {
2138 Slog.w(TAG, "Object died trying to hide notification " + record.callback
2139 + " in package " + record.pkg);
2140 // don't worry about this, we're about to remove it from
2141 // the list anyway
2142 }
2143 mToastQueue.remove(index);
2144 keepProcessAliveLocked(record.pid);
2145 if (mToastQueue.size() > 0) {
2146 // Show the next one. If the callback fails, this will remove
2147 // it from the list, so don't assume that the list hasn't changed
2148 // after this point.
2149 showNextToastLocked();
2150 }
2151 }
2152
2153 private void scheduleTimeoutLocked(ToastRecord r)
2154 {
2155 mHandler.removeCallbacksAndMessages(r);
2156 Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r);
2157 long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY;
2158 mHandler.sendMessageDelayed(m, delay);
2159 }
2160
2161 private void handleTimeout(ToastRecord record)
2162 {
2163 if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback);
2164 synchronized (mToastQueue) {
2165 int index = indexOfToastLocked(record.pkg, record.callback);
2166 if (index >= 0) {
2167 cancelToastLocked(index);
2168 }
2169 }
2170 }
2171
2172 // lock on mToastQueue
2173 int indexOfToastLocked(String pkg, ITransientNotification callback)
2174 {
2175 IBinder cbak = callback.asBinder();
2176 ArrayList<ToastRecord> list = mToastQueue;
2177 int len = list.size();
2178 for (int i=0; i<len; i++) {
2179 ToastRecord r = list.get(i);
2180 if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) {
2181 return i;
2182 }
2183 }
2184 return -1;
2185 }
2186
2187 // lock on mToastQueue
2188 void keepProcessAliveLocked(int pid)
2189 {
2190 int toastCount = 0; // toasts from this pid
2191 ArrayList<ToastRecord> list = mToastQueue;
2192 int N = list.size();
2193 for (int i=0; i<N; i++) {
2194 ToastRecord r = list.get(i);
2195 if (r.pid == pid) {
2196 toastCount++;
2197 }
2198 }
2199 try {
2200 mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0);
2201 } catch (RemoteException e) {
2202 // Shouldn't happen.
2203 }
2204 }
2205
2206 private final class WorkerHandler extends Handler
2207 {
2208 @Override
2209 public void handleMessage(Message msg)
2210 {
2211 switch (msg.what)
2212 {
2213 case MESSAGE_TIMEOUT:
2214 handleTimeout((ToastRecord)msg.obj);
2215 break;
2216 }
2217 }
2218 }
2219
2220
2221 // Notifications
2222 // ============================================================================
2223 static int clamp(int x, int low, int high) {
2224 return (x < low) ? low : ((x > high) ? high : x);
2225 }
2226
2227 void sendAccessibilityEvent(Notification notification, CharSequence packageName) {
2228 AccessibilityManager manager = AccessibilityManager.getInstance(getContext());
svetoslavganov75986cf2009-05-14 22:28:01 -07002229 if (!manager.isEnabled()) {
2230 return;
2231 }
2232
2233 AccessibilityEvent event =
2234 AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
2235 event.setPackageName(packageName);
2236 event.setClassName(Notification.class.getName());
2237 event.setParcelableData(notification);
2238 CharSequence tickerText = notification.tickerText;
2239 if (!TextUtils.isEmpty(tickerText)) {
2240 event.getText().add(tickerText);
2241 }
2242
2243 manager.sendAccessibilityEvent(event);
2244 }
2245
Joe Onorato46439ce2010-11-19 13:56:21 -08002246 private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete) {
2247 // tell the app
2248 if (sendDelete) {
Daniel Sandlerfde19b12013-01-17 00:21:05 -05002249 if (r.getNotification().deleteIntent != null) {
Joe Onorato46439ce2010-11-19 13:56:21 -08002250 try {
Daniel Sandlerfde19b12013-01-17 00:21:05 -05002251 r.getNotification().deleteIntent.send();
Joe Onorato46439ce2010-11-19 13:56:21 -08002252 } catch (PendingIntent.CanceledException ex) {
2253 // do nothing - there's no relevant way to recover, and
2254 // no reason to let this propagate
Daniel Sandler4f91efd2013-04-25 16:38:41 -04002255 Slog.w(TAG, "canceled PendingIntent for " + r.sbn.getPackageName(), ex);
Joe Onorato46439ce2010-11-19 13:56:21 -08002256 }
2257 }
2258 }
2259
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002260 // status bar
Daniel Sandlerfde19b12013-01-17 00:21:05 -05002261 if (r.getNotification().icon != 0) {
Adam Lesinski182f73f2013-12-05 16:48:06 -08002262 final long identity = Binder.clearCallingIdentity();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002263 try {
Joe Onorato0cbda992010-05-02 16:28:15 -07002264 mStatusBar.removeNotification(r.statusBarKey);
Adam Lesinski182f73f2013-12-05 16:48:06 -08002265 } finally {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002266 Binder.restoreCallingIdentity(identity);
2267 }
2268 r.statusBarKey = null;
Daniel Sandler09a247e2013-02-14 10:24:17 -05002269 notifyRemovedLocked(r);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002270 }
2271
2272 // sound
2273 if (mSoundNotification == r) {
2274 mSoundNotification = null;
Jeff Sharkey098d5802012-04-26 17:30:34 -07002275 final long identity = Binder.clearCallingIdentity();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002276 try {
Adam Lesinski182f73f2013-12-05 16:48:06 -08002277 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
Jeff Sharkey098d5802012-04-26 17:30:34 -07002278 if (player != null) {
2279 player.stopAsync();
2280 }
2281 } catch (RemoteException e) {
2282 } finally {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002283 Binder.restoreCallingIdentity(identity);
2284 }
2285 }
2286
2287 // vibrate
2288 if (mVibrateNotification == r) {
2289 mVibrateNotification = null;
2290 long identity = Binder.clearCallingIdentity();
2291 try {
2292 mVibrator.cancel();
2293 }
2294 finally {
2295 Binder.restoreCallingIdentity(identity);
2296 }
2297 }
2298
2299 // light
2300 mLights.remove(r);
2301 if (mLedNotification == r) {
2302 mLedNotification = null;
2303 }
Daniel Sandler23d7c702013-03-07 16:32:06 -05002304
2305 // Save it for users of getHistoricalNotifications()
2306 mArchive.record(r.sbn);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002307 }
2308
2309 /**
Dianne Hackbornd8a43f62009-08-17 23:33:56 -07002310 * Cancels a notification ONLY if it has all of the {@code mustHaveFlags}
Doug Zongkerab5c49c2009-12-04 10:31:43 -08002311 * and none of the {@code mustNotHaveFlags}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002312 */
Adam Lesinski182f73f2013-12-05 16:48:06 -08002313 void cancelNotification(final String pkg, final String tag, final int id,
Svetoslav Ganov835835e2013-08-04 20:17:52 -07002314 final int mustHaveFlags, final int mustNotHaveFlags, final boolean sendDelete,
2315 final int userId) {
2316 // In enqueueNotificationInternal notifications are added by scheduling the
2317 // work on the worker handler. Hence, we also schedule the cancel on this
2318 // handler to avoid a scenario where an add notification call followed by a
2319 // remove notification call ends up in not removing the notification.
2320 mHandler.post(new Runnable() {
2321 @Override
2322 public void run() {
2323 EventLog.writeEvent(EventLogTags.NOTIFICATION_CANCEL, pkg, id, tag, userId,
2324 mustHaveFlags, mustNotHaveFlags);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002325
Svetoslav Ganov835835e2013-08-04 20:17:52 -07002326 synchronized (mNotificationList) {
2327 int index = indexOfNotificationLocked(pkg, tag, id, userId);
2328 if (index >= 0) {
2329 NotificationRecord r = mNotificationList.get(index);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08002330
Svetoslav Ganov835835e2013-08-04 20:17:52 -07002331 if ((r.getNotification().flags & mustHaveFlags) != mustHaveFlags) {
2332 return;
2333 }
2334 if ((r.getNotification().flags & mustNotHaveFlags) != 0) {
2335 return;
2336 }
2337
2338 mNotificationList.remove(index);
John Spurlock5579ec32014-03-28 09:42:05 -04002339 mNotificationsByKey.remove(r.sbn.getKey());
Svetoslav Ganov835835e2013-08-04 20:17:52 -07002340
2341 cancelNotificationLocked(r, sendDelete);
2342 updateLightsLocked();
2343 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002344 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002345 }
Svetoslav Ganov835835e2013-08-04 20:17:52 -07002346 });
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002347 }
2348
2349 /**
Daniel Sandler321e9c52012-10-12 10:59:26 -07002350 * Determine whether the userId applies to the notification in question, either because
2351 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard).
2352 */
2353 private boolean notificationMatchesUserId(NotificationRecord r, int userId) {
2354 return
2355 // looking for USER_ALL notifications? match everything
2356 userId == UserHandle.USER_ALL
2357 // a notification sent to USER_ALL matches any query
Daniel Sandlerfde19b12013-01-17 00:21:05 -05002358 || r.getUserId() == UserHandle.USER_ALL
Daniel Sandler321e9c52012-10-12 10:59:26 -07002359 // an exact user match
Daniel Sandlerfde19b12013-01-17 00:21:05 -05002360 || r.getUserId() == userId;
Daniel Sandler321e9c52012-10-12 10:59:26 -07002361 }
2362
2363 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002364 * Cancels all notifications from a given package that have all of the
2365 * {@code mustHaveFlags}.
2366 */
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08002367 boolean cancelAllNotificationsInt(String pkg, int mustHaveFlags,
Dianne Hackborn41203752012-08-31 14:05:51 -07002368 int mustNotHaveFlags, boolean doit, int userId) {
Daniel Sandler321e9c52012-10-12 10:59:26 -07002369 EventLog.writeEvent(EventLogTags.NOTIFICATION_CANCEL_ALL, pkg, userId,
2370 mustHaveFlags, mustNotHaveFlags);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002371
2372 synchronized (mNotificationList) {
2373 final int N = mNotificationList.size();
2374 boolean canceledSomething = false;
2375 for (int i = N-1; i >= 0; --i) {
2376 NotificationRecord r = mNotificationList.get(i);
Daniel Sandler321e9c52012-10-12 10:59:26 -07002377 if (!notificationMatchesUserId(r, userId)) {
Dianne Hackborn41203752012-08-31 14:05:51 -07002378 continue;
2379 }
Amith Yamasani5ec00e92012-11-07 16:58:30 -08002380 // Don't remove notifications to all, if there's no package name specified
Daniel Sandlerfde19b12013-01-17 00:21:05 -05002381 if (r.getUserId() == UserHandle.USER_ALL && pkg == null) {
Amith Yamasani5ec00e92012-11-07 16:58:30 -08002382 continue;
2383 }
Daniel Sandlerfde19b12013-01-17 00:21:05 -05002384 if ((r.getFlags() & mustHaveFlags) != mustHaveFlags) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002385 continue;
2386 }
Daniel Sandlerfde19b12013-01-17 00:21:05 -05002387 if ((r.getFlags() & mustNotHaveFlags) != 0) {
Dianne Hackbornd8a43f62009-08-17 23:33:56 -07002388 continue;
2389 }
Daniel Sandler4f91efd2013-04-25 16:38:41 -04002390 if (pkg != null && !r.sbn.getPackageName().equals(pkg)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002391 continue;
2392 }
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08002393 canceledSomething = true;
2394 if (!doit) {
2395 return true;
2396 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002397 mNotificationList.remove(i);
John Spurlock5579ec32014-03-28 09:42:05 -04002398 mNotificationsByKey.remove(r.sbn.getKey());
Joe Onorato46439ce2010-11-19 13:56:21 -08002399 cancelNotificationLocked(r, false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002400 }
2401 if (canceledSomething) {
2402 updateLightsLocked();
2403 }
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08002404 return canceledSomething;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002405 }
2406 }
2407
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002408
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002409
Daniel Sandler9eb434c2013-07-31 20:21:07 -04002410 // Return true if the UID is a system or phone UID and therefore should not have
Daniel Sandler7c011302013-06-10 12:06:51 -04002411 // any notifications or toasts blocked.
Daniel Sandler9eb434c2013-07-31 20:21:07 -04002412 boolean isUidSystem(int uid) {
Daniel Sandler7c011302013-06-10 12:06:51 -04002413 final int appid = UserHandle.getAppId(uid);
2414 return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0);
2415 }
2416
Daniel Sandler9eb434c2013-07-31 20:21:07 -04002417 // same as isUidSystem(int, int) for the Binder caller's UID.
2418 boolean isCallerSystem() {
2419 return isUidSystem(Binder.getCallingUid());
2420 }
2421
Daniel Sandler0da673f2012-04-11 12:33:16 -04002422 void checkCallerIsSystem() {
Daniel Sandler7c011302013-06-10 12:06:51 -04002423 if (isCallerSystem()) {
Daniel Sandler0da673f2012-04-11 12:33:16 -04002424 return;
2425 }
Daniel Sandler7c011302013-06-10 12:06:51 -04002426 throw new SecurityException("Disallowed call for uid " + Binder.getCallingUid());
Daniel Sandler0da673f2012-04-11 12:33:16 -04002427 }
2428
2429 void checkCallerIsSystemOrSameApp(String pkg) {
Daniel Sandler7c011302013-06-10 12:06:51 -04002430 if (isCallerSystem()) {
Dianne Hackbornd8a43f62009-08-17 23:33:56 -07002431 return;
2432 }
Daniel Sandler7c011302013-06-10 12:06:51 -04002433 final int uid = Binder.getCallingUid();
Dianne Hackbornd8a43f62009-08-17 23:33:56 -07002434 try {
Amith Yamasanif203aee2012-08-29 18:41:53 -07002435 ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo(
2436 pkg, 0, UserHandle.getCallingUserId());
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07002437 if (!UserHandle.isSameApp(ai.uid, uid)) {
Dianne Hackbornd8a43f62009-08-17 23:33:56 -07002438 throw new SecurityException("Calling uid " + uid + " gave package"
2439 + pkg + " which is owned by uid " + ai.uid);
2440 }
Amith Yamasanif203aee2012-08-29 18:41:53 -07002441 } catch (RemoteException re) {
2442 throw new SecurityException("Unknown package " + pkg + "\n" + re);
Dianne Hackbornd8a43f62009-08-17 23:33:56 -07002443 }
2444 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08002445
Adam Lesinskie8240262014-03-26 16:01:00 -07002446 void cancelAllLocked(int userId) {
2447 final int N = mNotificationList.size();
2448 for (int i=N-1; i>=0; i--) {
2449 NotificationRecord r = mNotificationList.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002450
Adam Lesinskie8240262014-03-26 16:01:00 -07002451 if (!notificationMatchesUserId(r, userId)) {
2452 continue;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002453 }
2454
Adam Lesinskie8240262014-03-26 16:01:00 -07002455 if ((r.getFlags() & (Notification.FLAG_ONGOING_EVENT
2456 | Notification.FLAG_NO_CLEAR)) == 0) {
2457 mNotificationList.remove(i);
John Spurlock5579ec32014-03-28 09:42:05 -04002458 mNotificationsByKey.remove(r.sbn.getKey());
Adam Lesinskie8240262014-03-26 16:01:00 -07002459 cancelNotificationLocked(r, true);
2460 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002461 }
Adam Lesinskie8240262014-03-26 16:01:00 -07002462
2463 updateLightsLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002464 }
2465
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002466 // lock on mNotificationList
Adam Lesinski182f73f2013-12-05 16:48:06 -08002467 void updateLightsLocked()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002468 {
The Android Open Source Project10592532009-03-18 17:39:46 -07002469 // handle notification lights
2470 if (mLedNotification == null) {
2471 // get next notification, if any
2472 int n = mLights.size();
2473 if (n > 0) {
2474 mLedNotification = mLights.get(n-1);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002475 }
2476 }
Mike Lockwoodc22404a2009-12-02 11:15:02 -05002477
Mike Lockwood63b5ad92011-08-30 09:55:30 -04002478 // Don't flash while we are in a call or screen is on
2479 if (mLedNotification == null || mInCall || mScreenOn) {
Mike Lockwood3cb67a32009-11-27 14:25:58 -05002480 mNotificationLight.turnOff();
The Android Open Source Project10592532009-03-18 17:39:46 -07002481 } else {
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -04002482 final Notification ledno = mLedNotification.sbn.getNotification();
Daniel Sandlerfde19b12013-01-17 00:21:05 -05002483 int ledARGB = ledno.ledARGB;
2484 int ledOnMS = ledno.ledOnMS;
2485 int ledOffMS = ledno.ledOffMS;
2486 if ((ledno.defaults & Notification.DEFAULT_LIGHTS) != 0) {
Mike Lockwood670f9322010-01-20 12:13:36 -05002487 ledARGB = mDefaultNotificationColor;
2488 ledOnMS = mDefaultNotificationLedOn;
2489 ledOffMS = mDefaultNotificationLedOff;
2490 }
2491 if (mNotificationPulseEnabled) {
2492 // pulse repeatedly
Adam Lesinski182f73f2013-12-05 16:48:06 -08002493 mNotificationLight.setFlashing(ledARGB, Light.LIGHT_FLASH_TIMED,
Mike Lockwood670f9322010-01-20 12:13:36 -05002494 ledOnMS, ledOffMS);
Mike Lockwood670f9322010-01-20 12:13:36 -05002495 }
The Android Open Source Project10592532009-03-18 17:39:46 -07002496 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002497 }
2498
2499 // lock on mNotificationList
Adam Lesinski182f73f2013-12-05 16:48:06 -08002500 int indexOfNotificationLocked(String pkg, String tag, int id, int userId)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002501 {
2502 ArrayList<NotificationRecord> list = mNotificationList;
2503 final int len = list.size();
2504 for (int i=0; i<len; i++) {
2505 NotificationRecord r = list.get(i);
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -04002506 if (!notificationMatchesUserId(r, userId) || r.sbn.getId() != id) {
Dianne Hackborn41203752012-08-31 14:05:51 -07002507 continue;
2508 }
Fred Quintana6ecaff12009-09-25 14:23:13 -07002509 if (tag == null) {
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -04002510 if (r.sbn.getTag() != null) {
Fred Quintana6ecaff12009-09-25 14:23:13 -07002511 continue;
2512 }
2513 } else {
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -04002514 if (!tag.equals(r.sbn.getTag())) {
Fred Quintana6ecaff12009-09-25 14:23:13 -07002515 continue;
2516 }
2517 }
Daniel Sandler4f91efd2013-04-25 16:38:41 -04002518 if (r.sbn.getPackageName().equals(pkg)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002519 return i;
2520 }
2521 }
2522 return -1;
2523 }
2524
Mike Lockwoodc22404a2009-12-02 11:15:02 -05002525 private void updateNotificationPulse() {
2526 synchronized (mNotificationList) {
2527 updateLightsLocked();
2528 }
2529 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002530}