blob: 2df79b232e8bb15f9b9f76f47826980c9d386d9d [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Adam Lesinski182f73f2013-12-05 16:48:06 -080017package com.android.server.notification;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080018
John Spurlockd8afe3c2014-08-01 14:04:07 -040019import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_EFFECTS;
Christoph Studerb82bc782014-08-20 14:29:43 +020020import static android.service.notification.NotificationListenerService.TRIM_FULL;
21import static android.service.notification.NotificationListenerService.TRIM_LIGHT;
Jeff Sharkey098d5802012-04-26 17:30:34 -070022import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
23import static org.xmlpull.v1.XmlPullParser.END_TAG;
24import static org.xmlpull.v1.XmlPullParser.START_TAG;
svetoslavganov75986cf2009-05-14 22:28:01 -070025
Dianne Hackborn41203752012-08-31 14:05:51 -070026import android.app.ActivityManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080027import android.app.ActivityManagerNative;
John Spurlock7340fc82014-04-24 18:50:12 -040028import android.app.AppGlobals;
Daniel Sandler4a900ac2013-01-30 14:04:10 -050029import android.app.AppOpsManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080030import android.app.IActivityManager;
31import android.app.INotificationManager;
John Spurlock1fc476d2015-04-14 16:05:20 -040032import android.app.INotificationManagerCallback;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080033import android.app.ITransientNotification;
34import android.app.Notification;
John Spurlockb4782522014-08-22 14:54:46 -040035import android.app.NotificationManager;
John Spurlock1fc476d2015-04-14 16:05:20 -040036import android.app.NotificationManager.Policy;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080037import android.app.PendingIntent;
38import android.app.StatusBarManager;
39import android.content.BroadcastReceiver;
Daniel Sandler5feceeb2013-03-22 18:29:23 -070040import android.content.ComponentName;
Dianne Hackborn1dac2772009-06-26 18:16:48 -070041import android.content.ContentResolver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080042import android.content.Context;
43import android.content.Intent;
44import android.content.IntentFilter;
John Spurlock7340fc82014-04-24 18:50:12 -040045import android.content.pm.ApplicationInfo;
Kenny Guy70058402014-10-28 20:45:06 +000046import android.content.pm.IPackageManager;
Daniel Sandler4a900ac2013-01-30 14:04:10 -050047import android.content.pm.PackageInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080048import android.content.pm.PackageManager;
49import android.content.pm.PackageManager.NameNotFoundException;
Christoph Studercee44ba2014-05-20 18:36:43 +020050import android.content.pm.ParceledListSlice;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080051import android.content.res.Resources;
Dianne Hackborn1dac2772009-06-26 18:16:48 -070052import android.database.ContentObserver;
John Spurlock7b414672014-07-18 13:02:39 -040053import android.media.AudioAttributes;
svetoslavganov75986cf2009-05-14 22:28:01 -070054import android.media.AudioManager;
John Spurlockcdb57ae2015-02-11 19:04:11 -050055import android.media.AudioManagerInternal;
Jean-Michel Triviceb79bc2014-09-05 11:09:14 -070056import android.media.AudioSystem;
Jeff Sharkey098d5802012-04-26 17:30:34 -070057import android.media.IRingtonePlayer;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080058import android.net.Uri;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080059import android.os.Binder;
Selim Cinekb5605e52015-02-20 18:21:41 +010060import android.os.Build;
John Spurlock2b122f42014-08-27 16:29:47 -040061import android.os.Bundle;
John Spurlock056c5192014-04-20 21:52:01 -040062import android.os.Environment;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080063import android.os.Handler;
Chris Wrenf9536642014-04-17 10:01:54 -040064import android.os.HandlerThread;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080065import android.os.IBinder;
John Spurlock7340fc82014-04-24 18:50:12 -040066import android.os.IInterface;
Chris Wrenf9536642014-04-17 10:01:54 -040067import android.os.Looper;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080068import android.os.Message;
Dianne Hackbornd8a43f62009-08-17 23:33:56 -070069import android.os.Process;
svetoslavganov75986cf2009-05-14 22:28:01 -070070import android.os.RemoteException;
Selim Cinekb5605e52015-02-20 18:21:41 +010071import android.os.SystemProperties;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -070072import android.os.UserHandle;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080073import android.os.Vibrator;
74import android.provider.Settings;
Chris Wren333a61c2014-05-28 16:40:57 -040075import android.service.notification.Condition;
John Spurlocke77bb362014-04-26 10:24:59 -040076import android.service.notification.IConditionListener;
John Spurlock7340fc82014-04-24 18:50:12 -040077import android.service.notification.IConditionProvider;
Chris Wren333a61c2014-05-28 16:40:57 -040078import android.service.notification.INotificationListener;
Griff Hazen84a00ea2014-09-02 17:10:47 -070079import android.service.notification.IStatusBarNotificationHolder;
John Spurlock7340fc82014-04-24 18:50:12 -040080import android.service.notification.NotificationListenerService;
Christoph Studer05ad4822014-05-16 14:16:03 +020081import android.service.notification.NotificationRankingUpdate;
Daniel Sandler5feceeb2013-03-22 18:29:23 -070082import android.service.notification.StatusBarNotification;
John Spurlock056c5192014-04-20 21:52:01 -040083import android.service.notification.ZenModeConfig;
John Spurlock32fe4c62014-10-02 12:16:02 -040084import android.telephony.PhoneStateListener;
Daniel Sandlere96ffb12010-03-11 13:38:06 -050085import android.telephony.TelephonyManager;
svetoslavganov75986cf2009-05-14 22:28:01 -070086import android.text.TextUtils;
John Spurlocka4294292014-03-24 18:02:32 -040087import android.util.ArrayMap;
John Spurlock1fa865f2014-07-21 14:56:39 -040088import android.util.ArraySet;
Dianne Hackborn39606a02012-07-31 17:54:35 -070089import android.util.AtomicFile;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080090import android.util.Log;
Andy Stadler110988c2010-12-03 14:29:16 -080091import android.util.Slog;
Daniel Sandler0da673f2012-04-11 12:33:16 -040092import android.util.Xml;
svetoslavganov75986cf2009-05-14 22:28:01 -070093import android.view.accessibility.AccessibilityEvent;
94import android.view.accessibility.AccessibilityManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080095import android.widget.Toast;
96
Scott Greenwald9a05b312013-06-28 00:37:54 -040097import com.android.internal.R;
John Spurlock056c5192014-04-20 21:52:01 -040098import com.android.internal.util.FastXmlSerializer;
Adam Lesinski182f73f2013-12-05 16:48:06 -080099import com.android.server.EventLogTags;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800100import com.android.server.SystemService;
101import com.android.server.lights.Light;
102import com.android.server.lights.LightsManager;
John Spurlock7340fc82014-04-24 18:50:12 -0400103import com.android.server.notification.ManagedServices.ManagedServiceInfo;
104import com.android.server.notification.ManagedServices.UserProfiles;
John Spurlockb408e8e2014-04-23 21:12:45 -0400105import com.android.server.statusbar.StatusBarManagerInternal;
106
107import libcore.io.IoUtils;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800108
Jeff Sharkey098d5802012-04-26 17:30:34 -0700109import org.xmlpull.v1.XmlPullParser;
110import org.xmlpull.v1.XmlPullParserException;
John Spurlock056c5192014-04-20 21:52:01 -0400111import org.xmlpull.v1.XmlSerializer;
Jeff Sharkey098d5802012-04-26 17:30:34 -0700112
Daniel Sandler0da673f2012-04-11 12:33:16 -0400113import java.io.File;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800114import java.io.FileDescriptor;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400115import java.io.FileInputStream;
116import java.io.FileNotFoundException;
John Spurlock056c5192014-04-20 21:52:01 -0400117import java.io.FileOutputStream;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400118import java.io.IOException;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800119import java.io.PrintWriter;
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500120import java.util.ArrayDeque;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800121import java.util.ArrayList;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400122import java.util.HashSet;
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500123import java.util.Iterator;
Christoph Studer265c1052014-07-23 17:14:33 +0200124import java.util.Map.Entry;
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500125import java.util.NoSuchElementException;
John Spurlockb4782522014-08-22 14:54:46 -0400126import java.util.Objects;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400127
Daniel Sandlerd0a2f862010-08-03 15:29:31 -0400128/** {@hide} */
Adam Lesinski182f73f2013-12-05 16:48:06 -0800129public class NotificationManagerService extends SystemService {
130 static final String TAG = "NotificationService";
Christoph Studer1f32c652014-11-26 15:32:20 +0100131 static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
Selim Cinekb5605e52015-02-20 18:21:41 +0100132 public static final boolean ENABLE_CHILD_NOTIFICATIONS = Build.IS_DEBUGGABLE
133 && SystemProperties.getBoolean("debug.child_notifs", false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800134
Adam Lesinski182f73f2013-12-05 16:48:06 -0800135 static final int MAX_PACKAGE_NOTIFICATIONS = 50;
Joe Onoratobd73d012010-06-04 11:44:54 -0700136
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800137 // message codes
Adam Lesinski182f73f2013-12-05 16:48:06 -0800138 static final int MESSAGE_TIMEOUT = 2;
John Spurlock056c5192014-04-20 21:52:01 -0400139 static final int MESSAGE_SAVE_POLICY_FILE = 3;
Chris Wrenf9536642014-04-17 10:01:54 -0400140 static final int MESSAGE_RECONSIDER_RANKING = 4;
Chris Wren54bbef42014-07-09 18:37:56 -0400141 static final int MESSAGE_RANKING_CONFIG_CHANGE = 5;
142 static final int MESSAGE_SEND_RANKING_UPDATE = 6;
John Spurlockd8afe3c2014-08-01 14:04:07 -0400143 static final int MESSAGE_LISTENER_HINTS_CHANGED = 7;
Christoph Studer85a384b2014-08-27 20:16:15 +0200144 static final int MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED = 8;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800145
Adam Lesinski182f73f2013-12-05 16:48:06 -0800146 static final int LONG_DELAY = 3500; // 3.5 seconds
147 static final int SHORT_DELAY = 2000; // 2 seconds
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800148
Adam Lesinski182f73f2013-12-05 16:48:06 -0800149 static final long[] DEFAULT_VIBRATE_PATTERN = {0, 250, 250, 250};
Christoph Studer265c1052014-07-23 17:14:33 +0200150
Adam Lesinski182f73f2013-12-05 16:48:06 -0800151 static final int VIBRATE_PATTERN_MAXLEN = 8 * 2 + 1; // up to eight bumps
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800152
Adam Lesinski182f73f2013-12-05 16:48:06 -0800153 static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_NOTIFICATION;
154 static final boolean SCORE_ONGOING_HIGHER = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800155
Adam Lesinski182f73f2013-12-05 16:48:06 -0800156 static final int JUNK_SCORE = -1000;
157 static final int NOTIFICATION_PRIORITY_MULTIPLIER = 10;
158 static final int SCORE_DISPLAY_THRESHOLD = Notification.PRIORITY_MIN * NOTIFICATION_PRIORITY_MULTIPLIER;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400159
Daniel Sandler526fa0e2012-12-04 14:51:50 -0500160 // Notifications with scores below this will not interrupt the user, either via LED or
161 // sound or vibration
Adam Lesinski182f73f2013-12-05 16:48:06 -0800162 static final int SCORE_INTERRUPTION_THRESHOLD =
Daniel Sandler526fa0e2012-12-04 14:51:50 -0500163 Notification.PRIORITY_LOW * NOTIFICATION_PRIORITY_MULTIPLIER;
164
Adam Lesinski182f73f2013-12-05 16:48:06 -0800165 static final boolean ENABLE_BLOCKED_NOTIFICATIONS = true;
166 static final boolean ENABLE_BLOCKED_TOASTS = true;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400167
Christoph Studer12aeda82014-09-23 19:08:56 +0200168 // When #matchesCallFilter is called from the ringer, wait at most
169 // 3s to resolve the contacts. This timeout is required since
170 // ContactsProvider might take a long time to start up.
171 //
172 // Return STARRED_CONTACT when the timeout is hit in order to avoid
173 // missed calls in ZEN mode "Important".
174 static final int MATCHES_CALL_FILTER_CONTACTS_TIMEOUT_MS = 3000;
175 static final float MATCHES_CALL_FILTER_TIMEOUT_AFFINITY =
176 ValidateNotificationPeople.STARRED_CONTACT;
177
Christoph Studer265c1052014-07-23 17:14:33 +0200178 /** notification_enqueue status value for a newly enqueued notification. */
179 private static final int EVENTLOG_ENQUEUE_STATUS_NEW = 0;
180
181 /** notification_enqueue status value for an existing notification. */
182 private static final int EVENTLOG_ENQUEUE_STATUS_UPDATE = 1;
183
184 /** notification_enqueue status value for an ignored notification. */
185 private static final int EVENTLOG_ENQUEUE_STATUS_IGNORED = 2;
186
Adam Lesinski182f73f2013-12-05 16:48:06 -0800187 private IActivityManager mAm;
188 AudioManager mAudioManager;
John Spurlockcdb57ae2015-02-11 19:04:11 -0500189 AudioManagerInternal mAudioManagerInternal;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800190 StatusBarManagerInternal mStatusBar;
191 Vibrator mVibrator;
192
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800193 final IBinder mForegroundToken = new Binder();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800194 private WorkerHandler mHandler;
Chris Wrenf9536642014-04-17 10:01:54 -0400195 private final HandlerThread mRankingThread = new HandlerThread("ranker",
196 Process.THREAD_PRIORITY_BACKGROUND);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800197
Adam Lesinski182f73f2013-12-05 16:48:06 -0800198 private Light mNotificationLight;
199 Light mAttentionLight;
Mike Lockwood670f9322010-01-20 12:13:36 -0500200 private int mDefaultNotificationColor;
201 private int mDefaultNotificationLedOn;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800202
Mike Lockwood670f9322010-01-20 12:13:36 -0500203 private int mDefaultNotificationLedOff;
Daniel Sandleredbb3802012-11-13 20:49:47 -0800204 private long[] mDefaultVibrationPattern;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800205
Daniel Sandleredbb3802012-11-13 20:49:47 -0800206 private long[] mFallbackVibrationPattern;
Chris Wren5116a822014-06-04 15:59:50 -0400207 private boolean mUseAttentionLight;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800208 boolean mSystemReady;
Daniel Sandleredbb3802012-11-13 20:49:47 -0800209
John Spurlockd8afe3c2014-08-01 14:04:07 -0400210 private boolean mDisableNotificationEffects;
John Spurlock32fe4c62014-10-02 12:16:02 -0400211 private int mCallState;
Chris Wren6054e612014-11-25 17:16:46 -0500212 private String mSoundNotificationKey;
213 private String mVibrateNotificationKey;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800214
John Spurlockd8afe3c2014-08-01 14:04:07 -0400215 private final ArraySet<ManagedServiceInfo> mListenersDisablingEffects = new ArraySet<>();
John Spurlockb4782522014-08-22 14:54:46 -0400216 private ComponentName mEffectsSuppressor;
John Spurlockd8afe3c2014-08-01 14:04:07 -0400217 private int mListenerHints; // right now, all hints are global
John Spurlock83104102015-02-12 23:25:12 -0500218 private int mInterruptionFilter = NotificationListenerService.INTERRUPTION_FILTER_UNKNOWN;
John Spurlock1fa865f2014-07-21 14:56:39 -0400219
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500220 // for enabling and disabling notification pulse behavior
Mike Lockwood63b5ad92011-08-30 09:55:30 -0400221 private boolean mScreenOn = true;
Daniel Sandlere96ffb12010-03-11 13:38:06 -0500222 private boolean mInCall = false;
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500223 private boolean mNotificationPulseEnabled;
224
Daniel Sandler09a247e2013-02-14 10:24:17 -0500225 // used as a mutex for access to all active notifications & listeners
Adam Lesinski182f73f2013-12-05 16:48:06 -0800226 final ArrayList<NotificationRecord> mNotificationList =
Fred Quintana6ecaff12009-09-25 14:23:13 -0700227 new ArrayList<NotificationRecord>();
John Spurlocka4294292014-03-24 18:02:32 -0400228 final ArrayMap<String, NotificationRecord> mNotificationsByKey =
229 new ArrayMap<String, NotificationRecord>();
Adam Lesinski182f73f2013-12-05 16:48:06 -0800230 final ArrayList<ToastRecord> mToastQueue = new ArrayList<ToastRecord>();
Christoph Studer265c1052014-07-23 17:14:33 +0200231 final ArrayMap<String, NotificationRecord> mSummaryByGroupKey = new ArrayMap<>();
John Spurlock1fc476d2015-04-14 16:05:20 -0400232 private final ArrayMap<String, Policy.Token> mPolicyTokens = new ArrayMap<>();
233
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800234
Chris Wren6054e612014-11-25 17:16:46 -0500235 // The last key in this list owns the hardware.
236 ArrayList<String> mLights = new ArrayList<>();
svetoslavganov75986cf2009-05-14 22:28:01 -0700237
Adam Lesinski182f73f2013-12-05 16:48:06 -0800238 private AppOpsManager mAppOps;
Daniel Sandler4a900ac2013-01-30 14:04:10 -0500239
Griff Hazen9f637d12014-06-10 11:13:51 -0700240 private Archive mArchive;
241
Daniel Sandler0da673f2012-04-11 12:33:16 -0400242 // Notification control database. For now just contains disabled packages.
243 private AtomicFile mPolicyFile;
244 private HashSet<String> mBlockedPackages = new HashSet<String>();
245
246 private static final int DB_VERSION = 1;
247
248 private static final String TAG_BODY = "notification-policy";
249 private static final String ATTR_VERSION = "version";
250
251 private static final String TAG_BLOCKED_PKGS = "blocked-packages";
252 private static final String TAG_PACKAGE = "package";
253 private static final String ATTR_NAME = "name";
254
Chris Wren54bbef42014-07-09 18:37:56 -0400255 private RankingHelper mRankingHelper;
Scott Greenwald9a05b312013-06-28 00:37:54 -0400256
John Spurlockb408e8e2014-04-23 21:12:45 -0400257 private final UserProfiles mUserProfiles = new UserProfiles();
John Spurlock7340fc82014-04-24 18:50:12 -0400258 private NotificationListeners mListeners;
259 private ConditionProviders mConditionProviders;
Christoph Studer1c3f81f2014-04-16 15:05:56 +0200260 private NotificationUsageStats mUsageStats;
Christoph Studer546bec82014-03-14 12:17:12 +0100261
John Spurlocke6a7d932014-03-13 12:29:00 -0400262 private static final int MY_UID = Process.myUid();
263 private static final int MY_PID = Process.myPid();
264 private static final int REASON_DELEGATE_CLICK = 1;
265 private static final int REASON_DELEGATE_CANCEL = 2;
266 private static final int REASON_DELEGATE_CANCEL_ALL = 3;
267 private static final int REASON_DELEGATE_ERROR = 4;
268 private static final int REASON_PACKAGE_CHANGED = 5;
269 private static final int REASON_USER_STOPPED = 6;
270 private static final int REASON_PACKAGE_BANNED = 7;
271 private static final int REASON_NOMAN_CANCEL = 8;
272 private static final int REASON_NOMAN_CANCEL_ALL = 9;
273 private static final int REASON_LISTENER_CANCEL = 10;
274 private static final int REASON_LISTENER_CANCEL_ALL = 11;
Christoph Studere4ef156b2014-07-04 18:41:57 +0200275 private static final int REASON_GROUP_SUMMARY_CANCELED = 12;
Christoph Studer265c1052014-07-23 17:14:33 +0200276 private static final int REASON_GROUP_OPTIMIZATION = 13;
John Spurlocke6a7d932014-03-13 12:29:00 -0400277
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500278 private static class Archive {
Griff Hazen9f637d12014-06-10 11:13:51 -0700279 final int mBufferSize;
280 final ArrayDeque<StatusBarNotification> mBuffer;
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500281
Griff Hazen9f637d12014-06-10 11:13:51 -0700282 public Archive(int size) {
283 mBufferSize = size;
284 mBuffer = new ArrayDeque<StatusBarNotification>(mBufferSize);
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500285 }
Jeff Sharkey0c1baf92013-04-03 13:08:52 -0700286
Daniel Sandler5e62e3a2013-04-15 20:57:02 -0400287 public String toString() {
288 final StringBuilder sb = new StringBuilder();
289 final int N = mBuffer.size();
290 sb.append("Archive (");
291 sb.append(N);
292 sb.append(" notification");
293 sb.append((N==1)?")":"s)");
294 return sb.toString();
295 }
296
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500297 public void record(StatusBarNotification nr) {
Griff Hazen9f637d12014-06-10 11:13:51 -0700298 if (mBuffer.size() == mBufferSize) {
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500299 mBuffer.removeFirst();
300 }
Daniel Sandler26b81d52013-05-20 20:56:43 -0400301
302 // We don't want to store the heavy bits of the notification in the archive,
303 // but other clients in the system process might be using the object, so we
304 // store a (lightened) copy.
305 mBuffer.addLast(nr.cloneLight());
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500306 }
307
308 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) {
Griff Hazen9f637d12014-06-10 11:13:51 -0700357 if (count == 0) count = mBufferSize;
Daniel Sandler78d0d252013-02-12 08:14:52 -0500358 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) {
Griff Hazen9f637d12014-06-10 11:13:51 -0700369 if (count == 0) count = mBufferSize;
Daniel Sandler78d0d252013-02-12 08:14:52 -0500370 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
John Spurlock056c5192014-04-20 21:52:01 -0400382 private void loadPolicyFile() {
383 synchronized(mPolicyFile) {
384 mBlockedPackages.clear();
Daniel Sandler0da673f2012-04-11 12:33:16 -0400385
John Spurlock056c5192014-04-20 21:52:01 -0400386 FileInputStream infile = null;
387 try {
388 infile = mPolicyFile.openRead();
389 final XmlPullParser parser = Xml.newPullParser();
390 parser.setInput(infile, null);
Daniel Sandler0da673f2012-04-11 12:33:16 -0400391
John Spurlock056c5192014-04-20 21:52:01 -0400392 int type;
393 String tag;
394 int version = DB_VERSION;
395 while ((type = parser.next()) != END_DOCUMENT) {
396 tag = parser.getName();
397 if (type == START_TAG) {
398 if (TAG_BODY.equals(tag)) {
399 version = Integer.parseInt(
400 parser.getAttributeValue(null, ATTR_VERSION));
401 } else if (TAG_BLOCKED_PKGS.equals(tag)) {
402 while ((type = parser.next()) != END_DOCUMENT) {
403 tag = parser.getName();
404 if (TAG_PACKAGE.equals(tag)) {
405 mBlockedPackages.add(
406 parser.getAttributeValue(null, ATTR_NAME));
407 } else if (TAG_BLOCKED_PKGS.equals(tag) && type == END_TAG) {
408 break;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400409 }
410 }
411 }
412 }
John Spurlock056c5192014-04-20 21:52:01 -0400413 mZenModeHelper.readXml(parser);
Chris Wren54bbef42014-07-09 18:37:56 -0400414 mRankingHelper.readXml(parser);
Daniel Sandler0da673f2012-04-11 12:33:16 -0400415 }
John Spurlock056c5192014-04-20 21:52:01 -0400416 } catch (FileNotFoundException e) {
417 // No data yet
418 } catch (IOException e) {
419 Log.wtf(TAG, "Unable to read notification policy", e);
420 } catch (NumberFormatException e) {
421 Log.wtf(TAG, "Unable to parse notification policy", e);
422 } catch (XmlPullParserException e) {
423 Log.wtf(TAG, "Unable to parse notification policy", e);
424 } finally {
425 IoUtils.closeQuietly(infile);
426 }
427 }
428 }
429
430 public void savePolicyFile() {
431 mHandler.removeMessages(MESSAGE_SAVE_POLICY_FILE);
432 mHandler.sendEmptyMessage(MESSAGE_SAVE_POLICY_FILE);
433 }
434
435 private void handleSavePolicyFile() {
436 Slog.d(TAG, "handleSavePolicyFile");
437 synchronized (mPolicyFile) {
438 final FileOutputStream stream;
439 try {
440 stream = mPolicyFile.startWrite();
441 } catch (IOException e) {
442 Slog.w(TAG, "Failed to save policy file", e);
443 return;
444 }
445
446 try {
447 final XmlSerializer out = new FastXmlSerializer();
448 out.setOutput(stream, "utf-8");
449 out.startDocument(null, true);
450 out.startTag(null, TAG_BODY);
451 out.attribute(null, ATTR_VERSION, Integer.toString(DB_VERSION));
452 mZenModeHelper.writeXml(out);
Chris Wren54bbef42014-07-09 18:37:56 -0400453 mRankingHelper.writeXml(out);
John Spurlock056c5192014-04-20 21:52:01 -0400454 out.endTag(null, TAG_BODY);
455 out.endDocument();
456 mPolicyFile.finishWrite(stream);
457 } catch (IOException e) {
458 Slog.w(TAG, "Failed to save policy file, restoring backup", e);
459 mPolicyFile.failWrite(stream);
Daniel Sandler0da673f2012-04-11 12:33:16 -0400460 }
461 }
462 }
463
Daniel Sandler4a900ac2013-01-30 14:04:10 -0500464 /** Use this when you actually want to post a notification or toast.
465 *
466 * Unchecked. Not exposed via Binder, but can be called in the course of enqueue*().
467 */
468 private boolean noteNotificationOp(String pkg, int uid) {
469 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
470 != AppOpsManager.MODE_ALLOWED) {
471 Slog.v(TAG, "notifications are disabled by AppOps for " + pkg);
472 return false;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400473 }
Daniel Sandler4a900ac2013-01-30 14:04:10 -0500474 return true;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400475 }
476
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800477 private static final class ToastRecord
478 {
479 final int pid;
480 final String pkg;
481 final ITransientNotification callback;
482 int duration;
483
484 ToastRecord(int pid, String pkg, ITransientNotification callback, int duration)
485 {
486 this.pid = pid;
487 this.pkg = pkg;
488 this.callback = callback;
489 this.duration = duration;
490 }
491
492 void update(int duration) {
493 this.duration = duration;
494 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800495
John Spurlock25e2d242014-06-27 13:58:23 -0400496 void dump(PrintWriter pw, String prefix, DumpFilter filter) {
497 if (filter != null && !filter.matches(pkg)) return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800498 pw.println(prefix + this);
499 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800500
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800501 @Override
502 public final String toString()
503 {
504 return "ToastRecord{"
505 + Integer.toHexString(System.identityHashCode(this))
506 + " pkg=" + pkg
507 + " callback=" + callback
508 + " duration=" + duration;
509 }
510 }
511
Adam Lesinski182f73f2013-12-05 16:48:06 -0800512 private final NotificationDelegate mNotificationDelegate = new NotificationDelegate() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800513
Adam Lesinski182f73f2013-12-05 16:48:06 -0800514 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800515 public void onSetDisabled(int status) {
516 synchronized (mNotificationList) {
John Spurlockd8afe3c2014-08-01 14:04:07 -0400517 mDisableNotificationEffects =
518 (status & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0;
John Spurlock32fe4c62014-10-02 12:16:02 -0400519 if (disableNotificationEffects(null) != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800520 // cancel whatever's going on
521 long identity = Binder.clearCallingIdentity();
522 try {
Adam Lesinski182f73f2013-12-05 16:48:06 -0800523 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
Jeff Sharkey098d5802012-04-26 17:30:34 -0700524 if (player != null) {
525 player.stopAsync();
526 }
527 } catch (RemoteException e) {
528 } finally {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800529 Binder.restoreCallingIdentity(identity);
530 }
531
532 identity = Binder.clearCallingIdentity();
533 try {
534 mVibrator.cancel();
Jeff Sharkey098d5802012-04-26 17:30:34 -0700535 } finally {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800536 Binder.restoreCallingIdentity(identity);
537 }
538 }
539 }
540 }
541
Adam Lesinski182f73f2013-12-05 16:48:06 -0800542 @Override
John Spurlocke6a7d932014-03-13 12:29:00 -0400543 public void onClearAll(int callingUid, int callingPid, int userId) {
Adam Lesinskie8240262014-03-26 16:01:00 -0700544 synchronized (mNotificationList) {
Kenny Guya263e4e2014-03-03 18:24:03 +0000545 cancelAllLocked(callingUid, callingPid, userId, REASON_DELEGATE_CANCEL_ALL, null,
546 /*includeCurrentProfiles*/ true);
Adam Lesinskie8240262014-03-26 16:01:00 -0700547 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800548 }
549
Adam Lesinski182f73f2013-12-05 16:48:06 -0800550 @Override
Christoph Studer03b87a22014-04-30 17:33:27 +0200551 public void onNotificationClick(int callingUid, int callingPid, String key) {
552 synchronized (mNotificationList) {
553 EventLogTags.writeNotificationClicked(key);
554 NotificationRecord r = mNotificationsByKey.get(key);
555 if (r == null) {
556 Log.w(TAG, "No notification with key: " + key);
557 return;
558 }
559 StatusBarNotification sbn = r.sbn;
560 cancelNotification(callingUid, callingPid, sbn.getPackageName(), sbn.getTag(),
561 sbn.getId(), Notification.FLAG_AUTO_CANCEL,
562 Notification.FLAG_FOREGROUND_SERVICE, false, r.getUserId(),
563 REASON_DELEGATE_CLICK, null);
564 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800565 }
566
Adam Lesinski182f73f2013-12-05 16:48:06 -0800567 @Override
Christoph Studer4da84cd2014-10-21 17:24:20 +0200568 public void onNotificationActionClick(int callingUid, int callingPid, String key,
569 int actionIndex) {
570 synchronized (mNotificationList) {
571 EventLogTags.writeNotificationActionClicked(key, actionIndex);
572 NotificationRecord r = mNotificationsByKey.get(key);
573 if (r == null) {
574 Log.w(TAG, "No notification with key: " + key);
575 return;
576 }
577 // TODO: Log action click via UsageStats.
578 }
579 }
580
581 @Override
John Spurlocke6a7d932014-03-13 12:29:00 -0400582 public void onNotificationClear(int callingUid, int callingPid,
583 String pkg, String tag, int id, int userId) {
584 cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
Kenny Guy3a7c4a52014-03-03 18:24:03 +0000585 Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
John Spurlocke6a7d932014-03-13 12:29:00 -0400586 true, userId, REASON_DELEGATE_CANCEL, null);
Daniel Sandler0f0b11c2010-08-04 15:54:58 -0400587 }
588
Adam Lesinski182f73f2013-12-05 16:48:06 -0800589 @Override
Christoph Studer1f32c652014-11-26 15:32:20 +0100590 public void onPanelRevealed(boolean clearEffects) {
Christoph Studer760ea552014-03-21 13:10:21 +0100591 EventLogTags.writeNotificationPanelRevealed();
Christoph Studer1f32c652014-11-26 15:32:20 +0100592 if (clearEffects) {
593 clearEffects();
594 }
595 }
596
597 @Override
598 public void onPanelHidden() {
599 EventLogTags.writeNotificationPanelHidden();
600 }
601
602 @Override
603 public void clearEffects() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800604 synchronized (mNotificationList) {
Christoph Studer1f32c652014-11-26 15:32:20 +0100605 if (DBG) Slog.d(TAG, "clearEffects");
606
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800607 // sound
Chris Wren6054e612014-11-25 17:16:46 -0500608 mSoundNotificationKey = null;
Jeff Sharkey098d5802012-04-26 17:30:34 -0700609
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800610 long identity = Binder.clearCallingIdentity();
611 try {
Adam Lesinski182f73f2013-12-05 16:48:06 -0800612 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
Jeff Sharkey098d5802012-04-26 17:30:34 -0700613 if (player != null) {
614 player.stopAsync();
615 }
616 } catch (RemoteException e) {
617 } finally {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800618 Binder.restoreCallingIdentity(identity);
619 }
620
621 // vibrate
Chris Wren6054e612014-11-25 17:16:46 -0500622 mVibrateNotificationKey = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800623 identity = Binder.clearCallingIdentity();
624 try {
625 mVibrator.cancel();
Jeff Sharkey098d5802012-04-26 17:30:34 -0700626 } finally {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800627 Binder.restoreCallingIdentity(identity);
628 }
629
630 // light
631 mLights.clear();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800632 updateLightsLocked();
633 }
634 }
Joe Onorato005847b2010-06-04 16:08:02 -0400635
Adam Lesinski182f73f2013-12-05 16:48:06 -0800636 @Override
John Spurlocke6a7d932014-03-13 12:29:00 -0400637 public void onNotificationError(int callingUid, int callingPid, String pkg, String tag, int id,
Kenny Guy3a7c4a52014-03-03 18:24:03 +0000638 int uid, int initialPid, String message, int userId) {
Daniel Sandlerd0a2f862010-08-03 15:29:31 -0400639 Slog.d(TAG, "onNotification error pkg=" + pkg + " tag=" + tag + " id=" + id
640 + "; will crashApplication(uid=" + uid + ", pid=" + initialPid + ")");
John Spurlocke6a7d932014-03-13 12:29:00 -0400641 cancelNotification(callingUid, callingPid, pkg, tag, id, 0, 0, false, userId,
642 REASON_DELEGATE_ERROR, null);
Dianne Hackborn9d39d0c2010-06-24 15:57:42 -0700643 long ident = Binder.clearCallingIdentity();
644 try {
645 ActivityManagerNative.getDefault().crashApplication(uid, initialPid, pkg,
646 "Bad notification posted from package " + pkg
647 + ": " + message);
648 } catch (RemoteException e) {
649 }
650 Binder.restoreCallingIdentity(ident);
Joe Onorato005847b2010-06-04 16:08:02 -0400651 }
John Spurlocke677d712014-02-13 12:52:19 -0500652
653 @Override
Christoph Studer92b389d2014-04-01 18:44:40 +0200654 public void onNotificationVisibilityChanged(
655 String[] newlyVisibleKeys, String[] noLongerVisibleKeys) {
656 // Using ';' as separator since eventlogs uses ',' to separate
657 // args.
658 EventLogTags.writeNotificationVisibilityChanged(
659 TextUtils.join(";", newlyVisibleKeys),
660 TextUtils.join(";", noLongerVisibleKeys));
Christoph Studerffeb0c32014-05-07 22:23:56 +0200661 synchronized (mNotificationList) {
662 for (String key : newlyVisibleKeys) {
663 NotificationRecord r = mNotificationsByKey.get(key);
664 if (r == null) continue;
665 r.stats.onVisibilityChanged(true);
666 }
667 // Note that we might receive this event after notifications
668 // have already left the system, e.g. after dismissing from the
669 // shade. Hence not finding notifications in
670 // mNotificationsByKey is not an exceptional condition.
671 for (String key : noLongerVisibleKeys) {
672 NotificationRecord r = mNotificationsByKey.get(key);
673 if (r == null) continue;
674 r.stats.onVisibilityChanged(false);
675 }
676 }
Christoph Studer92b389d2014-04-01 18:44:40 +0200677 }
Chris Wren78403d72014-07-28 10:23:24 +0100678
679 @Override
680 public void onNotificationExpansionChanged(String key,
681 boolean userAction, boolean expanded) {
682 EventLogTags.writeNotificationExpansion(key, userAction ? 1 : 0, expanded ? 1 : 0);
683 synchronized (mNotificationList) {
684 NotificationRecord r = mNotificationsByKey.get(key);
685 if (r != null) {
686 r.stats.onExpansionChanged(userAction, expanded);
687 }
688 }
689 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800690 };
691
Kenny Guy70058402014-10-28 20:45:06 +0000692 private final BroadcastReceiver mPackageIntentReceiver = new BroadcastReceiver() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800693 @Override
694 public void onReceive(Context context, Intent intent) {
695 String action = intent.getAction();
Dianne Hackborn29cd7f12015-01-08 10:37:05 -0800696 if (action == null) {
697 return;
698 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800699
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800700 boolean queryRestart = false;
Chris Wrenae9bb572013-05-15 14:50:28 -0400701 boolean queryRemove = false;
Daniel Sandler26ece572012-06-01 15:38:46 -0400702 boolean packageChanged = false;
John Spurlock79f78922013-05-16 09:10:05 -0400703 boolean cancelNotifications = true;
Chris Wrenf9536642014-04-17 10:01:54 -0400704
Chris Wren3da73022013-05-10 14:41:21 -0400705 if (action.equals(Intent.ACTION_PACKAGE_ADDED)
Chris Wrenae9bb572013-05-15 14:50:28 -0400706 || (queryRemove=action.equals(Intent.ACTION_PACKAGE_REMOVED))
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800707 || action.equals(Intent.ACTION_PACKAGE_RESTARTED)
Daniel Sandler26ece572012-06-01 15:38:46 -0400708 || (packageChanged=action.equals(Intent.ACTION_PACKAGE_CHANGED))
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800709 || (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART))
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800710 || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
Kenny Guy70058402014-10-28 20:45:06 +0000711 int changeUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
712 UserHandle.USER_ALL);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800713 String pkgList[] = null;
Chris Wrenae9bb572013-05-15 14:50:28 -0400714 boolean queryReplace = queryRemove &&
715 intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
John Spurlocke77bb362014-04-26 10:24:59 -0400716 if (DBG) Slog.i(TAG, "action=" + action + " queryReplace=" + queryReplace);
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800717 if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800718 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800719 } else if (queryRestart) {
720 pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800721 } else {
722 Uri uri = intent.getData();
723 if (uri == null) {
724 return;
725 }
726 String pkgName = uri.getSchemeSpecificPart();
727 if (pkgName == null) {
728 return;
729 }
Daniel Sandler26ece572012-06-01 15:38:46 -0400730 if (packageChanged) {
731 // We cancel notifications for packages which have just been disabled
Christopher Tate06e5fed2013-10-09 14:39:15 -0700732 try {
Kenny Guy70058402014-10-28 20:45:06 +0000733 final IPackageManager pm = AppGlobals.getPackageManager();
734 final int enabled = pm.getApplicationEnabledSetting(pkgName,
735 changeUserId != UserHandle.USER_ALL ? changeUserId :
736 UserHandle.USER_OWNER);
Christopher Tate06e5fed2013-10-09 14:39:15 -0700737 if (enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED
738 || enabled == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
739 cancelNotifications = false;
740 }
741 } catch (IllegalArgumentException e) {
742 // Package doesn't exist; probably racing with uninstall.
743 // cancelNotifications is already true, so nothing to do here.
744 if (DBG) {
745 Slog.i(TAG, "Exception trying to look up app enabled setting", e);
746 }
Kenny Guy70058402014-10-28 20:45:06 +0000747 } catch (RemoteException e) {
748 // Failed to talk to PackageManagerService Should never happen!
Daniel Sandler26ece572012-06-01 15:38:46 -0400749 }
750 }
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800751 pkgList = new String[]{pkgName};
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800752 }
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700753
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800754 if (pkgList != null && (pkgList.length > 0)) {
755 for (String pkgName : pkgList) {
John Spurlock79f78922013-05-16 09:10:05 -0400756 if (cancelNotifications) {
John Spurlocke6a7d932014-03-13 12:29:00 -0400757 cancelAllNotificationsInt(MY_UID, MY_PID, pkgName, 0, 0, !queryRestart,
Kenny Guy70058402014-10-28 20:45:06 +0000758 changeUserId, REASON_PACKAGE_CHANGED, null);
John Spurlock79f78922013-05-16 09:10:05 -0400759 }
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800760 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800761 }
John Spurlockb408e8e2014-04-23 21:12:45 -0400762 mListeners.onPackagesChanged(queryReplace, pkgList);
John Spurlock7340fc82014-04-24 18:50:12 -0400763 mConditionProviders.onPackagesChanged(queryReplace, pkgList);
Kenny Guy70058402014-10-28 20:45:06 +0000764 }
765 }
766 };
767
768 private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
769 @Override
770 public void onReceive(Context context, Intent intent) {
771 String action = intent.getAction();
772
773 if (action.equals(Intent.ACTION_SCREEN_ON)) {
Mike Lockwood63b5ad92011-08-30 09:55:30 -0400774 // Keep track of screen on/off state, but do not turn off the notification light
775 // until user passes through the lock screen or views the notification.
776 mScreenOn = true;
Christoph Studer1f32c652014-11-26 15:32:20 +0100777 updateNotificationPulse();
Mike Lockwood63b5ad92011-08-30 09:55:30 -0400778 } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
779 mScreenOn = false;
Christoph Studer1f32c652014-11-26 15:32:20 +0100780 updateNotificationPulse();
Daniel Sandlere96ffb12010-03-11 13:38:06 -0500781 } else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) {
John Spurlock5d2eeb12014-01-16 10:46:36 -0500782 mInCall = TelephonyManager.EXTRA_STATE_OFFHOOK
783 .equals(intent.getStringExtra(TelephonyManager.EXTRA_STATE));
Daniel Sandlere96ffb12010-03-11 13:38:06 -0500784 updateNotificationPulse();
Dianne Hackborn80a4af22012-08-27 19:18:31 -0700785 } else if (action.equals(Intent.ACTION_USER_STOPPED)) {
786 int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
787 if (userHandle >= 0) {
John Spurlocke6a7d932014-03-13 12:29:00 -0400788 cancelAllNotificationsInt(MY_UID, MY_PID, null, 0, 0, true, userHandle,
789 REASON_USER_STOPPED, null);
Dianne Hackborn80a4af22012-08-27 19:18:31 -0700790 }
Mike Lockwood63b5ad92011-08-30 09:55:30 -0400791 } else if (action.equals(Intent.ACTION_USER_PRESENT)) {
792 // turn off LED when user passes through lock screen
793 mNotificationLight.turnOff();
John Spurlockcb566aa2014-08-03 22:58:28 -0400794 mStatusBar.notificationLightOff();
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400795 } else if (action.equals(Intent.ACTION_USER_SWITCHED)) {
796 // reload per-user settings
797 mSettingsObserver.update(null);
John Spurlockb408e8e2014-04-23 21:12:45 -0400798 mUserProfiles.updateCache(context);
Christoph Studerb53dfd42014-09-12 14:45:59 +0200799 // Refresh managed services
800 mConditionProviders.onUserSwitched();
801 mListeners.onUserSwitched();
Kenny Guy3a7c4a52014-03-03 18:24:03 +0000802 } else if (action.equals(Intent.ACTION_USER_ADDED)) {
John Spurlockb408e8e2014-04-23 21:12:45 -0400803 mUserProfiles.updateCache(context);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800804 }
805 }
806 };
807
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700808 class SettingsObserver extends ContentObserver {
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400809 private final Uri NOTIFICATION_LIGHT_PULSE_URI
810 = Settings.System.getUriFor(Settings.System.NOTIFICATION_LIGHT_PULSE);
811
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700812 SettingsObserver(Handler handler) {
813 super(handler);
814 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800815
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700816 void observe() {
Adam Lesinski182f73f2013-12-05 16:48:06 -0800817 ContentResolver resolver = getContext().getContentResolver();
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400818 resolver.registerContentObserver(NOTIFICATION_LIGHT_PULSE_URI,
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700819 false, this, UserHandle.USER_ALL);
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400820 update(null);
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700821 }
822
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400823 @Override public void onChange(boolean selfChange, Uri uri) {
824 update(uri);
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700825 }
826
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400827 public void update(Uri uri) {
Adam Lesinski182f73f2013-12-05 16:48:06 -0800828 ContentResolver resolver = getContext().getContentResolver();
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400829 if (uri == null || NOTIFICATION_LIGHT_PULSE_URI.equals(uri)) {
830 boolean pulseEnabled = Settings.System.getInt(resolver,
831 Settings.System.NOTIFICATION_LIGHT_PULSE, 0) != 0;
832 if (mNotificationPulseEnabled != pulseEnabled) {
833 mNotificationPulseEnabled = pulseEnabled;
834 updateNotificationPulse();
835 }
836 }
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700837 }
838 }
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500839
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400840 private SettingsObserver mSettingsObserver;
John Spurlock056c5192014-04-20 21:52:01 -0400841 private ZenModeHelper mZenModeHelper;
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400842
John Spurlockcad57682014-07-26 17:09:56 -0400843 private final Runnable mBuzzBeepBlinked = new Runnable() {
844 @Override
845 public void run() {
846 mStatusBar.buzzBeepBlinked();
847 }
848 };
849
Daniel Sandleredbb3802012-11-13 20:49:47 -0800850 static long[] getLongArray(Resources r, int resid, int maxlen, long[] def) {
851 int[] ar = r.getIntArray(resid);
852 if (ar == null) {
853 return def;
854 }
855 final int len = ar.length > maxlen ? maxlen : ar.length;
856 long[] out = new long[len];
857 for (int i=0; i<len; i++) {
858 out[i] = ar[i];
859 }
860 return out;
861 }
862
Jeff Brownb880d882014-02-10 19:47:07 -0800863 public NotificationManagerService(Context context) {
864 super(context);
865 }
866
Adam Lesinski182f73f2013-12-05 16:48:06 -0800867 @Override
868 public void onStart() {
Chris Wren54bbef42014-07-09 18:37:56 -0400869 Resources resources = getContext().getResources();
870
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800871 mAm = ActivityManagerNative.getDefault();
Adam Lesinski182f73f2013-12-05 16:48:06 -0800872 mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
873 mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
San Mehat3ee13172010-02-04 20:54:43 -0800874
Adam Lesinski182f73f2013-12-05 16:48:06 -0800875 mHandler = new WorkerHandler();
Chris Wrenf9536642014-04-17 10:01:54 -0400876 mRankingThread.start();
Chris Wren54bbef42014-07-09 18:37:56 -0400877 String[] extractorNames;
878 try {
879 extractorNames = resources.getStringArray(R.array.config_notificationSignalExtractors);
880 } catch (Resources.NotFoundException e) {
881 extractorNames = new String[0];
882 }
883 mRankingHelper = new RankingHelper(getContext(),
884 new RankingWorkerHandler(mRankingThread.getLooper()),
885 extractorNames);
John Spurlockb2278d62015-04-07 12:47:12 -0400886 mConditionProviders = new ConditionProviders(getContext(), mHandler, mUserProfiles);
887 mZenModeHelper = new ZenModeHelper(getContext(), mHandler.getLooper(), mConditionProviders);
John Spurlock1c923a32014-04-27 16:42:29 -0400888 mZenModeHelper.addCallback(new ZenModeHelper.Callback() {
John Spurlock056c5192014-04-20 21:52:01 -0400889 @Override
890 public void onConfigChanged() {
891 savePolicyFile();
892 }
John Spurlockd8afe3c2014-08-01 14:04:07 -0400893
894 @Override
895 void onZenModeChanged() {
896 synchronized(mNotificationList) {
Christoph Studer85a384b2014-08-27 20:16:15 +0200897 updateInterruptionFilterLocked();
John Spurlockd8afe3c2014-08-01 14:04:07 -0400898 }
899 }
John Spurlock1fc476d2015-04-14 16:05:20 -0400900
901 @Override
902 void onPolicyChanged() {
903 getContext().sendBroadcast(
904 new Intent(NotificationManager.ACTION_NOTIFICATION_POLICY_CHANGED)
905 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY));
906 }
John Spurlock056c5192014-04-20 21:52:01 -0400907 });
908 final File systemDir = new File(Environment.getDataDirectory(), "system");
909 mPolicyFile = new AtomicFile(new File(systemDir, "notification_policy.xml"));
Christoph Studer1c3f81f2014-04-16 15:05:56 +0200910 mUsageStats = new NotificationUsageStats(getContext());
Daniel Sandler4a900ac2013-01-30 14:04:10 -0500911
912 importOldBlockDb();
Daniel Sandler0da673f2012-04-11 12:33:16 -0400913
John Spurlock7340fc82014-04-24 18:50:12 -0400914 mListeners = new NotificationListeners();
Adam Lesinski182f73f2013-12-05 16:48:06 -0800915 mStatusBar = getLocalService(StatusBarManagerInternal.class);
916 mStatusBar.setNotificationDelegate(mNotificationDelegate);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800917
Adam Lesinski182f73f2013-12-05 16:48:06 -0800918 final LightsManager lights = getLocalService(LightsManager.class);
919 mNotificationLight = lights.getLight(LightsManager.LIGHT_ID_NOTIFICATIONS);
920 mAttentionLight = lights.getLight(LightsManager.LIGHT_ID_ATTENTION);
Mike Lockwood3cb67a32009-11-27 14:25:58 -0500921
Mike Lockwood670f9322010-01-20 12:13:36 -0500922 mDefaultNotificationColor = resources.getColor(
Scott Greenwald9a05b312013-06-28 00:37:54 -0400923 R.color.config_defaultNotificationColor);
Mike Lockwood670f9322010-01-20 12:13:36 -0500924 mDefaultNotificationLedOn = resources.getInteger(
Scott Greenwald9a05b312013-06-28 00:37:54 -0400925 R.integer.config_defaultNotificationLedOn);
Mike Lockwood670f9322010-01-20 12:13:36 -0500926 mDefaultNotificationLedOff = resources.getInteger(
Scott Greenwald9a05b312013-06-28 00:37:54 -0400927 R.integer.config_defaultNotificationLedOff);
Mike Lockwood670f9322010-01-20 12:13:36 -0500928
Daniel Sandleredbb3802012-11-13 20:49:47 -0800929 mDefaultVibrationPattern = getLongArray(resources,
Scott Greenwald9a05b312013-06-28 00:37:54 -0400930 R.array.config_defaultNotificationVibePattern,
Daniel Sandleredbb3802012-11-13 20:49:47 -0800931 VIBRATE_PATTERN_MAXLEN,
932 DEFAULT_VIBRATE_PATTERN);
933
934 mFallbackVibrationPattern = getLongArray(resources,
Scott Greenwald9a05b312013-06-28 00:37:54 -0400935 R.array.config_notificationFallbackVibePattern,
Daniel Sandleredbb3802012-11-13 20:49:47 -0800936 VIBRATE_PATTERN_MAXLEN,
937 DEFAULT_VIBRATE_PATTERN);
938
Chris Wren5116a822014-06-04 15:59:50 -0400939 mUseAttentionLight = resources.getBoolean(R.bool.config_useAttentionLight);
940
Joe Onorato39f5b6a2009-07-23 12:29:19 -0400941 // Don't start allowing notifications until the setup wizard has run once.
942 // After that, including subsequent boots, init with notifications turned on.
943 // This works on the first boot because the setup wizard will toggle this
944 // flag at least once and we'll go back to 0 after that.
Adam Lesinski182f73f2013-12-05 16:48:06 -0800945 if (0 == Settings.Global.getInt(getContext().getContentResolver(),
Jeff Brownbf6f6f92012-09-25 15:03:20 -0700946 Settings.Global.DEVICE_PROVISIONED, 0)) {
John Spurlockd8afe3c2014-08-01 14:04:07 -0400947 mDisableNotificationEffects = true;
Joe Onorato39f5b6a2009-07-23 12:29:19 -0400948 }
John Spurlockb2278d62015-04-07 12:47:12 -0400949 mZenModeHelper.initZenMode();
John Spurlockf3701772015-02-12 13:29:37 -0500950 mInterruptionFilter = mZenModeHelper.getZenModeListenerInterruptionFilter();
Joe Onorato39f5b6a2009-07-23 12:29:19 -0400951
John Spurlockb408e8e2014-04-23 21:12:45 -0400952 mUserProfiles.updateCache(getContext());
John Spurlock32fe4c62014-10-02 12:16:02 -0400953 listenForCallState();
Kenny Guya263e4e2014-03-03 18:24:03 +0000954
Mike Lockwood35e16bf2010-11-30 19:53:36 -0500955 // register for various Intents
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800956 IntentFilter filter = new IntentFilter();
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500957 filter.addAction(Intent.ACTION_SCREEN_ON);
958 filter.addAction(Intent.ACTION_SCREEN_OFF);
Daniel Sandlere96ffb12010-03-11 13:38:06 -0500959 filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
Mike Lockwood63b5ad92011-08-30 09:55:30 -0400960 filter.addAction(Intent.ACTION_USER_PRESENT);
Dianne Hackborn80a4af22012-08-27 19:18:31 -0700961 filter.addAction(Intent.ACTION_USER_STOPPED);
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400962 filter.addAction(Intent.ACTION_USER_SWITCHED);
Kenny Guy3a7c4a52014-03-03 18:24:03 +0000963 filter.addAction(Intent.ACTION_USER_ADDED);
Adam Lesinski182f73f2013-12-05 16:48:06 -0800964 getContext().registerReceiver(mIntentReceiver, filter);
Kenny Guy70058402014-10-28 20:45:06 +0000965
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800966 IntentFilter pkgFilter = new IntentFilter();
Chris Wren3da73022013-05-10 14:41:21 -0400967 pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800968 pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
Daniel Sandleraac0eb02011-08-06 22:51:56 -0400969 pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800970 pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
971 pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
972 pkgFilter.addDataScheme("package");
Kenny Guy70058402014-10-28 20:45:06 +0000973 getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, pkgFilter, null,
974 null);
975
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800976 IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
Kenny Guy70058402014-10-28 20:45:06 +0000977 getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, sdFilter, null,
978 null);
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800979
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400980 mSettingsObserver = new SettingsObserver(mHandler);
Scott Greenwald9a05b312013-06-28 00:37:54 -0400981
Griff Hazen9f637d12014-06-10 11:13:51 -0700982 mArchive = new Archive(resources.getInteger(
983 R.integer.config_notificationServiceArchiveSize));
984
Adam Lesinski182f73f2013-12-05 16:48:06 -0800985 publishBinderService(Context.NOTIFICATION_SERVICE, mService);
986 publishLocalService(NotificationManagerInternal.class, mInternalService);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800987 }
988
Daniel Sandler4a900ac2013-01-30 14:04:10 -0500989 /**
990 * Read the old XML-based app block database and import those blockages into the AppOps system.
991 */
992 private void importOldBlockDb() {
John Spurlock056c5192014-04-20 21:52:01 -0400993 loadPolicyFile();
Daniel Sandler4a900ac2013-01-30 14:04:10 -0500994
Adam Lesinski182f73f2013-12-05 16:48:06 -0800995 PackageManager pm = getContext().getPackageManager();
Daniel Sandler4a900ac2013-01-30 14:04:10 -0500996 for (String pkg : mBlockedPackages) {
997 PackageInfo info = null;
998 try {
999 info = pm.getPackageInfo(pkg, 0);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001000 setNotificationsEnabledForPackageImpl(pkg, info.applicationInfo.uid, false);
Daniel Sandler4a900ac2013-01-30 14:04:10 -05001001 } catch (NameNotFoundException e) {
1002 // forget you
1003 }
1004 }
1005 mBlockedPackages.clear();
Daniel Sandler4a900ac2013-01-30 14:04:10 -05001006 }
1007
Adam Lesinski182f73f2013-12-05 16:48:06 -08001008 @Override
1009 public void onBootPhase(int phase) {
1010 if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
1011 // no beeping until we're basically done booting
1012 mSystemReady = true;
Jeff Sharkey098d5802012-04-26 17:30:34 -07001013
Adam Lesinski182f73f2013-12-05 16:48:06 -08001014 // Grab our optional AudioService
1015 mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
John Spurlockcdb57ae2015-02-11 19:04:11 -05001016 mAudioManagerInternal = getLocalService(AudioManagerInternal.class);
John Spurlock661f2cf2014-11-17 10:29:10 -05001017 mZenModeHelper.onSystemReady();
Adam Lesinskia6db4ab2014-03-24 12:31:45 -07001018 } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
1019 // This observer will force an update when observe is called, causing us to
1020 // bind to listener services.
1021 mSettingsObserver.observe();
John Spurlockb408e8e2014-04-23 21:12:45 -04001022 mListeners.onBootPhaseAppsCanStart();
John Spurlock7340fc82014-04-24 18:50:12 -04001023 mConditionProviders.onBootPhaseAppsCanStart();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001024 }
1025 }
1026
Adam Lesinski182f73f2013-12-05 16:48:06 -08001027 void setNotificationsEnabledForPackageImpl(String pkg, int uid, boolean enabled) {
1028 Slog.v(TAG, (enabled?"en":"dis") + "abling notifications for " + pkg);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001029
Adam Lesinski182f73f2013-12-05 16:48:06 -08001030 mAppOps.setMode(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg,
1031 enabled ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001032
Adam Lesinski182f73f2013-12-05 16:48:06 -08001033 // Now, cancel any outstanding notifications that are part of a just-disabled app
1034 if (ENABLE_BLOCKED_NOTIFICATIONS && !enabled) {
John Spurlocke6a7d932014-03-13 12:29:00 -04001035 cancelAllNotificationsInt(MY_UID, MY_PID, pkg, 0, 0, true, UserHandle.getUserId(uid),
1036 REASON_PACKAGE_BANNED, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001037 }
1038 }
1039
John Spurlockd8afe3c2014-08-01 14:04:07 -04001040 private void updateListenerHintsLocked() {
Christoph Studer85a384b2014-08-27 20:16:15 +02001041 final int hints = mListenersDisablingEffects.isEmpty() ? 0 : HINT_HOST_DISABLE_EFFECTS;
John Spurlockd8afe3c2014-08-01 14:04:07 -04001042 if (hints == mListenerHints) return;
John Spurlocka7082992015-03-09 12:19:23 -04001043 ZenLog.traceListenerHintsChanged(mListenerHints, hints, mListenersDisablingEffects.size());
John Spurlockd8afe3c2014-08-01 14:04:07 -04001044 mListenerHints = hints;
1045 scheduleListenerHintsChanged(hints);
John Spurlock1fa865f2014-07-21 14:56:39 -04001046 }
1047
John Spurlockb4782522014-08-22 14:54:46 -04001048 private void updateEffectsSuppressorLocked() {
1049 final ComponentName suppressor = !mListenersDisablingEffects.isEmpty()
1050 ? mListenersDisablingEffects.valueAt(0).component : null;
1051 if (Objects.equals(suppressor, mEffectsSuppressor)) return;
John Spurlocka7082992015-03-09 12:19:23 -04001052 ZenLog.traceEffectsSuppressorChanged(mEffectsSuppressor, suppressor);
John Spurlockb4782522014-08-22 14:54:46 -04001053 mEffectsSuppressor = suppressor;
John Spurlock8403b752014-12-10 12:47:01 -05001054 mZenModeHelper.setEffectsSuppressed(suppressor != null);
John Spurlockb4782522014-08-22 14:54:46 -04001055 getContext().sendBroadcast(new Intent(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED)
1056 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY));
1057 }
1058
Christoph Studer85a384b2014-08-27 20:16:15 +02001059 private void updateInterruptionFilterLocked() {
1060 int interruptionFilter = mZenModeHelper.getZenModeListenerInterruptionFilter();
1061 if (interruptionFilter == mInterruptionFilter) return;
1062 mInterruptionFilter = interruptionFilter;
1063 scheduleInterruptionFilterChanged(interruptionFilter);
1064 }
1065
Adam Lesinski182f73f2013-12-05 16:48:06 -08001066 private final IBinder mService = new INotificationManager.Stub() {
1067 // Toasts
1068 // ============================================================================
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001069
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001070 @Override
Adam Lesinski182f73f2013-12-05 16:48:06 -08001071 public void enqueueToast(String pkg, ITransientNotification callback, int duration)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001072 {
Adam Lesinski182f73f2013-12-05 16:48:06 -08001073 if (DBG) {
1074 Slog.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback
1075 + " duration=" + duration);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001076 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001077
1078 if (pkg == null || callback == null) {
1079 Slog.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback);
1080 return ;
1081 }
1082
John Spurlock7340fc82014-04-24 18:50:12 -04001083 final boolean isSystemToast = isCallerSystem() || ("android".equals(pkg));
Adam Lesinski182f73f2013-12-05 16:48:06 -08001084
1085 if (ENABLE_BLOCKED_TOASTS && !noteNotificationOp(pkg, Binder.getCallingUid())) {
1086 if (!isSystemToast) {
1087 Slog.e(TAG, "Suppressing toast from package " + pkg + " by user request.");
1088 return;
1089 }
1090 }
1091
1092 synchronized (mToastQueue) {
1093 int callingPid = Binder.getCallingPid();
1094 long callingId = Binder.clearCallingIdentity();
1095 try {
1096 ToastRecord record;
1097 int index = indexOfToastLocked(pkg, callback);
1098 // If it's already in the queue, we update it in place, we don't
1099 // move it to the end of the queue.
1100 if (index >= 0) {
1101 record = mToastQueue.get(index);
1102 record.update(duration);
1103 } else {
1104 // Limit the number of toasts that any given package except the android
1105 // package can enqueue. Prevents DOS attacks and deals with leaks.
1106 if (!isSystemToast) {
1107 int count = 0;
1108 final int N = mToastQueue.size();
1109 for (int i=0; i<N; i++) {
1110 final ToastRecord r = mToastQueue.get(i);
1111 if (r.pkg.equals(pkg)) {
1112 count++;
1113 if (count >= MAX_PACKAGE_NOTIFICATIONS) {
1114 Slog.e(TAG, "Package has already posted " + count
1115 + " toasts. Not showing more. Package=" + pkg);
1116 return;
1117 }
1118 }
1119 }
1120 }
1121
1122 record = new ToastRecord(callingPid, pkg, callback, duration);
1123 mToastQueue.add(record);
1124 index = mToastQueue.size() - 1;
1125 keepProcessAliveLocked(callingPid);
1126 }
1127 // If it's at index 0, it's the current toast. It doesn't matter if it's
1128 // new or just been updated. Call back and tell it to show itself.
1129 // If the callback fails, this will remove it from the list, so don't
1130 // assume that it's valid after this.
1131 if (index == 0) {
1132 showNextToastLocked();
1133 }
1134 } finally {
1135 Binder.restoreCallingIdentity(callingId);
1136 }
1137 }
1138 }
1139
1140 @Override
1141 public void cancelToast(String pkg, ITransientNotification callback) {
1142 Slog.i(TAG, "cancelToast pkg=" + pkg + " callback=" + callback);
1143
1144 if (pkg == null || callback == null) {
1145 Slog.e(TAG, "Not cancelling notification. pkg=" + pkg + " callback=" + callback);
1146 return ;
1147 }
1148
1149 synchronized (mToastQueue) {
1150 long callingId = Binder.clearCallingIdentity();
1151 try {
1152 int index = indexOfToastLocked(pkg, callback);
1153 if (index >= 0) {
1154 cancelToastLocked(index);
1155 } else {
1156 Slog.w(TAG, "Toast already cancelled. pkg=" + pkg
1157 + " callback=" + callback);
1158 }
1159 } finally {
1160 Binder.restoreCallingIdentity(callingId);
1161 }
1162 }
1163 }
1164
1165 @Override
Christoph Studer8fd7f1e2014-04-11 17:35:05 -04001166 public void enqueueNotificationWithTag(String pkg, String opPkg, String tag, int id,
Adam Lesinski182f73f2013-12-05 16:48:06 -08001167 Notification notification, int[] idOut, int userId) throws RemoteException {
Christoph Studer8fd7f1e2014-04-11 17:35:05 -04001168 enqueueNotificationInternal(pkg, opPkg, Binder.getCallingUid(),
Adam Lesinski182f73f2013-12-05 16:48:06 -08001169 Binder.getCallingPid(), tag, id, notification, idOut, userId);
1170 }
1171
1172 @Override
1173 public void cancelNotificationWithTag(String pkg, String tag, int id, int userId) {
John Spurlock7340fc82014-04-24 18:50:12 -04001174 checkCallerIsSystemOrSameApp(pkg);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001175 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1176 Binder.getCallingUid(), userId, true, false, "cancelNotificationWithTag", pkg);
1177 // Don't allow client applications to cancel foreground service notis.
John Spurlocke6a7d932014-03-13 12:29:00 -04001178 cancelNotification(Binder.getCallingUid(), Binder.getCallingPid(), pkg, tag, id, 0,
Adam Lesinski182f73f2013-12-05 16:48:06 -08001179 Binder.getCallingUid() == Process.SYSTEM_UID
John Spurlocke6a7d932014-03-13 12:29:00 -04001180 ? 0 : Notification.FLAG_FOREGROUND_SERVICE, false, userId, REASON_NOMAN_CANCEL,
1181 null);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001182 }
1183
1184 @Override
1185 public void cancelAllNotifications(String pkg, int userId) {
John Spurlock7340fc82014-04-24 18:50:12 -04001186 checkCallerIsSystemOrSameApp(pkg);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001187
1188 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1189 Binder.getCallingUid(), userId, true, false, "cancelAllNotifications", pkg);
1190
1191 // Calling from user space, don't allow the canceling of actively
1192 // running foreground services.
John Spurlocke6a7d932014-03-13 12:29:00 -04001193 cancelAllNotificationsInt(Binder.getCallingUid(), Binder.getCallingPid(),
1194 pkg, 0, Notification.FLAG_FOREGROUND_SERVICE, true, userId,
1195 REASON_NOMAN_CANCEL_ALL, null);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001196 }
1197
1198 @Override
1199 public void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) {
John Spurlock7340fc82014-04-24 18:50:12 -04001200 checkCallerIsSystem();
Adam Lesinski182f73f2013-12-05 16:48:06 -08001201
1202 setNotificationsEnabledForPackageImpl(pkg, uid, enabled);
1203 }
1204
1205 /**
1206 * Use this when you just want to know if notifications are OK for this package.
1207 */
1208 @Override
1209 public boolean areNotificationsEnabledForPackage(String pkg, int uid) {
John Spurlock7340fc82014-04-24 18:50:12 -04001210 checkCallerIsSystem();
Adam Lesinski182f73f2013-12-05 16:48:06 -08001211 return (mAppOps.checkOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
1212 == AppOpsManager.MODE_ALLOWED);
1213 }
1214
Chris Wren54bbef42014-07-09 18:37:56 -04001215 @Override
1216 public void setPackagePriority(String pkg, int uid, int priority) {
1217 checkCallerIsSystem();
1218 mRankingHelper.setPackagePriority(pkg, uid, priority);
1219 savePolicyFile();
1220 }
1221
1222 @Override
1223 public int getPackagePriority(String pkg, int uid) {
1224 checkCallerIsSystem();
1225 return mRankingHelper.getPackagePriority(pkg, uid);
1226 }
1227
Chris Wren3ad4e3a2014-09-02 17:23:51 -04001228 @Override
John Spurlock1d881a12015-03-18 19:21:54 -04001229 public void setPackagePeekable(String pkg, int uid, boolean peekable) {
1230 checkCallerIsSystem();
1231
1232 mRankingHelper.setPackagePeekable(pkg, uid, peekable);
1233 }
1234
1235 @Override
1236 public boolean getPackagePeekable(String pkg, int uid) {
1237 checkCallerIsSystem();
1238 return mRankingHelper.getPackagePeekable(pkg, uid);
1239 }
1240
1241 @Override
Chris Wren3ad4e3a2014-09-02 17:23:51 -04001242 public void setPackageVisibilityOverride(String pkg, int uid, int visibility) {
1243 checkCallerIsSystem();
1244 mRankingHelper.setPackageVisibilityOverride(pkg, uid, visibility);
1245 savePolicyFile();
1246 }
1247
1248 @Override
1249 public int getPackageVisibilityOverride(String pkg, int uid) {
1250 checkCallerIsSystem();
1251 return mRankingHelper.getPackageVisibilityOverride(pkg, uid);
1252 }
1253
Adam Lesinski182f73f2013-12-05 16:48:06 -08001254 /**
1255 * System-only API for getting a list of current (i.e. not cleared) notifications.
1256 *
1257 * Requires ACCESS_NOTIFICATIONS which is signature|system.
Chris Wrenf9536642014-04-17 10:01:54 -04001258 * @returns A list of all the notifications, in natural order.
Adam Lesinski182f73f2013-12-05 16:48:06 -08001259 */
1260 @Override
1261 public StatusBarNotification[] getActiveNotifications(String callingPkg) {
1262 // enforce() will ensure the calling uid has the correct permission
1263 getContext().enforceCallingOrSelfPermission(
1264 android.Manifest.permission.ACCESS_NOTIFICATIONS,
1265 "NotificationManagerService.getActiveNotifications");
1266
1267 StatusBarNotification[] tmp = null;
1268 int uid = Binder.getCallingUid();
1269
1270 // noteOp will check to make sure the callingPkg matches the uid
1271 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
1272 == AppOpsManager.MODE_ALLOWED) {
1273 synchronized (mNotificationList) {
1274 tmp = new StatusBarNotification[mNotificationList.size()];
1275 final int N = mNotificationList.size();
1276 for (int i=0; i<N; i++) {
1277 tmp[i] = mNotificationList.get(i).sbn;
1278 }
1279 }
1280 }
1281 return tmp;
1282 }
1283
1284 /**
Dan Sandler994349c2015-04-15 11:02:54 -04001285 * Public API for getting a list of current notifications for the calling package/uid.
1286 *
1287 * @returns A list of all the package's notifications, in natural order.
1288 */
1289 @Override
1290 public ParceledListSlice<StatusBarNotification> getAppActiveNotifications(String pkg,
1291 int incomingUserId) {
1292 checkCallerIsSystemOrSameApp(pkg);
1293 int userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1294 Binder.getCallingUid(), incomingUserId, true, false,
1295 "getAppActiveNotifications", pkg);
1296
1297 final int N = mNotificationList.size();
1298 final ArrayList<StatusBarNotification> list = new ArrayList<StatusBarNotification>(N);
1299
1300 synchronized (mNotificationList) {
1301 for (int i = 0; i < N; i++) {
1302 final StatusBarNotification sbn = mNotificationList.get(i).sbn;
1303 if (sbn.getPackageName().equals(pkg) && sbn.getUserId() == userId) {
1304 // We could pass back a cloneLight() but clients might get confused and
1305 // try to send this thing back to notify() again, which would not work
1306 // very well.
1307 final StatusBarNotification sbnOut = new StatusBarNotification(
1308 sbn.getPackageName(),
1309 sbn.getOpPkg(),
1310 sbn.getId(), sbn.getTag(), sbn.getUid(), sbn.getInitialPid(),
1311 0, // hide score from apps
1312 sbn.getNotification().clone(),
1313 sbn.getUser(), sbn.getPostTime());
1314 list.add(sbnOut);
1315 }
1316 }
1317 }
1318
1319 return new ParceledListSlice<StatusBarNotification>(list);
1320 }
1321
1322 /**
Adam Lesinski182f73f2013-12-05 16:48:06 -08001323 * System-only API for getting a list of recent (cleared, no longer shown) notifications.
1324 *
1325 * Requires ACCESS_NOTIFICATIONS which is signature|system.
1326 */
1327 @Override
1328 public StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count) {
1329 // enforce() will ensure the calling uid has the correct permission
1330 getContext().enforceCallingOrSelfPermission(
1331 android.Manifest.permission.ACCESS_NOTIFICATIONS,
1332 "NotificationManagerService.getHistoricalNotifications");
1333
1334 StatusBarNotification[] tmp = null;
1335 int uid = Binder.getCallingUid();
1336
1337 // noteOp will check to make sure the callingPkg matches the uid
1338 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
1339 == AppOpsManager.MODE_ALLOWED) {
1340 synchronized (mArchive) {
1341 tmp = mArchive.getArray(count);
1342 }
1343 }
1344 return tmp;
1345 }
1346
1347 /**
1348 * Register a listener binder directly with the notification manager.
1349 *
1350 * Only works with system callers. Apps should extend
1351 * {@link android.service.notification.NotificationListenerService}.
1352 */
1353 @Override
1354 public void registerListener(final INotificationListener listener,
1355 final ComponentName component, final int userid) {
Christoph Studer3e144d32014-05-22 16:48:40 +02001356 enforceSystemOrSystemUI("INotificationManager.registerListener");
John Spurlock7340fc82014-04-24 18:50:12 -04001357 mListeners.registerService(listener, component, userid);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001358 }
1359
1360 /**
1361 * Remove a listener binder directly
1362 */
1363 @Override
1364 public void unregisterListener(INotificationListener listener, int userid) {
John Spurlock7340fc82014-04-24 18:50:12 -04001365 mListeners.unregisterService(listener, userid);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001366 }
1367
1368 /**
1369 * Allow an INotificationListener to simulate a "clear all" operation.
1370 *
1371 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onClearAllNotifications}
1372 *
1373 * @param token The binder for the listener, to check that the caller is allowed
1374 */
1375 @Override
John Spurlocka4294292014-03-24 18:02:32 -04001376 public void cancelNotificationsFromListener(INotificationListener token, String[] keys) {
John Spurlocke6a7d932014-03-13 12:29:00 -04001377 final int callingUid = Binder.getCallingUid();
1378 final int callingPid = Binder.getCallingPid();
Adam Lesinski182f73f2013-12-05 16:48:06 -08001379 long identity = Binder.clearCallingIdentity();
1380 try {
Adam Lesinskie8240262014-03-26 16:01:00 -07001381 synchronized (mNotificationList) {
John Spurlock7340fc82014-04-24 18:50:12 -04001382 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
John Spurlocka4294292014-03-24 18:02:32 -04001383 if (keys != null) {
1384 final int N = keys.length;
1385 for (int i = 0; i < N; i++) {
1386 NotificationRecord r = mNotificationsByKey.get(keys[i]);
Griff Hazen335e1f02014-09-11 14:49:31 -07001387 if (r == null) continue;
Kenny Guya263e4e2014-03-03 18:24:03 +00001388 final int userId = r.sbn.getUserId();
1389 if (userId != info.userid && userId != UserHandle.USER_ALL &&
John Spurlockb408e8e2014-04-23 21:12:45 -04001390 !mUserProfiles.isCurrentProfile(userId)) {
Kenny Guya263e4e2014-03-03 18:24:03 +00001391 throw new SecurityException("Disallowed call from listener: "
John Spurlock7340fc82014-04-24 18:50:12 -04001392 + info.service);
Kenny Guya263e4e2014-03-03 18:24:03 +00001393 }
Griff Hazen335e1f02014-09-11 14:49:31 -07001394 cancelNotificationFromListenerLocked(info, callingUid, callingPid,
1395 r.sbn.getPackageName(), r.sbn.getTag(), r.sbn.getId(),
1396 userId);
John Spurlocka4294292014-03-24 18:02:32 -04001397 }
1398 } else {
1399 cancelAllLocked(callingUid, callingPid, info.userid,
Kenny Guya263e4e2014-03-03 18:24:03 +00001400 REASON_LISTENER_CANCEL_ALL, info, info.supportsProfiles());
John Spurlocka4294292014-03-24 18:02:32 -04001401 }
Adam Lesinskie8240262014-03-26 16:01:00 -07001402 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001403 } finally {
1404 Binder.restoreCallingIdentity(identity);
1405 }
1406 }
1407
John Spurlock7340fc82014-04-24 18:50:12 -04001408 private void cancelNotificationFromListenerLocked(ManagedServiceInfo info,
Kenny Guya263e4e2014-03-03 18:24:03 +00001409 int callingUid, int callingPid, String pkg, String tag, int id, int userId) {
John Spurlocka4294292014-03-24 18:02:32 -04001410 cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
1411 Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
1412 true,
Kenny Guya263e4e2014-03-03 18:24:03 +00001413 userId, REASON_LISTENER_CANCEL, info);
John Spurlocka4294292014-03-24 18:02:32 -04001414 }
1415
Adam Lesinski182f73f2013-12-05 16:48:06 -08001416 /**
1417 * Allow an INotificationListener to simulate clearing (dismissing) a single notification.
1418 *
1419 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onNotificationClear}
1420 *
1421 * @param token The binder for the listener, to check that the caller is allowed
1422 */
1423 @Override
1424 public void cancelNotificationFromListener(INotificationListener token, String pkg,
1425 String tag, int id) {
John Spurlocke6a7d932014-03-13 12:29:00 -04001426 final int callingUid = Binder.getCallingUid();
1427 final int callingPid = Binder.getCallingPid();
Adam Lesinski182f73f2013-12-05 16:48:06 -08001428 long identity = Binder.clearCallingIdentity();
1429 try {
Adam Lesinskie8240262014-03-26 16:01:00 -07001430 synchronized (mNotificationList) {
John Spurlock7340fc82014-04-24 18:50:12 -04001431 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
Kenny Guya263e4e2014-03-03 18:24:03 +00001432 if (info.supportsProfiles()) {
1433 Log.e(TAG, "Ignoring deprecated cancelNotification(pkg, tag, id) "
1434 + "from " + info.component
1435 + " use cancelNotification(key) instead.");
1436 } else {
1437 cancelNotificationFromListenerLocked(info, callingUid, callingPid,
1438 pkg, tag, id, info.userid);
1439 }
Adam Lesinskie8240262014-03-26 16:01:00 -07001440 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001441 } finally {
1442 Binder.restoreCallingIdentity(identity);
1443 }
1444 }
1445
1446 /**
1447 * Allow an INotificationListener to request the list of outstanding notifications seen by
1448 * the current user. Useful when starting up, after which point the listener callbacks
1449 * should be used.
1450 *
1451 * @param token The binder for the listener, to check that the caller is allowed
Dan Sandlerea75fdd2014-08-12 12:29:19 -04001452 * @param keys An array of notification keys to fetch, or null to fetch everything
Chris Wrenf9536642014-04-17 10:01:54 -04001453 * @returns The return value will contain the notifications specified in keys, in that
1454 * order, or if keys is null, all the notifications, in natural order.
Adam Lesinski182f73f2013-12-05 16:48:06 -08001455 */
1456 @Override
Christoph Studercee44ba2014-05-20 18:36:43 +02001457 public ParceledListSlice<StatusBarNotification> getActiveNotificationsFromListener(
Christoph Studerb82bc782014-08-20 14:29:43 +02001458 INotificationListener token, String[] keys, int trim) {
Adam Lesinski182f73f2013-12-05 16:48:06 -08001459 synchronized (mNotificationList) {
John Spurlock7340fc82014-04-24 18:50:12 -04001460 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
Dan Sandlerea75fdd2014-08-12 12:29:19 -04001461 final boolean getKeys = keys != null;
1462 final int N = getKeys ? keys.length : mNotificationList.size();
Christoph Studerb82bc782014-08-20 14:29:43 +02001463 final ArrayList<StatusBarNotification> list
1464 = new ArrayList<StatusBarNotification>(N);
Christoph Studercee44ba2014-05-20 18:36:43 +02001465 for (int i=0; i<N; i++) {
Dan Sandlerea75fdd2014-08-12 12:29:19 -04001466 final NotificationRecord r = getKeys
1467 ? mNotificationsByKey.get(keys[i])
1468 : mNotificationList.get(i);
Christoph Studerb82bc782014-08-20 14:29:43 +02001469 if (r == null) continue;
1470 StatusBarNotification sbn = r.sbn;
1471 if (!isVisibleToListener(sbn, info)) continue;
1472 StatusBarNotification sbnToSend =
1473 (trim == TRIM_FULL) ? sbn : sbn.cloneLight();
1474 list.add(sbnToSend);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001475 }
Christoph Studercee44ba2014-05-20 18:36:43 +02001476 return new ParceledListSlice<StatusBarNotification>(list);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001477 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001478 }
1479
1480 @Override
John Spurlockd8afe3c2014-08-01 14:04:07 -04001481 public void requestHintsFromListener(INotificationListener token, int hints) {
1482 final long identity = Binder.clearCallingIdentity();
1483 try {
1484 synchronized (mNotificationList) {
1485 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1486 final boolean disableEffects = (hints & HINT_HOST_DISABLE_EFFECTS) != 0;
1487 if (disableEffects) {
1488 mListenersDisablingEffects.add(info);
1489 } else {
1490 mListenersDisablingEffects.remove(info);
1491 }
John Spurlockd8afe3c2014-08-01 14:04:07 -04001492 updateListenerHintsLocked();
John Spurlockb4782522014-08-22 14:54:46 -04001493 updateEffectsSuppressorLocked();
John Spurlock1fa865f2014-07-21 14:56:39 -04001494 }
John Spurlockd8afe3c2014-08-01 14:04:07 -04001495 } finally {
1496 Binder.restoreCallingIdentity(identity);
John Spurlock1fa865f2014-07-21 14:56:39 -04001497 }
1498 }
1499
1500 @Override
John Spurlockd8afe3c2014-08-01 14:04:07 -04001501 public int getHintsFromListener(INotificationListener token) {
John Spurlock1fa865f2014-07-21 14:56:39 -04001502 synchronized (mNotificationList) {
John Spurlockd8afe3c2014-08-01 14:04:07 -04001503 return mListenerHints;
John Spurlock1fa865f2014-07-21 14:56:39 -04001504 }
1505 }
1506
1507 @Override
Christoph Studer85a384b2014-08-27 20:16:15 +02001508 public void requestInterruptionFilterFromListener(INotificationListener token,
1509 int interruptionFilter) throws RemoteException {
1510 final long identity = Binder.clearCallingIdentity();
1511 try {
1512 synchronized (mNotificationList) {
John Spurlock661f2cf2014-11-17 10:29:10 -05001513 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1514 mZenModeHelper.requestFromListener(info.component, interruptionFilter);
Christoph Studer85a384b2014-08-27 20:16:15 +02001515 updateInterruptionFilterLocked();
1516 }
1517 } finally {
1518 Binder.restoreCallingIdentity(identity);
1519 }
1520 }
1521
1522 @Override
1523 public int getInterruptionFilterFromListener(INotificationListener token)
1524 throws RemoteException {
1525 synchronized (mNotificationLight) {
1526 return mInterruptionFilter;
1527 }
1528 }
1529
1530 @Override
Christoph Studerb82bc782014-08-20 14:29:43 +02001531 public void setOnNotificationPostedTrimFromListener(INotificationListener token, int trim)
1532 throws RemoteException {
1533 synchronized (mNotificationList) {
1534 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1535 if (info == null) return;
1536 mListeners.setOnNotificationPostedTrimLocked(info, trim);
1537 }
1538 }
1539
1540 @Override
John Spurlockb2278d62015-04-07 12:47:12 -04001541 public int getZenMode() {
1542 return mZenModeHelper.getZenMode();
1543 }
1544
1545 @Override
John Spurlock056c5192014-04-20 21:52:01 -04001546 public ZenModeConfig getZenModeConfig() {
John Spurlockcdb57ae2015-02-11 19:04:11 -05001547 enforceSystemOrSystemUIOrVolume("INotificationManager.getZenModeConfig");
John Spurlock056c5192014-04-20 21:52:01 -04001548 return mZenModeHelper.getConfig();
1549 }
1550
1551 @Override
John Spurlockb2278d62015-04-07 12:47:12 -04001552 public boolean setZenModeConfig(ZenModeConfig config, String reason) {
John Spurlock7340fc82014-04-24 18:50:12 -04001553 checkCallerIsSystem();
John Spurlockb2278d62015-04-07 12:47:12 -04001554 return mZenModeHelper.setConfig(config, reason);
John Spurlock056c5192014-04-20 21:52:01 -04001555 }
1556
1557 @Override
John Spurlockb2278d62015-04-07 12:47:12 -04001558 public void setZenMode(int mode, Uri conditionId, String reason) throws RemoteException {
John Spurlockcdb57ae2015-02-11 19:04:11 -05001559 enforceSystemOrSystemUIOrVolume("INotificationManager.setZenMode");
1560 final long identity = Binder.clearCallingIdentity();
1561 try {
John Spurlockb2278d62015-04-07 12:47:12 -04001562 mZenModeHelper.setManualZenMode(mode, conditionId, reason);
John Spurlockcdb57ae2015-02-11 19:04:11 -05001563 } finally {
1564 Binder.restoreCallingIdentity(identity);
1565 }
1566 }
1567
1568 @Override
John Spurlocke77bb362014-04-26 10:24:59 -04001569 public void notifyConditions(String pkg, IConditionProvider provider,
1570 Condition[] conditions) {
1571 final ManagedServiceInfo info = mConditionProviders.checkServiceToken(provider);
1572 checkCallerIsSystemOrSameApp(pkg);
1573 final long identity = Binder.clearCallingIdentity();
1574 try {
1575 mConditionProviders.notifyConditions(pkg, info, conditions);
1576 } finally {
1577 Binder.restoreCallingIdentity(identity);
1578 }
1579 }
1580
1581 @Override
John Spurlock3b98b3f2014-05-01 09:08:48 -04001582 public void requestZenModeConditions(IConditionListener callback, int relevance) {
John Spurlockcdb57ae2015-02-11 19:04:11 -05001583 enforceSystemOrSystemUIOrVolume("INotificationManager.requestZenModeConditions");
John Spurlockb2278d62015-04-07 12:47:12 -04001584 mZenModeHelper.requestZenModeConditions(callback, relevance);
John Spurlock3b98b3f2014-05-01 09:08:48 -04001585 }
1586
John Spurlockcdb57ae2015-02-11 19:04:11 -05001587 private void enforceSystemOrSystemUIOrVolume(String message) {
1588 if (mAudioManagerInternal != null) {
1589 final int vcuid = mAudioManagerInternal.getVolumeControllerUid();
1590 if (vcuid > 0 && Binder.getCallingUid() == vcuid) {
1591 return;
1592 }
1593 }
1594 enforceSystemOrSystemUI(message);
1595 }
1596
John Spurlocke77bb362014-04-26 10:24:59 -04001597 private void enforceSystemOrSystemUI(String message) {
1598 if (isCallerSystem()) return;
1599 getContext().enforceCallingPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
1600 message);
John Spurlock7340fc82014-04-24 18:50:12 -04001601 }
1602
John Spurlock1fc476d2015-04-14 16:05:20 -04001603 private void enforcePolicyToken(Policy.Token token, String method) {
1604 if (!checkPolicyToken(token)) {
1605 Slog.w(TAG, "Invalid notification policy token calling " + method);
1606 throw new SecurityException("Invalid notification policy token");
1607 }
1608 }
1609
1610 private boolean checkPolicyToken(Policy.Token token) {
1611 return mPolicyTokens.containsValue(token)
1612 || mListeners.mPolicyTokens.containsValue(token);
1613 }
1614
John Spurlock7340fc82014-04-24 18:50:12 -04001615 @Override
Adam Lesinski182f73f2013-12-05 16:48:06 -08001616 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1617 if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
1618 != PackageManager.PERMISSION_GRANTED) {
John Spurlock2b122f42014-08-27 16:29:47 -04001619 pw.println("Permission Denial: can't dump NotificationManager from pid="
Adam Lesinski182f73f2013-12-05 16:48:06 -08001620 + Binder.getCallingPid()
1621 + ", uid=" + Binder.getCallingUid());
1622 return;
1623 }
1624
John Spurlock25e2d242014-06-27 13:58:23 -04001625 dumpImpl(pw, DumpFilter.parseFromArguments(args));
Adam Lesinski182f73f2013-12-05 16:48:06 -08001626 }
John Spurlockb4782522014-08-22 14:54:46 -04001627
1628 @Override
1629 public ComponentName getEffectsSuppressor() {
John Spurlockcdb57ae2015-02-11 19:04:11 -05001630 enforceSystemOrSystemUIOrVolume("INotificationManager.getEffectsSuppressor");
John Spurlockb4782522014-08-22 14:54:46 -04001631 return mEffectsSuppressor;
1632 }
John Spurlock2b122f42014-08-27 16:29:47 -04001633
1634 @Override
1635 public boolean matchesCallFilter(Bundle extras) {
1636 enforceSystemOrSystemUI("INotificationManager.matchesCallFilter");
Christoph Studer12aeda82014-09-23 19:08:56 +02001637 return mZenModeHelper.matchesCallFilter(
1638 UserHandle.getCallingUserHandle(),
1639 extras,
1640 mRankingHelper.findExtractor(ValidateNotificationPeople.class),
1641 MATCHES_CALL_FILTER_CONTACTS_TIMEOUT_MS,
1642 MATCHES_CALL_FILTER_TIMEOUT_AFFINITY);
John Spurlock2b122f42014-08-27 16:29:47 -04001643 }
John Spurlock530052a2014-11-30 16:26:19 -05001644
1645 @Override
1646 public boolean isSystemConditionProviderEnabled(String path) {
John Spurlockcdb57ae2015-02-11 19:04:11 -05001647 enforceSystemOrSystemUIOrVolume("INotificationManager.isSystemConditionProviderEnabled");
John Spurlockb2278d62015-04-07 12:47:12 -04001648 return mConditionProviders.isSystemProviderEnabled(path);
John Spurlock530052a2014-11-30 16:26:19 -05001649 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001650
Christopher Tatef9767d62015-04-08 14:35:43 -07001651 // Backup/restore interface
1652 @Override
1653 public byte[] getBackupPayload(int user) {
1654 // TODO: build a payload of whatever is appropriate
1655 return null;
1656 }
1657
1658 @Override
1659 public void applyRestore(byte[] payload, int user) {
1660 // TODO: apply the restored payload as new current state
1661 }
1662
John Spurlock1fc476d2015-04-14 16:05:20 -04001663 @Override
1664 public Policy.Token getPolicyTokenFromListener(INotificationListener listener) {
1665 final long identity = Binder.clearCallingIdentity();
1666 try {
1667 return mListeners.getPolicyToken(listener);
1668 } finally {
1669 Binder.restoreCallingIdentity(identity);
John Spurlocka4294292014-03-24 18:02:32 -04001670 }
John Spurlocka4294292014-03-24 18:02:32 -04001671 }
John Spurlock1fc476d2015-04-14 16:05:20 -04001672
1673 @Override
1674 public void requestNotificationPolicyToken(String pkg,
1675 INotificationManagerCallback callback) throws RemoteException {
1676 if (callback == null) {
1677 Slog.w(TAG, "requestNotificationPolicyToken: no callback specified");
1678 return;
1679 }
1680 if (pkg == null) {
1681 Slog.w(TAG, "requestNotificationPolicyToken denied: no package specified");
1682 callback.onPolicyToken(null);
1683 return;
1684 }
1685 Policy.Token token = null;
1686 final long identity = Binder.clearCallingIdentity();
1687 try {
1688 synchronized (mNotificationList) {
1689 token = mPolicyTokens.get(pkg);
1690 if (token == null) {
1691 token = new Policy.Token(new Binder());
1692 mPolicyTokens.put(pkg, token);
1693 }
1694 if (DBG) Slog.w(TAG, "requestNotificationPolicyToken granted for " + pkg);
1695 }
1696 } finally {
1697 Binder.restoreCallingIdentity(identity);
1698 }
1699 callback.onPolicyToken(token);
1700 }
1701
1702 @Override
1703 public boolean isNotificationPolicyTokenValid(String pkg, Policy.Token token) {
1704 return checkPolicyToken(token);
1705 }
1706
1707 @Override
1708 public Policy getNotificationPolicy(Policy.Token token) {
1709 enforcePolicyToken(token, "getNotificationPolicy");
1710 final long identity = Binder.clearCallingIdentity();
1711 try {
1712 return mZenModeHelper.getNotificationPolicy();
1713 } finally {
1714 Binder.restoreCallingIdentity(identity);
1715 }
1716 }
1717
1718 @Override
1719 public void setNotificationPolicy(Policy.Token token, Policy policy) {
1720 enforcePolicyToken(token, "setNotificationPolicy");
1721 final long identity = Binder.clearCallingIdentity();
1722 try {
1723 mZenModeHelper.setNotificationPolicy(policy);
1724 } finally {
1725 Binder.restoreCallingIdentity(identity);
1726 }
1727 }
1728 };
John Spurlocka4294292014-03-24 18:02:32 -04001729
John Spurlock32fe4c62014-10-02 12:16:02 -04001730 private String disableNotificationEffects(NotificationRecord record) {
1731 if (mDisableNotificationEffects) {
1732 return "booleanState";
1733 }
1734 if ((mListenerHints & HINT_HOST_DISABLE_EFFECTS) != 0) {
1735 return "listenerHints";
1736 }
1737 if (mCallState != TelephonyManager.CALL_STATE_IDLE && !mZenModeHelper.isCall(record)) {
1738 return "callState";
1739 }
1740 return null;
John Spurlock1fa865f2014-07-21 14:56:39 -04001741 }
1742
John Spurlock25e2d242014-06-27 13:58:23 -04001743 void dumpImpl(PrintWriter pw, DumpFilter filter) {
1744 pw.print("Current Notification Manager state");
1745 if (filter != null) {
John Spurlock50806fc2014-07-15 10:22:02 -04001746 pw.print(" (filtered to "); pw.print(filter); pw.print(")");
John Spurlock25e2d242014-06-27 13:58:23 -04001747 }
1748 pw.println(':');
Adam Lesinski182f73f2013-12-05 16:48:06 -08001749 int N;
John Spurlock50806fc2014-07-15 10:22:02 -04001750 final boolean zenOnly = filter != null && filter.zen;
Adam Lesinski182f73f2013-12-05 16:48:06 -08001751
John Spurlock50806fc2014-07-15 10:22:02 -04001752 if (!zenOnly) {
1753 synchronized (mToastQueue) {
1754 N = mToastQueue.size();
1755 if (N > 0) {
1756 pw.println(" Toast Queue:");
1757 for (int i=0; i<N; i++) {
1758 mToastQueue.get(i).dump(pw, " ", filter);
1759 }
1760 pw.println(" ");
Adam Lesinski182f73f2013-12-05 16:48:06 -08001761 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001762 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001763 }
1764
1765 synchronized (mNotificationList) {
John Spurlock50806fc2014-07-15 10:22:02 -04001766 if (!zenOnly) {
1767 N = mNotificationList.size();
John Spurlock25e2d242014-06-27 13:58:23 -04001768 if (N > 0) {
John Spurlock50806fc2014-07-15 10:22:02 -04001769 pw.println(" Notification List:");
John Spurlock25e2d242014-06-27 13:58:23 -04001770 for (int i=0; i<N; i++) {
John Spurlock50806fc2014-07-15 10:22:02 -04001771 final NotificationRecord nr = mNotificationList.get(i);
1772 if (filter != null && !filter.matches(nr.sbn)) continue;
1773 nr.dump(pw, " ", getContext());
John Spurlock25e2d242014-06-27 13:58:23 -04001774 }
1775 pw.println(" ");
Adam Lesinski182f73f2013-12-05 16:48:06 -08001776 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001777
John Spurlock50806fc2014-07-15 10:22:02 -04001778 if (filter == null) {
1779 N = mLights.size();
1780 if (N > 0) {
1781 pw.println(" Lights List:");
1782 for (int i=0; i<N; i++) {
Chris Wren6054e612014-11-25 17:16:46 -05001783 if (i == N - 1) {
1784 pw.print(" > ");
1785 } else {
1786 pw.print(" ");
1787 }
1788 pw.println(mLights.get(i));
John Spurlock50806fc2014-07-15 10:22:02 -04001789 }
1790 pw.println(" ");
1791 }
John Spurlockcb566aa2014-08-03 22:58:28 -04001792 pw.println(" mUseAttentionLight=" + mUseAttentionLight);
1793 pw.println(" mNotificationPulseEnabled=" + mNotificationPulseEnabled);
Chris Wren6054e612014-11-25 17:16:46 -05001794 pw.println(" mSoundNotificationKey=" + mSoundNotificationKey);
1795 pw.println(" mVibrateNotificationKey=" + mVibrateNotificationKey);
John Spurlockd8afe3c2014-08-01 14:04:07 -04001796 pw.println(" mDisableNotificationEffects=" + mDisableNotificationEffects);
John Spurlock32fe4c62014-10-02 12:16:02 -04001797 pw.println(" mCallState=" + callStateToString(mCallState));
John Spurlock50806fc2014-07-15 10:22:02 -04001798 pw.println(" mSystemReady=" + mSystemReady);
1799 }
1800 pw.println(" mArchive=" + mArchive.toString());
1801 Iterator<StatusBarNotification> iter = mArchive.descendingIterator();
1802 int i=0;
1803 while (iter.hasNext()) {
1804 final StatusBarNotification sbn = iter.next();
1805 if (filter != null && !filter.matches(sbn)) continue;
1806 pw.println(" " + sbn);
1807 if (++i >= 5) {
1808 if (iter.hasNext()) pw.println(" ...");
1809 break;
1810 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001811 }
1812 }
1813
John Spurlock50806fc2014-07-15 10:22:02 -04001814 if (!zenOnly) {
1815 pw.println("\n Usage Stats:");
1816 mUsageStats.dump(pw, " ", filter);
1817 }
Christoph Studer546bec82014-03-14 12:17:12 +01001818
John Spurlock50806fc2014-07-15 10:22:02 -04001819 if (filter == null || zenOnly) {
John Spurlock25e2d242014-06-27 13:58:23 -04001820 pw.println("\n Zen Mode:");
John Spurlockf3701772015-02-12 13:29:37 -05001821 pw.print(" mInterruptionFilter="); pw.println(mInterruptionFilter);
John Spurlock25e2d242014-06-27 13:58:23 -04001822 mZenModeHelper.dump(pw, " ");
John Spurlock6ae82a72014-07-16 16:23:01 -04001823
1824 pw.println("\n Zen Log:");
1825 ZenLog.dump(pw, " ");
John Spurlock25e2d242014-06-27 13:58:23 -04001826 }
John Spurlocke77bb362014-04-26 10:24:59 -04001827
John Spurlock50806fc2014-07-15 10:22:02 -04001828 if (!zenOnly) {
1829 pw.println("\n Ranking Config:");
1830 mRankingHelper.dump(pw, " ", filter);
Chris Wren54bbef42014-07-09 18:37:56 -04001831
John Spurlock50806fc2014-07-15 10:22:02 -04001832 pw.println("\n Notification listeners:");
1833 mListeners.dump(pw, filter);
John Spurlockd8afe3c2014-08-01 14:04:07 -04001834 pw.print(" mListenerHints: "); pw.println(mListenerHints);
1835 pw.print(" mListenersDisablingEffects: (");
1836 N = mListenersDisablingEffects.size();
John Spurlock1fa865f2014-07-21 14:56:39 -04001837 for (int i = 0; i < N; i++) {
John Spurlockd8afe3c2014-08-01 14:04:07 -04001838 final ManagedServiceInfo listener = mListenersDisablingEffects.valueAt(i);
John Spurlock1fa865f2014-07-21 14:56:39 -04001839 if (i > 0) pw.print(',');
1840 pw.print(listener.component);
1841 }
1842 pw.println(')');
John Spurlock1fc476d2015-04-14 16:05:20 -04001843 pw.print(" mPolicyTokens.keys: ");
1844 pw.println(TextUtils.join(",", mPolicyTokens.keySet()));
1845 pw.print(" mListeners.mPolicyTokens.keys: ");
1846 pw.println(TextUtils.join(",", mListeners.mPolicyTokens.keySet()));
John Spurlock50806fc2014-07-15 10:22:02 -04001847 }
John Spurlocke77bb362014-04-26 10:24:59 -04001848
1849 pw.println("\n Condition providers:");
John Spurlock25e2d242014-06-27 13:58:23 -04001850 mConditionProviders.dump(pw, filter);
Christoph Studer265c1052014-07-23 17:14:33 +02001851
1852 pw.println("\n Group summaries:");
1853 for (Entry<String, NotificationRecord> entry : mSummaryByGroupKey.entrySet()) {
1854 NotificationRecord r = entry.getValue();
1855 pw.println(" " + entry.getKey() + " -> " + r.getKey());
1856 if (mNotificationsByKey.get(r.getKey()) != r) {
1857 pw.println("!!!!!!LEAK: Record not found in mNotificationsByKey.");
1858 r.dump(pw, " ", getContext());
1859 }
1860 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001861 }
1862 }
1863
Adam Lesinski182f73f2013-12-05 16:48:06 -08001864 /**
1865 * The private API only accessible to the system process.
1866 */
1867 private final NotificationManagerInternal mInternalService = new NotificationManagerInternal() {
1868 @Override
Christoph Studer8fd7f1e2014-04-11 17:35:05 -04001869 public void enqueueNotification(String pkg, String opPkg, int callingUid, int callingPid,
Adam Lesinski182f73f2013-12-05 16:48:06 -08001870 String tag, int id, Notification notification, int[] idReceived, int userId) {
Christoph Studer8fd7f1e2014-04-11 17:35:05 -04001871 enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, notification,
Adam Lesinski182f73f2013-12-05 16:48:06 -08001872 idReceived, userId);
1873 }
Christoph Studer365e4c32014-09-18 20:35:36 +02001874
1875 @Override
1876 public void removeForegroundServiceFlagFromNotification(String pkg, int notificationId,
1877 int userId) {
1878 checkCallerIsSystem();
1879 synchronized (mNotificationList) {
1880 int i = indexOfNotificationLocked(pkg, null, notificationId, userId);
1881 if (i < 0) {
1882 Log.d(TAG, "stripForegroundServiceFlag: Could not find notification with "
1883 + "pkg=" + pkg + " / id=" + notificationId + " / userId=" + userId);
1884 return;
1885 }
1886 NotificationRecord r = mNotificationList.get(i);
1887 StatusBarNotification sbn = r.sbn;
1888 // NoMan adds flags FLAG_NO_CLEAR and FLAG_ONGOING_EVENT when it sees
1889 // FLAG_FOREGROUND_SERVICE. Hence it's not enough to remove FLAG_FOREGROUND_SERVICE,
1890 // we have to revert to the flags we received initially *and* force remove
1891 // FLAG_FOREGROUND_SERVICE.
1892 sbn.getNotification().flags =
1893 (r.mOriginalFlags & ~Notification.FLAG_FOREGROUND_SERVICE);
1894 mRankingHelper.sort(mNotificationList);
1895 mListeners.notifyPostedLocked(sbn, sbn /* oldSbn */);
1896 }
1897 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001898 };
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001899
Christoph Studer8fd7f1e2014-04-11 17:35:05 -04001900 void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,
Scott Greenwald9b05c612013-06-25 23:44:05 -04001901 final int callingPid, final String tag, final int id, final Notification notification,
Adam Lesinski182f73f2013-12-05 16:48:06 -08001902 int[] idOut, int incomingUserId) {
Daniel Sandler0da673f2012-04-11 12:33:16 -04001903 if (DBG) {
Adam Lesinski182f73f2013-12-05 16:48:06 -08001904 Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id
1905 + " notification=" + notification);
Daniel Sandler0da673f2012-04-11 12:33:16 -04001906 }
John Spurlock7340fc82014-04-24 18:50:12 -04001907 checkCallerIsSystemOrSameApp(pkg);
1908 final boolean isSystemNotification = isUidSystem(callingUid) || ("android".equals(pkg));
Justin Koh38156c52014-06-04 13:57:49 -07001909 final boolean isNotificationFromListener = mListeners.isListenerPackage(pkg);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001910
Scott Greenwald9b05c612013-06-25 23:44:05 -04001911 final int userId = ActivityManager.handleIncomingUser(callingPid,
1912 callingUid, incomingUserId, true, false, "enqueueNotification", pkg);
Jeff Sharkey65c4a2b2012-09-25 17:22:27 -07001913 final UserHandle user = new UserHandle(userId);
Dianne Hackborn41203752012-08-31 14:05:51 -07001914
Joe Onoratobd73d012010-06-04 11:44:54 -07001915 // Limit the number of notifications that any given package except the android
Justin Koh38156c52014-06-04 13:57:49 -07001916 // package or a registered listener can enqueue. Prevents DOS attacks and deals with leaks.
1917 if (!isSystemNotification && !isNotificationFromListener) {
Joe Onoratobd73d012010-06-04 11:44:54 -07001918 synchronized (mNotificationList) {
1919 int count = 0;
1920 final int N = mNotificationList.size();
1921 for (int i=0; i<N; i++) {
1922 final NotificationRecord r = mNotificationList.get(i);
Daniel Sandler4f91efd2013-04-25 16:38:41 -04001923 if (r.sbn.getPackageName().equals(pkg) && r.sbn.getUserId() == userId) {
Joe Onoratobd73d012010-06-04 11:44:54 -07001924 count++;
1925 if (count >= MAX_PACKAGE_NOTIFICATIONS) {
1926 Slog.e(TAG, "Package has already posted " + count
1927 + " notifications. Not showing more. package=" + pkg);
1928 return;
1929 }
1930 }
1931 }
1932 }
1933 }
1934
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001935 if (pkg == null || notification == null) {
1936 throw new IllegalArgumentException("null not allowed: pkg=" + pkg
1937 + " id=" + id + " notification=" + notification);
1938 }
1939 if (notification.icon != 0) {
Christoph Studer4600f9b2014-07-22 22:44:43 +02001940 if (!notification.isValid()) {
1941 throw new IllegalArgumentException("Invalid notification (): pkg=" + pkg
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001942 + " id=" + id + " notification=" + notification);
1943 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001944 }
1945
Scott Greenwald9b05c612013-06-25 23:44:05 -04001946 mHandler.post(new Runnable() {
1947 @Override
1948 public void run() {
Daniel Sandler0da673f2012-04-11 12:33:16 -04001949
Scott Greenwald9b05c612013-06-25 23:44:05 -04001950 synchronized (mNotificationList) {
Christoph Studercd4adf82014-08-19 17:50:49 +02001951
1952 // === Scoring ===
1953
1954 // 0. Sanitize inputs
1955 notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN,
1956 Notification.PRIORITY_MAX);
1957 // Migrate notification flags to scores
1958 if (0 != (notification.flags & Notification.FLAG_HIGH_PRIORITY)) {
1959 if (notification.priority < Notification.PRIORITY_MAX) {
1960 notification.priority = Notification.PRIORITY_MAX;
1961 }
1962 } else if (SCORE_ONGOING_HIGHER &&
1963 0 != (notification.flags & Notification.FLAG_ONGOING_EVENT)) {
1964 if (notification.priority < Notification.PRIORITY_HIGH) {
1965 notification.priority = Notification.PRIORITY_HIGH;
1966 }
1967 }
John Spurlock1d881a12015-03-18 19:21:54 -04001968 // force no heads up per package config
1969 if (!mRankingHelper.getPackagePeekable(pkg, callingUid)) {
1970 if (notification.extras == null) {
1971 notification.extras = new Bundle();
1972 }
1973 notification.extras.putInt(Notification.EXTRA_AS_HEADS_UP,
1974 Notification.HEADS_UP_NEVER);
1975 }
Christoph Studercd4adf82014-08-19 17:50:49 +02001976
1977 // 1. initial score: buckets of 10, around the app [-20..20]
1978 final int score = notification.priority * NOTIFICATION_PRIORITY_MULTIPLIER;
1979
1980 // 2. extract ranking signals from the notification data
1981 final StatusBarNotification n = new StatusBarNotification(
1982 pkg, opPkg, id, tag, callingUid, callingPid, score, notification,
1983 user);
1984 NotificationRecord r = new NotificationRecord(n, score);
1985 NotificationRecord old = mNotificationsByKey.get(n.getKey());
1986 if (old != null) {
1987 // Retain ranking information from previous record
1988 r.copyRankingInformation(old);
1989 }
Christoph Studer265c1052014-07-23 17:14:33 +02001990
1991 // Handle grouped notifications and bail out early if we
1992 // can to avoid extracting signals.
1993 handleGroupedNotificationLocked(r, old, callingUid, callingPid);
1994 boolean ignoreNotification =
Christoph Studer4a9849b2015-01-06 18:55:08 +01001995 removeUnusedGroupedNotificationLocked(r, old, callingUid, callingPid);
Christoph Studercd4adf82014-08-19 17:50:49 +02001996
Christoph Studer1382e672014-10-24 16:30:54 +02001997 // This conditional is a dirty hack to limit the logging done on
1998 // behalf of the download manager without affecting other apps.
1999 if (!pkg.equals("com.android.providers.downloads")
2000 || Log.isLoggable("DownloadManager", Log.VERBOSE)) {
Christoph Studer265c1052014-07-23 17:14:33 +02002001 int enqueueStatus = EVENTLOG_ENQUEUE_STATUS_NEW;
2002 if (ignoreNotification) {
2003 enqueueStatus = EVENTLOG_ENQUEUE_STATUS_IGNORED;
2004 } else if (old != null) {
2005 enqueueStatus = EVENTLOG_ENQUEUE_STATUS_UPDATE;
2006 }
Christoph Studer1382e672014-10-24 16:30:54 +02002007 EventLogTags.writeNotificationEnqueue(callingUid, callingPid,
2008 pkg, id, tag, userId, notification.toString(),
Christoph Studer265c1052014-07-23 17:14:33 +02002009 enqueueStatus);
Christoph Studer1382e672014-10-24 16:30:54 +02002010 }
Christoph Studer265c1052014-07-23 17:14:33 +02002011
2012 if (ignoreNotification) {
2013 return;
2014 }
2015
2016 mRankingHelper.extractSignals(r);
2017
Christoph Studercd4adf82014-08-19 17:50:49 +02002018 // 3. Apply local rules
2019
2020 // blocked apps
2021 if (ENABLE_BLOCKED_NOTIFICATIONS && !noteNotificationOp(pkg, callingUid)) {
2022 if (!isSystemNotification) {
2023 r.score = JUNK_SCORE;
2024 Slog.e(TAG, "Suppressing notification from package " + pkg
2025 + " by user request.");
2026 }
2027 }
2028
2029 if (r.score < SCORE_DISPLAY_THRESHOLD) {
2030 // Notification will be blocked because the score is too low.
2031 return;
2032 }
2033
Christoph Studer71f18fd2014-05-20 17:02:04 +02002034 int index = indexOfNotificationLocked(n.getKey());
Scott Greenwald9b05c612013-06-25 23:44:05 -04002035 if (index < 0) {
2036 mNotificationList.add(r);
Christoph Studer546bec82014-03-14 12:17:12 +01002037 mUsageStats.registerPostedByApp(r);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002038 } else {
Christoph Studerd89188d2014-04-03 00:02:39 +02002039 old = mNotificationList.get(index);
2040 mNotificationList.set(index, r);
Christoph Studer061dee22014-05-09 12:28:55 +02002041 mUsageStats.registerUpdatedByApp(r, old);
Scott Greenwald9b05c612013-06-25 23:44:05 -04002042 // Make sure we don't lose the foreground service state.
Christoph Studer71f18fd2014-05-20 17:02:04 +02002043 notification.flags |=
Chris Wrena3446562014-06-03 18:11:47 -04002044 old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;
Chris Wrena3446562014-06-03 18:11:47 -04002045 r.isUpdate = true;
John Spurlocka4294292014-03-24 18:02:32 -04002046 }
Christoph Studer1cd5add2014-07-03 00:23:05 +02002047
John Spurlocka4294292014-03-24 18:02:32 -04002048 mNotificationsByKey.put(n.getKey(), r);
Scott Greenwald9b05c612013-06-25 23:44:05 -04002049
Christoph Studer1cd5add2014-07-03 00:23:05 +02002050 // Ensure if this is a foreground service that the proper additional
2051 // flags are set.
2052 if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
2053 notification.flags |= Notification.FLAG_ONGOING_EVENT
2054 | Notification.FLAG_NO_CLEAR;
2055 }
2056
Chris Wren52eba542014-06-02 15:40:32 -04002057 applyZenModeLocked(r);
Christoph Studercd4adf82014-08-19 17:50:49 +02002058 mRankingHelper.sort(mNotificationList);
Chris Wrenf9536642014-04-17 10:01:54 -04002059
Scott Greenwald9b05c612013-06-25 23:44:05 -04002060 if (notification.icon != 0) {
Christoph Studercef37cf2014-07-25 14:18:17 +02002061 StatusBarNotification oldSbn = (old != null) ? old.sbn : null;
2062 mListeners.notifyPostedLocked(n, oldSbn);
Scott Greenwald9b05c612013-06-25 23:44:05 -04002063 } else {
2064 Slog.e(TAG, "Not posting notification with icon==0: " + notification);
Christoph Studer71f18fd2014-05-20 17:02:04 +02002065 if (old != null && !old.isCanceled) {
Chris Wrena3446562014-06-03 18:11:47 -04002066 mListeners.notifyRemovedLocked(n);
Scott Greenwald9b05c612013-06-25 23:44:05 -04002067 }
2068 // ATTENTION: in a future release we will bail out here
Adam Lesinski182f73f2013-12-05 16:48:06 -08002069 // so that we do not play sounds, show lights, etc. for invalid
2070 // notifications
Scott Greenwald9b05c612013-06-25 23:44:05 -04002071 Slog.e(TAG, "WARNING: In a future release this will crash the app: "
2072 + n.getPackageName());
2073 }
2074
Chris Wrena3446562014-06-03 18:11:47 -04002075 buzzBeepBlinkLocked(r);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002076 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002077 }
Scott Greenwald9b05c612013-06-25 23:44:05 -04002078 });
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002079
2080 idOut[0] = id;
2081 }
2082
Christoph Studer265c1052014-07-23 17:14:33 +02002083 /**
2084 * Ensures that grouped notification receive their special treatment.
2085 *
2086 * <p>Cancels group children if the new notification causes a group to lose
2087 * its summary.</p>
2088 *
2089 * <p>Updates mSummaryByGroupKey.</p>
2090 */
2091 private void handleGroupedNotificationLocked(NotificationRecord r, NotificationRecord old,
2092 int callingUid, int callingPid) {
2093 StatusBarNotification sbn = r.sbn;
2094 Notification n = sbn.getNotification();
2095 String group = sbn.getGroupKey();
2096 boolean isSummary = n.isGroupSummary();
2097
2098 Notification oldN = old != null ? old.sbn.getNotification() : null;
2099 String oldGroup = old != null ? old.sbn.getGroupKey() : null;
2100 boolean oldIsSummary = old != null && oldN.isGroupSummary();
2101
2102 if (oldIsSummary) {
2103 NotificationRecord removedSummary = mSummaryByGroupKey.remove(oldGroup);
2104 if (removedSummary != old) {
2105 String removedKey =
2106 removedSummary != null ? removedSummary.getKey() : "<null>";
2107 Slog.w(TAG, "Removed summary didn't match old notification: old=" + old.getKey() +
2108 ", removed=" + removedKey);
2109 }
2110 }
2111 if (isSummary) {
2112 mSummaryByGroupKey.put(group, r);
2113 }
2114
2115 // Clear out group children of the old notification if the update
2116 // causes the group summary to go away. This happens when the old
2117 // notification was a summary and the new one isn't, or when the old
2118 // notification was a summary and its group key changed.
2119 if (oldIsSummary && (!isSummary || !oldGroup.equals(group))) {
2120 cancelGroupChildrenLocked(old, callingUid, callingPid, null,
2121 REASON_GROUP_SUMMARY_CANCELED);
2122 }
2123 }
2124
2125 /**
2126 * Performs group notification optimizations if SysUI is the only active
2127 * notification listener and returns whether the given notification should
2128 * be ignored.
2129 *
2130 * <p>Returns true if the given notification is a child of a group with a
2131 * summary, which means that SysUI will never show it, and hence the new
Christoph Studer4a9849b2015-01-06 18:55:08 +01002132 * notification can be safely ignored. Also cancels any previous instance
2133 * of the ignored notification.</p>
Christoph Studer265c1052014-07-23 17:14:33 +02002134 *
2135 * <p>For summaries, cancels all children of that group, as SysUI will
2136 * never show them anymore.</p>
2137 *
2138 * @return true if the given notification can be ignored as an optimization
2139 */
2140 private boolean removeUnusedGroupedNotificationLocked(NotificationRecord r,
Christoph Studer4a9849b2015-01-06 18:55:08 +01002141 NotificationRecord old, int callingUid, int callingPid) {
Selim Cinekb5605e52015-02-20 18:21:41 +01002142 if (!ENABLE_CHILD_NOTIFICATIONS) {
2143 // No optimizations are possible if listeners want groups.
2144 if (mListeners.notificationGroupsDesired()) {
2145 return false;
Christoph Studer265c1052014-07-23 17:14:33 +02002146 }
Selim Cinekb5605e52015-02-20 18:21:41 +01002147
2148 StatusBarNotification sbn = r.sbn;
2149 String group = sbn.getGroupKey();
2150 boolean isSummary = sbn.getNotification().isGroupSummary();
2151 boolean isChild = sbn.getNotification().isGroupChild();
2152
2153 NotificationRecord summary = mSummaryByGroupKey.get(group);
2154 if (isChild && summary != null) {
2155 // Child with an active summary -> ignore
Christoph Studer4a9849b2015-01-06 18:55:08 +01002156 if (DBG) {
Selim Cinekb5605e52015-02-20 18:21:41 +01002157 Slog.d(TAG, "Ignoring group child " + sbn.getKey() + " due to existing summary "
2158 + summary.getKey());
Christoph Studer4a9849b2015-01-06 18:55:08 +01002159 }
Selim Cinekb5605e52015-02-20 18:21:41 +01002160 // Make sure we don't leave an old version of the notification around.
2161 if (old != null) {
2162 if (DBG) {
2163 Slog.d(TAG, "Canceling old version of ignored group child " + sbn.getKey());
2164 }
2165 cancelNotificationLocked(old, false, REASON_GROUP_OPTIMIZATION);
2166 }
2167 return true;
2168 } else if (isSummary) {
2169 // Summary -> cancel children
2170 cancelGroupChildrenLocked(r, callingUid, callingPid, null,
2171 REASON_GROUP_OPTIMIZATION);
Christoph Studer4a9849b2015-01-06 18:55:08 +01002172 }
Christoph Studer265c1052014-07-23 17:14:33 +02002173 }
2174 return false;
2175 }
2176
Chris Wrena3446562014-06-03 18:11:47 -04002177 private void buzzBeepBlinkLocked(NotificationRecord record) {
John Spurlockcad57682014-07-26 17:09:56 -04002178 boolean buzzBeepBlinked = false;
Chris Wrena3446562014-06-03 18:11:47 -04002179 final Notification notification = record.sbn.getNotification();
2180
2181 // Should this notification make noise, vibe, or use the LED?
Chris Wrence00a232014-11-21 16:25:19 -05002182 final boolean aboveThreshold = record.score >= SCORE_INTERRUPTION_THRESHOLD;
2183 final boolean canInterrupt = aboveThreshold && !record.isIntercepted();
Chris Wrena3446562014-06-03 18:11:47 -04002184 if (DBG || record.isIntercepted())
2185 Slog.v(TAG,
2186 "pkg=" + record.sbn.getPackageName() + " canInterrupt=" + canInterrupt +
2187 " intercept=" + record.isIntercepted()
2188 );
2189
2190 final int currentUser;
2191 final long token = Binder.clearCallingIdentity();
2192 try {
2193 currentUser = ActivityManager.getCurrentUser();
2194 } finally {
2195 Binder.restoreCallingIdentity(token);
2196 }
2197
2198 // If we're not supposed to beep, vibrate, etc. then don't.
John Spurlock32fe4c62014-10-02 12:16:02 -04002199 final String disableEffects = disableNotificationEffects(record);
2200 if (disableEffects != null) {
2201 ZenLog.traceDisableEffects(record, disableEffects);
2202 }
2203 if (disableEffects == null
Chris Wrena3446562014-06-03 18:11:47 -04002204 && (!(record.isUpdate
2205 && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 ))
2206 && (record.getUserId() == UserHandle.USER_ALL ||
2207 record.getUserId() == currentUser ||
2208 mUserProfiles.isCurrentProfile(record.getUserId()))
2209 && canInterrupt
2210 && mSystemReady
2211 && mAudioManager != null) {
2212 if (DBG) Slog.v(TAG, "Interrupting!");
2213
2214 sendAccessibilityEvent(notification, record.sbn.getPackageName());
2215
2216 // sound
2217
2218 // should we use the default notification sound? (indicated either by
2219 // DEFAULT_SOUND or because notification.sound is pointing at
2220 // Settings.System.NOTIFICATION_SOUND)
2221 final boolean useDefaultSound =
2222 (notification.defaults & Notification.DEFAULT_SOUND) != 0 ||
2223 Settings.System.DEFAULT_NOTIFICATION_URI
2224 .equals(notification.sound);
2225
2226 Uri soundUri = null;
2227 boolean hasValidSound = false;
2228
2229 if (useDefaultSound) {
2230 soundUri = Settings.System.DEFAULT_NOTIFICATION_URI;
2231
2232 // check to see if the default notification sound is silent
2233 ContentResolver resolver = getContext().getContentResolver();
2234 hasValidSound = Settings.System.getString(resolver,
2235 Settings.System.NOTIFICATION_SOUND) != null;
2236 } else if (notification.sound != null) {
2237 soundUri = notification.sound;
2238 hasValidSound = (soundUri != null);
2239 }
2240
2241 if (hasValidSound) {
2242 boolean looping =
2243 (notification.flags & Notification.FLAG_INSISTENT) != 0;
Marco Nelissen1c066302014-11-18 10:48:12 -08002244 AudioAttributes audioAttributes = audioAttributesForNotification(notification);
Chris Wren6054e612014-11-25 17:16:46 -05002245 mSoundNotificationKey = record.getKey();
Chris Wrena3446562014-06-03 18:11:47 -04002246 // do not play notifications if stream volume is 0 (typically because
2247 // ringer mode is silent) or if there is a user of exclusive audio focus
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -07002248 if ((mAudioManager.getStreamVolume(
2249 AudioAttributes.toLegacyStreamType(audioAttributes)) != 0)
2250 && !mAudioManager.isAudioFocusExclusive()) {
Chris Wrena3446562014-06-03 18:11:47 -04002251 final long identity = Binder.clearCallingIdentity();
2252 try {
2253 final IRingtonePlayer player =
2254 mAudioManager.getRingtonePlayer();
2255 if (player != null) {
2256 if (DBG) Slog.v(TAG, "Playing sound " + soundUri
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -07002257 + " with attributes " + audioAttributes);
Chris Wrena3446562014-06-03 18:11:47 -04002258 player.playAsync(soundUri, record.sbn.getUser(), looping,
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -07002259 audioAttributes);
John Spurlockcad57682014-07-26 17:09:56 -04002260 buzzBeepBlinked = true;
Chris Wrena3446562014-06-03 18:11:47 -04002261 }
2262 } catch (RemoteException e) {
2263 } finally {
2264 Binder.restoreCallingIdentity(identity);
2265 }
2266 }
2267 }
2268
2269 // vibrate
2270 // Does the notification want to specify its own vibration?
2271 final boolean hasCustomVibrate = notification.vibrate != null;
2272
2273 // new in 4.2: if there was supposed to be a sound and we're in vibrate
2274 // mode, and no other vibration is specified, we fall back to vibration
2275 final boolean convertSoundToVibration =
2276 !hasCustomVibrate
2277 && hasValidSound
John Spurlock661f2cf2014-11-17 10:29:10 -05002278 && (mAudioManager.getRingerModeInternal()
Chris Wrena3446562014-06-03 18:11:47 -04002279 == AudioManager.RINGER_MODE_VIBRATE);
2280
2281 // The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback.
2282 final boolean useDefaultVibrate =
2283 (notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
2284
2285 if ((useDefaultVibrate || convertSoundToVibration || hasCustomVibrate)
John Spurlock661f2cf2014-11-17 10:29:10 -05002286 && !(mAudioManager.getRingerModeInternal()
Chris Wrena3446562014-06-03 18:11:47 -04002287 == AudioManager.RINGER_MODE_SILENT)) {
Chris Wren6054e612014-11-25 17:16:46 -05002288 mVibrateNotificationKey = record.getKey();
Chris Wrena3446562014-06-03 18:11:47 -04002289
2290 if (useDefaultVibrate || convertSoundToVibration) {
2291 // Escalate privileges so we can use the vibrator even if the
2292 // notifying app does not have the VIBRATE permission.
2293 long identity = Binder.clearCallingIdentity();
2294 try {
2295 mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
2296 useDefaultVibrate ? mDefaultVibrationPattern
2297 : mFallbackVibrationPattern,
2298 ((notification.flags & Notification.FLAG_INSISTENT) != 0)
John Spurlock7b414672014-07-18 13:02:39 -04002299 ? 0: -1, audioAttributesForNotification(notification));
John Spurlockcad57682014-07-26 17:09:56 -04002300 buzzBeepBlinked = true;
Chris Wrena3446562014-06-03 18:11:47 -04002301 } finally {
2302 Binder.restoreCallingIdentity(identity);
2303 }
2304 } else if (notification.vibrate.length > 1) {
2305 // If you want your own vibration pattern, you need the VIBRATE
2306 // permission
2307 mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
2308 notification.vibrate,
2309 ((notification.flags & Notification.FLAG_INSISTENT) != 0)
John Spurlock7b414672014-07-18 13:02:39 -04002310 ? 0: -1, audioAttributesForNotification(notification));
John Spurlockcad57682014-07-26 17:09:56 -04002311 buzzBeepBlinked = true;
Chris Wrena3446562014-06-03 18:11:47 -04002312 }
2313 }
2314 }
2315
2316 // light
2317 // release the light
2318 boolean wasShowLights = mLights.remove(record.getKey());
Chris Wrence00a232014-11-21 16:25:19 -05002319 if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0 && aboveThreshold) {
Chris Wrena3446562014-06-03 18:11:47 -04002320 mLights.add(record.getKey());
2321 updateLightsLocked();
Chris Wren5116a822014-06-04 15:59:50 -04002322 if (mUseAttentionLight) {
2323 mAttentionLight.pulse();
2324 }
John Spurlockcad57682014-07-26 17:09:56 -04002325 buzzBeepBlinked = true;
Chris Wrena3446562014-06-03 18:11:47 -04002326 } else if (wasShowLights) {
2327 updateLightsLocked();
2328 }
John Spurlockcad57682014-07-26 17:09:56 -04002329 if (buzzBeepBlinked) {
2330 mHandler.post(mBuzzBeepBlinked);
2331 }
Chris Wrena3446562014-06-03 18:11:47 -04002332 }
2333
John Spurlock7b414672014-07-18 13:02:39 -04002334 private static AudioAttributes audioAttributesForNotification(Notification n) {
Marco Nelissen1c066302014-11-18 10:48:12 -08002335 if (n.audioAttributes != null
2336 && !Notification.AUDIO_ATTRIBUTES_DEFAULT.equals(n.audioAttributes)) {
2337 // the audio attributes are set and different from the default, use them
John Spurlockbfa5dc42014-07-28 23:30:45 -04002338 return n.audioAttributes;
Jean-Michel Triviceb79bc2014-09-05 11:09:14 -07002339 } else if (n.audioStreamType >= 0 && n.audioStreamType < AudioSystem.getNumStreamTypes()) {
2340 // the stream type is valid, use it
2341 return new AudioAttributes.Builder()
2342 .setInternalLegacyStreamType(n.audioStreamType)
2343 .build();
2344 } else if (n.audioStreamType == AudioSystem.STREAM_DEFAULT) {
2345 return Notification.AUDIO_ATTRIBUTES_DEFAULT;
2346 } else {
2347 Log.w(TAG, String.format("Invalid stream type: %d", n.audioStreamType));
2348 return Notification.AUDIO_ATTRIBUTES_DEFAULT;
John Spurlockbfa5dc42014-07-28 23:30:45 -04002349 }
John Spurlock7b414672014-07-18 13:02:39 -04002350 }
2351
Adam Lesinski182f73f2013-12-05 16:48:06 -08002352 void showNextToastLocked() {
2353 ToastRecord record = mToastQueue.get(0);
2354 while (record != null) {
2355 if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback);
2356 try {
2357 record.callback.show();
2358 scheduleTimeoutLocked(record);
2359 return;
2360 } catch (RemoteException e) {
2361 Slog.w(TAG, "Object died trying to show notification " + record.callback
2362 + " in package " + record.pkg);
2363 // remove it from the list and let the process die
2364 int index = mToastQueue.indexOf(record);
2365 if (index >= 0) {
2366 mToastQueue.remove(index);
2367 }
2368 keepProcessAliveLocked(record.pid);
2369 if (mToastQueue.size() > 0) {
2370 record = mToastQueue.get(0);
2371 } else {
2372 record = null;
2373 }
2374 }
2375 }
2376 }
2377
2378 void cancelToastLocked(int index) {
2379 ToastRecord record = mToastQueue.get(index);
2380 try {
2381 record.callback.hide();
2382 } catch (RemoteException e) {
2383 Slog.w(TAG, "Object died trying to hide notification " + record.callback
2384 + " in package " + record.pkg);
2385 // don't worry about this, we're about to remove it from
2386 // the list anyway
2387 }
2388 mToastQueue.remove(index);
2389 keepProcessAliveLocked(record.pid);
2390 if (mToastQueue.size() > 0) {
2391 // Show the next one. If the callback fails, this will remove
2392 // it from the list, so don't assume that the list hasn't changed
2393 // after this point.
2394 showNextToastLocked();
2395 }
2396 }
2397
2398 private void scheduleTimeoutLocked(ToastRecord r)
2399 {
2400 mHandler.removeCallbacksAndMessages(r);
2401 Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r);
2402 long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY;
2403 mHandler.sendMessageDelayed(m, delay);
2404 }
2405
2406 private void handleTimeout(ToastRecord record)
2407 {
2408 if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback);
2409 synchronized (mToastQueue) {
2410 int index = indexOfToastLocked(record.pkg, record.callback);
2411 if (index >= 0) {
2412 cancelToastLocked(index);
2413 }
2414 }
2415 }
2416
2417 // lock on mToastQueue
2418 int indexOfToastLocked(String pkg, ITransientNotification callback)
2419 {
2420 IBinder cbak = callback.asBinder();
2421 ArrayList<ToastRecord> list = mToastQueue;
2422 int len = list.size();
2423 for (int i=0; i<len; i++) {
2424 ToastRecord r = list.get(i);
2425 if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) {
2426 return i;
2427 }
2428 }
2429 return -1;
2430 }
2431
2432 // lock on mToastQueue
2433 void keepProcessAliveLocked(int pid)
2434 {
2435 int toastCount = 0; // toasts from this pid
2436 ArrayList<ToastRecord> list = mToastQueue;
2437 int N = list.size();
2438 for (int i=0; i<N; i++) {
2439 ToastRecord r = list.get(i);
2440 if (r.pid == pid) {
2441 toastCount++;
2442 }
2443 }
2444 try {
2445 mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0);
2446 } catch (RemoteException e) {
2447 // Shouldn't happen.
2448 }
2449 }
2450
Chris Wrenf9536642014-04-17 10:01:54 -04002451 private void handleRankingReconsideration(Message message) {
Chris Wren470c1ac2014-05-21 15:28:10 -04002452 if (!(message.obj instanceof RankingReconsideration)) return;
2453 RankingReconsideration recon = (RankingReconsideration) message.obj;
2454 recon.run();
Chris Wren333a61c2014-05-28 16:40:57 -04002455 boolean changed;
Chris Wren470c1ac2014-05-21 15:28:10 -04002456 synchronized (mNotificationList) {
2457 final NotificationRecord record = mNotificationsByKey.get(recon.getKey());
2458 if (record == null) {
2459 return;
Chris Wrenf9536642014-04-17 10:01:54 -04002460 }
Chris Wren333a61c2014-05-28 16:40:57 -04002461 int indexBefore = findNotificationRecordIndexLocked(record);
2462 boolean interceptBefore = record.isIntercepted();
Chris Wren3ad4e3a2014-09-02 17:23:51 -04002463 int visibilityBefore = record.getPackageVisibilityOverride();
Chris Wren470c1ac2014-05-21 15:28:10 -04002464 recon.applyChangesLocked(record);
Chris Wren333a61c2014-05-28 16:40:57 -04002465 applyZenModeLocked(record);
Chris Wren54bbef42014-07-09 18:37:56 -04002466 mRankingHelper.sort(mNotificationList);
Chris Wren333a61c2014-05-28 16:40:57 -04002467 int indexAfter = findNotificationRecordIndexLocked(record);
2468 boolean interceptAfter = record.isIntercepted();
Chris Wren3ad4e3a2014-09-02 17:23:51 -04002469 int visibilityAfter = record.getPackageVisibilityOverride();
2470 changed = indexBefore != indexAfter || interceptBefore != interceptAfter
2471 || visibilityBefore != visibilityAfter;
Chris Wrena3446562014-06-03 18:11:47 -04002472 if (interceptBefore && !interceptAfter) {
2473 buzzBeepBlinkLocked(record);
2474 }
Chris Wrenf9536642014-04-17 10:01:54 -04002475 }
Chris Wren333a61c2014-05-28 16:40:57 -04002476 if (changed) {
Chris Wren470c1ac2014-05-21 15:28:10 -04002477 scheduleSendRankingUpdate();
2478 }
2479 }
2480
Chris Wren54bbef42014-07-09 18:37:56 -04002481 private void handleRankingConfigChange() {
2482 synchronized (mNotificationList) {
2483 final int N = mNotificationList.size();
2484 ArrayList<String> orderBefore = new ArrayList<String>(N);
Chris Wren3ad4e3a2014-09-02 17:23:51 -04002485 int[] visibilities = new int[N];
Chris Wren54bbef42014-07-09 18:37:56 -04002486 for (int i = 0; i < N; i++) {
2487 final NotificationRecord r = mNotificationList.get(i);
2488 orderBefore.add(r.getKey());
Chris Wren3ad4e3a2014-09-02 17:23:51 -04002489 visibilities[i] = r.getPackageVisibilityOverride();
Chris Wren54bbef42014-07-09 18:37:56 -04002490 mRankingHelper.extractSignals(r);
2491 }
Chris Wren54bbef42014-07-09 18:37:56 -04002492 for (int i = 0; i < N; i++) {
Chris Wren3ad4e3a2014-09-02 17:23:51 -04002493 mRankingHelper.sort(mNotificationList);
2494 final NotificationRecord r = mNotificationList.get(i);
2495 if (!orderBefore.get(i).equals(r.getKey())
2496 || visibilities[i] != r.getPackageVisibilityOverride()) {
Chris Wren54bbef42014-07-09 18:37:56 -04002497 scheduleSendRankingUpdate();
2498 return;
2499 }
2500 }
2501 }
2502 }
2503
Christoph Studerd5092bc2014-07-03 17:47:58 +02002504 // let zen mode evaluate this record
Chris Wren333a61c2014-05-28 16:40:57 -04002505 private void applyZenModeLocked(NotificationRecord record) {
Christoph Studerd5092bc2014-07-03 17:47:58 +02002506 record.setIntercepted(mZenModeHelper.shouldIntercept(record));
Chris Wren333a61c2014-05-28 16:40:57 -04002507 }
2508
Chris Wren470c1ac2014-05-21 15:28:10 -04002509 // lock on mNotificationList
2510 private int findNotificationRecordIndexLocked(NotificationRecord target) {
Chris Wren54bbef42014-07-09 18:37:56 -04002511 return mRankingHelper.indexOf(mNotificationList, target);
Chris Wrenf9536642014-04-17 10:01:54 -04002512 }
2513
2514 private void scheduleSendRankingUpdate() {
2515 mHandler.removeMessages(MESSAGE_SEND_RANKING_UPDATE);
2516 Message m = Message.obtain(mHandler, MESSAGE_SEND_RANKING_UPDATE);
2517 mHandler.sendMessage(m);
2518 }
2519
2520 private void handleSendRankingUpdate() {
2521 synchronized (mNotificationList) {
Chris Wren333a61c2014-05-28 16:40:57 -04002522 mListeners.notifyRankingUpdateLocked();
Chris Wrenf9536642014-04-17 10:01:54 -04002523 }
2524 }
2525
John Spurlockd8afe3c2014-08-01 14:04:07 -04002526 private void scheduleListenerHintsChanged(int state) {
2527 mHandler.removeMessages(MESSAGE_LISTENER_HINTS_CHANGED);
2528 mHandler.obtainMessage(MESSAGE_LISTENER_HINTS_CHANGED, state, 0).sendToTarget();
John Spurlock1fa865f2014-07-21 14:56:39 -04002529 }
2530
Christoph Studer85a384b2014-08-27 20:16:15 +02002531 private void scheduleInterruptionFilterChanged(int listenerInterruptionFilter) {
2532 mHandler.removeMessages(MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED);
2533 mHandler.obtainMessage(
2534 MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED,
2535 listenerInterruptionFilter,
2536 0).sendToTarget();
2537 }
2538
John Spurlockd8afe3c2014-08-01 14:04:07 -04002539 private void handleListenerHintsChanged(int hints) {
John Spurlock1fa865f2014-07-21 14:56:39 -04002540 synchronized (mNotificationList) {
John Spurlockd8afe3c2014-08-01 14:04:07 -04002541 mListeners.notifyListenerHintsChangedLocked(hints);
John Spurlock1fa865f2014-07-21 14:56:39 -04002542 }
2543 }
2544
Christoph Studer85a384b2014-08-27 20:16:15 +02002545 private void handleListenerInterruptionFilterChanged(int interruptionFilter) {
2546 synchronized (mNotificationList) {
2547 mListeners.notifyInterruptionFilterChanged(interruptionFilter);
2548 }
2549 }
2550
Adam Lesinski182f73f2013-12-05 16:48:06 -08002551 private final class WorkerHandler extends Handler
2552 {
2553 @Override
2554 public void handleMessage(Message msg)
2555 {
2556 switch (msg.what)
2557 {
2558 case MESSAGE_TIMEOUT:
2559 handleTimeout((ToastRecord)msg.obj);
2560 break;
John Spurlock056c5192014-04-20 21:52:01 -04002561 case MESSAGE_SAVE_POLICY_FILE:
2562 handleSavePolicyFile();
2563 break;
Chris Wrenf9536642014-04-17 10:01:54 -04002564 case MESSAGE_SEND_RANKING_UPDATE:
2565 handleSendRankingUpdate();
2566 break;
John Spurlockd8afe3c2014-08-01 14:04:07 -04002567 case MESSAGE_LISTENER_HINTS_CHANGED:
2568 handleListenerHintsChanged(msg.arg1);
John Spurlock1fa865f2014-07-21 14:56:39 -04002569 break;
Christoph Studer85a384b2014-08-27 20:16:15 +02002570 case MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED:
2571 handleListenerInterruptionFilterChanged(msg.arg1);
2572 break;
Chris Wrenf9536642014-04-17 10:01:54 -04002573 }
2574 }
2575
2576 }
2577
2578 private final class RankingWorkerHandler extends Handler
2579 {
2580 public RankingWorkerHandler(Looper looper) {
2581 super(looper);
2582 }
2583
2584 @Override
2585 public void handleMessage(Message msg) {
2586 switch (msg.what) {
2587 case MESSAGE_RECONSIDER_RANKING:
2588 handleRankingReconsideration(msg);
2589 break;
Chris Wren54bbef42014-07-09 18:37:56 -04002590 case MESSAGE_RANKING_CONFIG_CHANGE:
2591 handleRankingConfigChange();
2592 break;
Adam Lesinski182f73f2013-12-05 16:48:06 -08002593 }
2594 }
2595 }
2596
Adam Lesinski182f73f2013-12-05 16:48:06 -08002597 // Notifications
2598 // ============================================================================
2599 static int clamp(int x, int low, int high) {
2600 return (x < low) ? low : ((x > high) ? high : x);
2601 }
2602
2603 void sendAccessibilityEvent(Notification notification, CharSequence packageName) {
2604 AccessibilityManager manager = AccessibilityManager.getInstance(getContext());
svetoslavganov75986cf2009-05-14 22:28:01 -07002605 if (!manager.isEnabled()) {
2606 return;
2607 }
2608
2609 AccessibilityEvent event =
2610 AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
2611 event.setPackageName(packageName);
2612 event.setClassName(Notification.class.getName());
2613 event.setParcelableData(notification);
2614 CharSequence tickerText = notification.tickerText;
2615 if (!TextUtils.isEmpty(tickerText)) {
2616 event.getText().add(tickerText);
2617 }
2618
2619 manager.sendAccessibilityEvent(event);
2620 }
2621
Christoph Studer546bec82014-03-14 12:17:12 +01002622 private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason) {
Joe Onorato46439ce2010-11-19 13:56:21 -08002623 // tell the app
2624 if (sendDelete) {
Daniel Sandlerfde19b12013-01-17 00:21:05 -05002625 if (r.getNotification().deleteIntent != null) {
Joe Onorato46439ce2010-11-19 13:56:21 -08002626 try {
Daniel Sandlerfde19b12013-01-17 00:21:05 -05002627 r.getNotification().deleteIntent.send();
Joe Onorato46439ce2010-11-19 13:56:21 -08002628 } catch (PendingIntent.CanceledException ex) {
2629 // do nothing - there's no relevant way to recover, and
2630 // no reason to let this propagate
Daniel Sandler4f91efd2013-04-25 16:38:41 -04002631 Slog.w(TAG, "canceled PendingIntent for " + r.sbn.getPackageName(), ex);
Joe Onorato46439ce2010-11-19 13:56:21 -08002632 }
2633 }
2634 }
2635
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002636 // status bar
Daniel Sandlerfde19b12013-01-17 00:21:05 -05002637 if (r.getNotification().icon != 0) {
Christoph Studer71f18fd2014-05-20 17:02:04 +02002638 r.isCanceled = true;
Chris Wren333a61c2014-05-28 16:40:57 -04002639 mListeners.notifyRemovedLocked(r.sbn);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002640 }
2641
Chris Wren6054e612014-11-25 17:16:46 -05002642 final String canceledKey = r.getKey();
2643
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002644 // sound
Chris Wren6054e612014-11-25 17:16:46 -05002645 if (canceledKey.equals(mSoundNotificationKey)) {
2646 mSoundNotificationKey = null;
Jeff Sharkey098d5802012-04-26 17:30:34 -07002647 final long identity = Binder.clearCallingIdentity();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002648 try {
Adam Lesinski182f73f2013-12-05 16:48:06 -08002649 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
Jeff Sharkey098d5802012-04-26 17:30:34 -07002650 if (player != null) {
2651 player.stopAsync();
2652 }
2653 } catch (RemoteException e) {
2654 } finally {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002655 Binder.restoreCallingIdentity(identity);
2656 }
2657 }
2658
2659 // vibrate
Chris Wren6054e612014-11-25 17:16:46 -05002660 if (canceledKey.equals(mVibrateNotificationKey)) {
2661 mVibrateNotificationKey = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002662 long identity = Binder.clearCallingIdentity();
2663 try {
2664 mVibrator.cancel();
2665 }
2666 finally {
2667 Binder.restoreCallingIdentity(identity);
2668 }
2669 }
2670
2671 // light
Chris Wren6054e612014-11-25 17:16:46 -05002672 mLights.remove(canceledKey);
Daniel Sandler23d7c702013-03-07 16:32:06 -05002673
Christoph Studer546bec82014-03-14 12:17:12 +01002674 // Record usage stats
2675 switch (reason) {
2676 case REASON_DELEGATE_CANCEL:
2677 case REASON_DELEGATE_CANCEL_ALL:
2678 case REASON_LISTENER_CANCEL:
2679 case REASON_LISTENER_CANCEL_ALL:
2680 mUsageStats.registerDismissedByUser(r);
2681 break;
2682 case REASON_NOMAN_CANCEL:
2683 case REASON_NOMAN_CANCEL_ALL:
2684 mUsageStats.registerRemovedByApp(r);
2685 break;
2686 case REASON_DELEGATE_CLICK:
2687 mUsageStats.registerCancelDueToClick(r);
2688 break;
2689 default:
2690 mUsageStats.registerCancelUnknown(r);
2691 break;
2692 }
2693
Christoph Studercef37cf2014-07-25 14:18:17 +02002694 mNotificationsByKey.remove(r.sbn.getKey());
Christoph Studer265c1052014-07-23 17:14:33 +02002695 String groupKey = r.getGroupKey();
2696 NotificationRecord groupSummary = mSummaryByGroupKey.get(groupKey);
2697 if (groupSummary != null && groupSummary.getKey().equals(r.getKey())) {
2698 mSummaryByGroupKey.remove(groupKey);
2699 }
Christoph Studercef37cf2014-07-25 14:18:17 +02002700
Daniel Sandler23d7c702013-03-07 16:32:06 -05002701 // Save it for users of getHistoricalNotifications()
2702 mArchive.record(r.sbn);
Christoph Studer81e5b5f2014-10-22 17:19:56 +02002703
Chris Wren6054e612014-11-25 17:16:46 -05002704 EventLogTags.writeNotificationCanceled(canceledKey, reason);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002705 }
2706
2707 /**
Dianne Hackbornd8a43f62009-08-17 23:33:56 -07002708 * Cancels a notification ONLY if it has all of the {@code mustHaveFlags}
Doug Zongkerab5c49c2009-12-04 10:31:43 -08002709 * and none of the {@code mustNotHaveFlags}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002710 */
John Spurlocke6a7d932014-03-13 12:29:00 -04002711 void cancelNotification(final int callingUid, final int callingPid,
2712 final String pkg, final String tag, final int id,
Svetoslav Ganov835835e2013-08-04 20:17:52 -07002713 final int mustHaveFlags, final int mustNotHaveFlags, final boolean sendDelete,
John Spurlock7340fc82014-04-24 18:50:12 -04002714 final int userId, final int reason, final ManagedServiceInfo listener) {
Svetoslav Ganov835835e2013-08-04 20:17:52 -07002715 // In enqueueNotificationInternal notifications are added by scheduling the
2716 // work on the worker handler. Hence, we also schedule the cancel on this
2717 // handler to avoid a scenario where an add notification call followed by a
2718 // remove notification call ends up in not removing the notification.
2719 mHandler.post(new Runnable() {
2720 @Override
2721 public void run() {
Christoph Studere4ef156b2014-07-04 18:41:57 +02002722 String listenerName = listener == null ? null : listener.component.toShortString();
Chris Wrenbddb5bc2015-03-04 08:47:46 -08002723 if (DBG) EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, id, tag,
2724 userId, mustHaveFlags, mustNotHaveFlags, reason, listenerName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002725
Svetoslav Ganov835835e2013-08-04 20:17:52 -07002726 synchronized (mNotificationList) {
2727 int index = indexOfNotificationLocked(pkg, tag, id, userId);
2728 if (index >= 0) {
2729 NotificationRecord r = mNotificationList.get(index);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08002730
Christoph Studer546bec82014-03-14 12:17:12 +01002731 // Ideally we'd do this in the caller of this method. However, that would
2732 // require the caller to also find the notification.
2733 if (reason == REASON_DELEGATE_CLICK) {
2734 mUsageStats.registerClickedByUser(r);
2735 }
2736
Svetoslav Ganov835835e2013-08-04 20:17:52 -07002737 if ((r.getNotification().flags & mustHaveFlags) != mustHaveFlags) {
2738 return;
2739 }
2740 if ((r.getNotification().flags & mustNotHaveFlags) != 0) {
2741 return;
2742 }
2743
2744 mNotificationList.remove(index);
2745
Christoph Studer546bec82014-03-14 12:17:12 +01002746 cancelNotificationLocked(r, sendDelete, reason);
Christoph Studer265c1052014-07-23 17:14:33 +02002747 cancelGroupChildrenLocked(r, callingUid, callingPid, listenerName,
2748 REASON_GROUP_SUMMARY_CANCELED);
Svetoslav Ganov835835e2013-08-04 20:17:52 -07002749 updateLightsLocked();
2750 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002751 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002752 }
Svetoslav Ganov835835e2013-08-04 20:17:52 -07002753 });
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002754 }
2755
2756 /**
Daniel Sandler321e9c52012-10-12 10:59:26 -07002757 * Determine whether the userId applies to the notification in question, either because
2758 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard).
2759 */
2760 private boolean notificationMatchesUserId(NotificationRecord r, int userId) {
2761 return
2762 // looking for USER_ALL notifications? match everything
2763 userId == UserHandle.USER_ALL
2764 // a notification sent to USER_ALL matches any query
Daniel Sandlerfde19b12013-01-17 00:21:05 -05002765 || r.getUserId() == UserHandle.USER_ALL
Daniel Sandler321e9c52012-10-12 10:59:26 -07002766 // an exact user match
Daniel Sandlerfde19b12013-01-17 00:21:05 -05002767 || r.getUserId() == userId;
Daniel Sandler321e9c52012-10-12 10:59:26 -07002768 }
2769
2770 /**
Kenny Guy3a7c4a52014-03-03 18:24:03 +00002771 * Determine whether the userId applies to the notification in question, either because
2772 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard) or
Kenny Guy2a764942014-04-02 13:29:20 +01002773 * because it matches one of the users profiles.
Kenny Guy3a7c4a52014-03-03 18:24:03 +00002774 */
Kenny Guy2a764942014-04-02 13:29:20 +01002775 private boolean notificationMatchesCurrentProfiles(NotificationRecord r, int userId) {
Kenny Guya263e4e2014-03-03 18:24:03 +00002776 return notificationMatchesUserId(r, userId)
John Spurlockb408e8e2014-04-23 21:12:45 -04002777 || mUserProfiles.isCurrentProfile(r.getUserId());
Kenny Guy3a7c4a52014-03-03 18:24:03 +00002778 }
2779
2780 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002781 * Cancels all notifications from a given package that have all of the
2782 * {@code mustHaveFlags}.
2783 */
John Spurlocke6a7d932014-03-13 12:29:00 -04002784 boolean cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, int mustHaveFlags,
2785 int mustNotHaveFlags, boolean doit, int userId, int reason,
John Spurlock7340fc82014-04-24 18:50:12 -04002786 ManagedServiceInfo listener) {
Christoph Studere4ef156b2014-07-04 18:41:57 +02002787 String listenerName = listener == null ? null : listener.component.toShortString();
John Spurlocke6a7d932014-03-13 12:29:00 -04002788 EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
2789 pkg, userId, mustHaveFlags, mustNotHaveFlags, reason,
Christoph Studere4ef156b2014-07-04 18:41:57 +02002790 listenerName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002791
2792 synchronized (mNotificationList) {
2793 final int N = mNotificationList.size();
Christoph Studere4ef156b2014-07-04 18:41:57 +02002794 ArrayList<NotificationRecord> canceledNotifications = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002795 for (int i = N-1; i >= 0; --i) {
2796 NotificationRecord r = mNotificationList.get(i);
Daniel Sandler321e9c52012-10-12 10:59:26 -07002797 if (!notificationMatchesUserId(r, userId)) {
Dianne Hackborn41203752012-08-31 14:05:51 -07002798 continue;
2799 }
Amith Yamasani5ec00e92012-11-07 16:58:30 -08002800 // Don't remove notifications to all, if there's no package name specified
Daniel Sandlerfde19b12013-01-17 00:21:05 -05002801 if (r.getUserId() == UserHandle.USER_ALL && pkg == null) {
Amith Yamasani5ec00e92012-11-07 16:58:30 -08002802 continue;
2803 }
Daniel Sandlerfde19b12013-01-17 00:21:05 -05002804 if ((r.getFlags() & mustHaveFlags) != mustHaveFlags) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002805 continue;
2806 }
Daniel Sandlerfde19b12013-01-17 00:21:05 -05002807 if ((r.getFlags() & mustNotHaveFlags) != 0) {
Dianne Hackbornd8a43f62009-08-17 23:33:56 -07002808 continue;
2809 }
Daniel Sandler4f91efd2013-04-25 16:38:41 -04002810 if (pkg != null && !r.sbn.getPackageName().equals(pkg)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002811 continue;
2812 }
Christoph Studere4ef156b2014-07-04 18:41:57 +02002813 if (canceledNotifications == null) {
2814 canceledNotifications = new ArrayList<>();
2815 }
2816 canceledNotifications.add(r);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08002817 if (!doit) {
2818 return true;
2819 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002820 mNotificationList.remove(i);
Christoph Studer546bec82014-03-14 12:17:12 +01002821 cancelNotificationLocked(r, false, reason);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002822 }
Christoph Studere4ef156b2014-07-04 18:41:57 +02002823 if (doit && canceledNotifications != null) {
2824 final int M = canceledNotifications.size();
2825 for (int i = 0; i < M; i++) {
2826 cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid,
Christoph Studer265c1052014-07-23 17:14:33 +02002827 listenerName, REASON_GROUP_SUMMARY_CANCELED);
Christoph Studere4ef156b2014-07-04 18:41:57 +02002828 }
2829 }
2830 if (canceledNotifications != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002831 updateLightsLocked();
2832 }
Christoph Studere4ef156b2014-07-04 18:41:57 +02002833 return canceledNotifications != null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002834 }
2835 }
2836
Adam Lesinski350159c2014-03-27 11:15:11 -07002837 void cancelAllLocked(int callingUid, int callingPid, int userId, int reason,
John Spurlock7340fc82014-04-24 18:50:12 -04002838 ManagedServiceInfo listener, boolean includeCurrentProfiles) {
Christoph Studere4ef156b2014-07-04 18:41:57 +02002839 String listenerName = listener == null ? null : listener.component.toShortString();
John Spurlocke6a7d932014-03-13 12:29:00 -04002840 EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
Christoph Studere4ef156b2014-07-04 18:41:57 +02002841 null, userId, 0, 0, reason, listenerName);
Christoph Studer546bec82014-03-14 12:17:12 +01002842
Christoph Studere4ef156b2014-07-04 18:41:57 +02002843 ArrayList<NotificationRecord> canceledNotifications = null;
Adam Lesinskie8240262014-03-26 16:01:00 -07002844 final int N = mNotificationList.size();
2845 for (int i=N-1; i>=0; i--) {
2846 NotificationRecord r = mNotificationList.get(i);
Kenny Guya263e4e2014-03-03 18:24:03 +00002847 if (includeCurrentProfiles) {
2848 if (!notificationMatchesCurrentProfiles(r, userId)) {
2849 continue;
2850 }
2851 } else {
2852 if (!notificationMatchesUserId(r, userId)) {
2853 continue;
2854 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002855 }
2856
Adam Lesinskie8240262014-03-26 16:01:00 -07002857 if ((r.getFlags() & (Notification.FLAG_ONGOING_EVENT
2858 | Notification.FLAG_NO_CLEAR)) == 0) {
2859 mNotificationList.remove(i);
Christoph Studer546bec82014-03-14 12:17:12 +01002860 cancelNotificationLocked(r, true, reason);
Christoph Studere4ef156b2014-07-04 18:41:57 +02002861 // Make a note so we can cancel children later.
2862 if (canceledNotifications == null) {
2863 canceledNotifications = new ArrayList<>();
2864 }
2865 canceledNotifications.add(r);
Adam Lesinskie8240262014-03-26 16:01:00 -07002866 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002867 }
Christoph Studere4ef156b2014-07-04 18:41:57 +02002868 int M = canceledNotifications != null ? canceledNotifications.size() : 0;
2869 for (int i = 0; i < M; i++) {
2870 cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid,
Christoph Studer265c1052014-07-23 17:14:33 +02002871 listenerName, REASON_GROUP_SUMMARY_CANCELED);
Christoph Studere4ef156b2014-07-04 18:41:57 +02002872 }
Adam Lesinskie8240262014-03-26 16:01:00 -07002873 updateLightsLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002874 }
2875
Christoph Studere4ef156b2014-07-04 18:41:57 +02002876 // Warning: The caller is responsible for invoking updateLightsLocked().
2877 private void cancelGroupChildrenLocked(NotificationRecord r, int callingUid, int callingPid,
Christoph Studer265c1052014-07-23 17:14:33 +02002878 String listenerName, int reason) {
Christoph Studere4ef156b2014-07-04 18:41:57 +02002879 Notification n = r.getNotification();
Christoph Studer3f31f5d2014-07-31 16:55:32 +02002880 if (!n.isGroupSummary()) {
Christoph Studere4ef156b2014-07-04 18:41:57 +02002881 return;
2882 }
2883
2884 String pkg = r.sbn.getPackageName();
2885 int userId = r.getUserId();
2886
2887 if (pkg == null) {
2888 if (DBG) Log.e(TAG, "No package for group summary: " + r.getKey());
2889 return;
2890 }
2891
2892 final int N = mNotificationList.size();
2893 for (int i = N - 1; i >= 0; i--) {
2894 NotificationRecord childR = mNotificationList.get(i);
Christoph Studere4ef156b2014-07-04 18:41:57 +02002895 StatusBarNotification childSbn = childR.sbn;
Christoph Studerc44caa92014-08-22 19:16:00 +02002896 if (childR.getNotification().isGroupChild() &&
2897 childR.getGroupKey().equals(r.getGroupKey())) {
Christoph Studer265c1052014-07-23 17:14:33 +02002898 EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, childSbn.getId(),
2899 childSbn.getTag(), userId, 0, 0, reason, listenerName);
Christoph Studere4ef156b2014-07-04 18:41:57 +02002900 mNotificationList.remove(i);
Christoph Studer265c1052014-07-23 17:14:33 +02002901 cancelNotificationLocked(childR, false, reason);
Christoph Studere4ef156b2014-07-04 18:41:57 +02002902 }
2903 }
2904 }
2905
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002906 // lock on mNotificationList
Adam Lesinski182f73f2013-12-05 16:48:06 -08002907 void updateLightsLocked()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002908 {
The Android Open Source Project10592532009-03-18 17:39:46 -07002909 // handle notification lights
Chris Wren6054e612014-11-25 17:16:46 -05002910 NotificationRecord ledNotification = null;
2911 while (ledNotification == null && !mLights.isEmpty()) {
2912 final String owner = mLights.get(mLights.size() - 1);
2913 ledNotification = mNotificationsByKey.get(owner);
2914 if (ledNotification == null) {
2915 Slog.wtfStack(TAG, "LED Notification does not exist: " + owner);
2916 mLights.remove(owner);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002917 }
2918 }
Mike Lockwoodc22404a2009-12-02 11:15:02 -05002919
Mike Lockwood63b5ad92011-08-30 09:55:30 -04002920 // Don't flash while we are in a call or screen is on
Chris Wren6054e612014-11-25 17:16:46 -05002921 if (ledNotification == null || mInCall || mScreenOn) {
Mike Lockwood3cb67a32009-11-27 14:25:58 -05002922 mNotificationLight.turnOff();
John Spurlockcb566aa2014-08-03 22:58:28 -04002923 mStatusBar.notificationLightOff();
The Android Open Source Project10592532009-03-18 17:39:46 -07002924 } else {
Chris Wren6054e612014-11-25 17:16:46 -05002925 final Notification ledno = ledNotification.sbn.getNotification();
Daniel Sandlerfde19b12013-01-17 00:21:05 -05002926 int ledARGB = ledno.ledARGB;
2927 int ledOnMS = ledno.ledOnMS;
2928 int ledOffMS = ledno.ledOffMS;
2929 if ((ledno.defaults & Notification.DEFAULT_LIGHTS) != 0) {
Mike Lockwood670f9322010-01-20 12:13:36 -05002930 ledARGB = mDefaultNotificationColor;
2931 ledOnMS = mDefaultNotificationLedOn;
2932 ledOffMS = mDefaultNotificationLedOff;
2933 }
2934 if (mNotificationPulseEnabled) {
2935 // pulse repeatedly
Adam Lesinski182f73f2013-12-05 16:48:06 -08002936 mNotificationLight.setFlashing(ledARGB, Light.LIGHT_FLASH_TIMED,
Mike Lockwood670f9322010-01-20 12:13:36 -05002937 ledOnMS, ledOffMS);
Mike Lockwood670f9322010-01-20 12:13:36 -05002938 }
John Spurlock39292322014-08-13 11:00:59 -04002939 // let SystemUI make an independent decision
2940 mStatusBar.notificationLightPulse(ledARGB, ledOnMS, ledOffMS);
The Android Open Source Project10592532009-03-18 17:39:46 -07002941 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002942 }
2943
2944 // lock on mNotificationList
Adam Lesinski182f73f2013-12-05 16:48:06 -08002945 int indexOfNotificationLocked(String pkg, String tag, int id, int userId)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002946 {
2947 ArrayList<NotificationRecord> list = mNotificationList;
2948 final int len = list.size();
2949 for (int i=0; i<len; i++) {
2950 NotificationRecord r = list.get(i);
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -04002951 if (!notificationMatchesUserId(r, userId) || r.sbn.getId() != id) {
Dianne Hackborn41203752012-08-31 14:05:51 -07002952 continue;
2953 }
Fred Quintana6ecaff12009-09-25 14:23:13 -07002954 if (tag == null) {
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -04002955 if (r.sbn.getTag() != null) {
Fred Quintana6ecaff12009-09-25 14:23:13 -07002956 continue;
2957 }
2958 } else {
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -04002959 if (!tag.equals(r.sbn.getTag())) {
Fred Quintana6ecaff12009-09-25 14:23:13 -07002960 continue;
2961 }
2962 }
Daniel Sandler4f91efd2013-04-25 16:38:41 -04002963 if (r.sbn.getPackageName().equals(pkg)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002964 return i;
2965 }
2966 }
2967 return -1;
2968 }
2969
Christoph Studer71f18fd2014-05-20 17:02:04 +02002970 // lock on mNotificationList
2971 int indexOfNotificationLocked(String key) {
Christoph Studerc5115552014-06-12 20:22:31 +02002972 final int N = mNotificationList.size();
2973 for (int i = 0; i < N; i++) {
2974 if (key.equals(mNotificationList.get(i).getKey())) {
2975 return i;
2976 }
Christoph Studer71f18fd2014-05-20 17:02:04 +02002977 }
Christoph Studerc5115552014-06-12 20:22:31 +02002978 return -1;
Christoph Studer71f18fd2014-05-20 17:02:04 +02002979 }
2980
Mike Lockwoodc22404a2009-12-02 11:15:02 -05002981 private void updateNotificationPulse() {
2982 synchronized (mNotificationList) {
2983 updateLightsLocked();
2984 }
2985 }
John Spurlocke677d712014-02-13 12:52:19 -05002986
John Spurlock7340fc82014-04-24 18:50:12 -04002987 private static boolean isUidSystem(int uid) {
2988 final int appid = UserHandle.getAppId(uid);
2989 return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0);
2990 }
John Spurlockb408e8e2014-04-23 21:12:45 -04002991
John Spurlock7340fc82014-04-24 18:50:12 -04002992 private static boolean isCallerSystem() {
2993 return isUidSystem(Binder.getCallingUid());
2994 }
2995
2996 private static void checkCallerIsSystem() {
2997 if (isCallerSystem()) {
2998 return;
2999 }
3000 throw new SecurityException("Disallowed call for uid " + Binder.getCallingUid());
3001 }
3002
3003 private static void checkCallerIsSystemOrSameApp(String pkg) {
3004 if (isCallerSystem()) {
3005 return;
3006 }
3007 final int uid = Binder.getCallingUid();
3008 try {
3009 ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo(
3010 pkg, 0, UserHandle.getCallingUserId());
Dan Sandler09afc2e2014-07-18 14:29:20 -04003011 if (ai == null) {
3012 throw new SecurityException("Unknown package " + pkg);
3013 }
John Spurlock7340fc82014-04-24 18:50:12 -04003014 if (!UserHandle.isSameApp(ai.uid, uid)) {
3015 throw new SecurityException("Calling uid " + uid + " gave package"
3016 + pkg + " which is owned by uid " + ai.uid);
3017 }
3018 } catch (RemoteException re) {
3019 throw new SecurityException("Unknown package " + pkg + "\n" + re);
3020 }
3021 }
3022
John Spurlock32fe4c62014-10-02 12:16:02 -04003023 private static String callStateToString(int state) {
3024 switch (state) {
3025 case TelephonyManager.CALL_STATE_IDLE: return "CALL_STATE_IDLE";
3026 case TelephonyManager.CALL_STATE_RINGING: return "CALL_STATE_RINGING";
3027 case TelephonyManager.CALL_STATE_OFFHOOK: return "CALL_STATE_OFFHOOK";
3028 default: return "CALL_STATE_UNKNOWN_" + state;
3029 }
3030 }
3031
3032 private void listenForCallState() {
3033 TelephonyManager.from(getContext()).listen(new PhoneStateListener() {
3034 @Override
3035 public void onCallStateChanged(int state, String incomingNumber) {
3036 if (mCallState == state) return;
3037 if (DBG) Slog.d(TAG, "Call state changed: " + callStateToString(state));
3038 mCallState = state;
3039 }
3040 }, PhoneStateListener.LISTEN_CALL_STATE);
3041 }
3042
Christoph Studer05ad4822014-05-16 14:16:03 +02003043 /**
3044 * Generates a NotificationRankingUpdate from 'sbns', considering only
3045 * notifications visible to the given listener.
Chris Wren333a61c2014-05-28 16:40:57 -04003046 *
3047 * <p>Caller must hold a lock on mNotificationList.</p>
Christoph Studer05ad4822014-05-16 14:16:03 +02003048 */
Chris Wren333a61c2014-05-28 16:40:57 -04003049 private NotificationRankingUpdate makeRankingUpdateLocked(ManagedServiceInfo info) {
Christoph Studer05ad4822014-05-16 14:16:03 +02003050 int speedBumpIndex = -1;
Chris Wren333a61c2014-05-28 16:40:57 -04003051 final int N = mNotificationList.size();
3052 ArrayList<String> keys = new ArrayList<String>(N);
Christoph Studer1d599da2014-06-12 15:25:59 +02003053 ArrayList<String> interceptedKeys = new ArrayList<String>(N);
Chris Wren3ad4e3a2014-09-02 17:23:51 -04003054 Bundle visibilityOverrides = new Bundle();
Chris Wren333a61c2014-05-28 16:40:57 -04003055 for (int i = 0; i < N; i++) {
3056 NotificationRecord record = mNotificationList.get(i);
Christoph Studercef37cf2014-07-25 14:18:17 +02003057 if (!isVisibleToListener(record.sbn, info)) {
Christoph Studer05ad4822014-05-16 14:16:03 +02003058 continue;
3059 }
Chris Wren333a61c2014-05-28 16:40:57 -04003060 keys.add(record.sbn.getKey());
3061 if (record.isIntercepted()) {
Christoph Studer1d599da2014-06-12 15:25:59 +02003062 interceptedKeys.add(record.sbn.getKey());
Christoph Studer05ad4822014-05-16 14:16:03 +02003063 }
Chris Wren3ad4e3a2014-09-02 17:23:51 -04003064 if (record.getPackageVisibilityOverride()
3065 != NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE) {
3066 visibilityOverrides.putInt(record.sbn.getKey(),
3067 record.getPackageVisibilityOverride());
3068 }
Christoph Studer327bf782014-09-05 11:40:01 +02003069 // Find first min-prio notification for speedbump placement.
Christoph Studer05ad4822014-05-16 14:16:03 +02003070 if (speedBumpIndex == -1 &&
Christoph Studer327bf782014-09-05 11:40:01 +02003071 // Intrusiveness trumps priority, hence ignore intrusives.
3072 !record.isRecentlyIntrusive() &&
Christoph Studerfbac2142014-09-05 17:40:43 +02003073 // Currently, package priority is either PRIORITY_DEFAULT or PRIORITY_MAX, so
3074 // scanning for PRIORITY_MIN within the package bucket PRIORITY_DEFAULT
3075 // (or lower as a safeguard) is sufficient to find the speedbump index.
3076 // We'll have to revisit this when more package priority buckets are introduced.
3077 record.getPackagePriority() <= Notification.PRIORITY_DEFAULT &&
Chris Wren333a61c2014-05-28 16:40:57 -04003078 record.sbn.getNotification().priority == Notification.PRIORITY_MIN) {
Christoph Studer05ad4822014-05-16 14:16:03 +02003079 speedBumpIndex = keys.size() - 1;
3080 }
3081 }
3082 String[] keysAr = keys.toArray(new String[keys.size()]);
Christoph Studer1d599da2014-06-12 15:25:59 +02003083 String[] interceptedKeysAr = interceptedKeys.toArray(new String[interceptedKeys.size()]);
Chris Wren3ad4e3a2014-09-02 17:23:51 -04003084 return new NotificationRankingUpdate(keysAr, interceptedKeysAr, visibilityOverrides,
3085 speedBumpIndex);
Christoph Studer05ad4822014-05-16 14:16:03 +02003086 }
3087
Christoph Studercef37cf2014-07-25 14:18:17 +02003088 private boolean isVisibleToListener(StatusBarNotification sbn, ManagedServiceInfo listener) {
3089 if (!listener.enabledAndUserMatches(sbn.getUserId())) {
3090 return false;
3091 }
Justin Koh8d11a5a2014-08-04 18:29:49 -07003092 // TODO: remove this for older listeners.
Christoph Studercef37cf2014-07-25 14:18:17 +02003093 return true;
3094 }
3095
John Spurlock7340fc82014-04-24 18:50:12 -04003096 public class NotificationListeners extends ManagedServices {
3097
Christoph Studerb82bc782014-08-20 14:29:43 +02003098 private final ArraySet<ManagedServiceInfo> mLightTrimListeners = new ArraySet<>();
John Spurlock1fc476d2015-04-14 16:05:20 -04003099 private final ArrayMap<ComponentName, Policy.Token> mPolicyTokens = new ArrayMap<>();
Christoph Studer265c1052014-07-23 17:14:33 +02003100 private boolean mNotificationGroupsDesired;
Christoph Studerb82bc782014-08-20 14:29:43 +02003101
John Spurlock7340fc82014-04-24 18:50:12 -04003102 public NotificationListeners() {
3103 super(getContext(), mHandler, mNotificationList, mUserProfiles);
3104 }
3105
John Spurlock1fc476d2015-04-14 16:05:20 -04003106 public Policy.Token getPolicyToken(INotificationListener listener) {
3107 final ManagedServiceInfo info = checkServiceTokenLocked(listener);
3108 return info == null ? null : mPolicyTokens.get(info.component);
3109 }
3110
John Spurlock7340fc82014-04-24 18:50:12 -04003111 @Override
3112 protected Config getConfig() {
3113 Config c = new Config();
3114 c.caption = "notification listener";
3115 c.serviceInterface = NotificationListenerService.SERVICE_INTERFACE;
3116 c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_LISTENERS;
3117 c.bindPermission = android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE;
3118 c.settingsAction = Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS;
3119 c.clientLabel = R.string.notification_listener_binding_label;
3120 return c;
3121 }
3122
3123 @Override
3124 protected IInterface asInterface(IBinder binder) {
3125 return INotificationListener.Stub.asInterface(binder);
3126 }
3127
3128 @Override
John Spurlock3b98b3f2014-05-01 09:08:48 -04003129 public void onServiceAdded(ManagedServiceInfo info) {
3130 final INotificationListener listener = (INotificationListener) info.service;
Chris Wren333a61c2014-05-28 16:40:57 -04003131 final NotificationRankingUpdate update;
Christoph Studer05ad4822014-05-16 14:16:03 +02003132 synchronized (mNotificationList) {
Christoph Studer265c1052014-07-23 17:14:33 +02003133 updateNotificationGroupsDesiredLocked();
Chris Wren333a61c2014-05-28 16:40:57 -04003134 update = makeRankingUpdateLocked(info);
John Spurlock1fc476d2015-04-14 16:05:20 -04003135 mPolicyTokens.put(info.component, new Policy.Token(new Binder()));
Christoph Studer05ad4822014-05-16 14:16:03 +02003136 }
John Spurlock7340fc82014-04-24 18:50:12 -04003137 try {
Chris Wren333a61c2014-05-28 16:40:57 -04003138 listener.onListenerConnected(update);
John Spurlock7340fc82014-04-24 18:50:12 -04003139 } catch (RemoteException e) {
3140 // we tried
3141 }
3142 }
3143
John Spurlock1fa865f2014-07-21 14:56:39 -04003144 @Override
3145 protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
John Spurlockd8afe3c2014-08-01 14:04:07 -04003146 if (mListenersDisablingEffects.remove(removed)) {
3147 updateListenerHintsLocked();
Christoph Studer0d6ef4b2014-12-02 15:00:48 +01003148 updateEffectsSuppressorLocked();
John Spurlock1fa865f2014-07-21 14:56:39 -04003149 }
Christoph Studerb82bc782014-08-20 14:29:43 +02003150 mLightTrimListeners.remove(removed);
Christoph Studer265c1052014-07-23 17:14:33 +02003151 updateNotificationGroupsDesiredLocked();
John Spurlock1fc476d2015-04-14 16:05:20 -04003152 mPolicyTokens.remove(removed.component);
Christoph Studerb82bc782014-08-20 14:29:43 +02003153 }
3154
3155 public void setOnNotificationPostedTrimLocked(ManagedServiceInfo info, int trim) {
3156 if (trim == TRIM_LIGHT) {
3157 mLightTrimListeners.add(info);
3158 } else {
3159 mLightTrimListeners.remove(info);
3160 }
3161 }
3162
3163 public int getOnNotificationPostedTrim(ManagedServiceInfo info) {
3164 return mLightTrimListeners.contains(info) ? TRIM_LIGHT : TRIM_FULL;
3165
John Spurlock1fa865f2014-07-21 14:56:39 -04003166 }
3167
John Spurlock7340fc82014-04-24 18:50:12 -04003168 /**
3169 * asynchronously notify all listeners about a new notification
Christoph Studercef37cf2014-07-25 14:18:17 +02003170 *
3171 * <p>
3172 * Also takes care of removing a notification that has been visible to a listener before,
3173 * but isn't anymore.
John Spurlock7340fc82014-04-24 18:50:12 -04003174 */
Christoph Studercef37cf2014-07-25 14:18:17 +02003175 public void notifyPostedLocked(StatusBarNotification sbn, StatusBarNotification oldSbn) {
Christoph Studerb82bc782014-08-20 14:29:43 +02003176 // Lazily initialized snapshots of the notification.
3177 StatusBarNotification sbnClone = null;
3178 StatusBarNotification sbnCloneLight = null;
3179
John Spurlock7340fc82014-04-24 18:50:12 -04003180 for (final ManagedServiceInfo info : mServices) {
Christoph Studercef37cf2014-07-25 14:18:17 +02003181 boolean sbnVisible = isVisibleToListener(sbn, info);
3182 boolean oldSbnVisible = oldSbn != null ? isVisibleToListener(oldSbn, info) : false;
3183 // This notification hasn't been and still isn't visible -> ignore.
3184 if (!oldSbnVisible && !sbnVisible) {
Christoph Studer05ad4822014-05-16 14:16:03 +02003185 continue;
Chris Wrenf9536642014-04-17 10:01:54 -04003186 }
Chris Wren333a61c2014-05-28 16:40:57 -04003187 final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
Christoph Studercef37cf2014-07-25 14:18:17 +02003188
3189 // This notification became invisible -> remove the old one.
3190 if (oldSbnVisible && !sbnVisible) {
3191 final StatusBarNotification oldSbnLightClone = oldSbn.cloneLight();
3192 mHandler.post(new Runnable() {
3193 @Override
3194 public void run() {
3195 notifyRemoved(info, oldSbnLightClone, update);
3196 }
3197 });
Christoph Studer05ad4822014-05-16 14:16:03 +02003198 continue;
3199 }
Christoph Studercef37cf2014-07-25 14:18:17 +02003200
Christoph Studerb82bc782014-08-20 14:29:43 +02003201 final int trim = mListeners.getOnNotificationPostedTrim(info);
3202
3203 if (trim == TRIM_LIGHT && sbnCloneLight == null) {
3204 sbnCloneLight = sbn.cloneLight();
3205 } else if (trim == TRIM_FULL && sbnClone == null) {
3206 sbnClone = sbn.clone();
3207 }
3208 final StatusBarNotification sbnToPost =
3209 (trim == TRIM_FULL) ? sbnClone : sbnCloneLight;
3210
Christoph Studer05ad4822014-05-16 14:16:03 +02003211 mHandler.post(new Runnable() {
3212 @Override
3213 public void run() {
Christoph Studerb82bc782014-08-20 14:29:43 +02003214 notifyPosted(info, sbnToPost, update);
Christoph Studer05ad4822014-05-16 14:16:03 +02003215 }
3216 });
Kenny Guy3a7c4a52014-03-03 18:24:03 +00003217 }
3218 }
Kenny Guy3a7c4a52014-03-03 18:24:03 +00003219
John Spurlock7340fc82014-04-24 18:50:12 -04003220 /**
3221 * asynchronously notify all listeners about a removed notification
3222 */
Chris Wren333a61c2014-05-28 16:40:57 -04003223 public void notifyRemovedLocked(StatusBarNotification sbn) {
John Spurlock7340fc82014-04-24 18:50:12 -04003224 // make a copy in case changes are made to the underlying Notification object
3225 // NOTE: this copy is lightweight: it doesn't include heavyweight parts of the
3226 // notification
3227 final StatusBarNotification sbnLight = sbn.cloneLight();
Chris Wrenf9536642014-04-17 10:01:54 -04003228 for (final ManagedServiceInfo info : mServices) {
Christoph Studercef37cf2014-07-25 14:18:17 +02003229 if (!isVisibleToListener(sbn, info)) {
Christoph Studer05ad4822014-05-16 14:16:03 +02003230 continue;
Chris Wrenf9536642014-04-17 10:01:54 -04003231 }
Chris Wren333a61c2014-05-28 16:40:57 -04003232 final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
Christoph Studer05ad4822014-05-16 14:16:03 +02003233 mHandler.post(new Runnable() {
3234 @Override
3235 public void run() {
Christoph Studercef37cf2014-07-25 14:18:17 +02003236 notifyRemoved(info, sbnLight, update);
Christoph Studer05ad4822014-05-16 14:16:03 +02003237 }
3238 });
Chris Wrenf9536642014-04-17 10:01:54 -04003239 }
3240 }
3241
3242 /**
3243 * asynchronously notify all listeners about a reordering of notifications
Chris Wrenf9536642014-04-17 10:01:54 -04003244 */
Chris Wren333a61c2014-05-28 16:40:57 -04003245 public void notifyRankingUpdateLocked() {
Chris Wrenf9536642014-04-17 10:01:54 -04003246 for (final ManagedServiceInfo serviceInfo : mServices) {
Christoph Studer05ad4822014-05-16 14:16:03 +02003247 if (!serviceInfo.isEnabledForCurrentProfiles()) {
3248 continue;
3249 }
Christoph Studercef37cf2014-07-25 14:18:17 +02003250 final NotificationRankingUpdate update = makeRankingUpdateLocked(serviceInfo);
John Spurlock7340fc82014-04-24 18:50:12 -04003251 mHandler.post(new Runnable() {
3252 @Override
3253 public void run() {
Chris Wren333a61c2014-05-28 16:40:57 -04003254 notifyRankingUpdate(serviceInfo, update);
John Spurlock7340fc82014-04-24 18:50:12 -04003255 }
3256 });
Kenny Guya263e4e2014-03-03 18:24:03 +00003257 }
Kenny Guya263e4e2014-03-03 18:24:03 +00003258 }
Kenny Guya263e4e2014-03-03 18:24:03 +00003259
John Spurlockd8afe3c2014-08-01 14:04:07 -04003260 public void notifyListenerHintsChangedLocked(final int hints) {
John Spurlock1fa865f2014-07-21 14:56:39 -04003261 for (final ManagedServiceInfo serviceInfo : mServices) {
3262 if (!serviceInfo.isEnabledForCurrentProfiles()) {
3263 continue;
3264 }
3265 mHandler.post(new Runnable() {
3266 @Override
3267 public void run() {
John Spurlockd8afe3c2014-08-01 14:04:07 -04003268 notifyListenerHintsChanged(serviceInfo, hints);
John Spurlock1fa865f2014-07-21 14:56:39 -04003269 }
3270 });
3271 }
3272 }
3273
Christoph Studer85a384b2014-08-27 20:16:15 +02003274 public void notifyInterruptionFilterChanged(final int interruptionFilter) {
3275 for (final ManagedServiceInfo serviceInfo : mServices) {
3276 if (!serviceInfo.isEnabledForCurrentProfiles()) {
3277 continue;
3278 }
3279 mHandler.post(new Runnable() {
3280 @Override
3281 public void run() {
3282 notifyInterruptionFilterChanged(serviceInfo, interruptionFilter);
3283 }
3284 });
3285 }
3286 }
3287
Christoph Studercef37cf2014-07-25 14:18:17 +02003288 private void notifyPosted(final ManagedServiceInfo info,
Christoph Studer05ad4822014-05-16 14:16:03 +02003289 final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) {
John Spurlock7340fc82014-04-24 18:50:12 -04003290 final INotificationListener listener = (INotificationListener)info.service;
Griff Hazen84a00ea2014-09-02 17:10:47 -07003291 StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
John Spurlock7340fc82014-04-24 18:50:12 -04003292 try {
Griff Hazen84a00ea2014-09-02 17:10:47 -07003293 listener.onNotificationPosted(sbnHolder, rankingUpdate);
John Spurlock7340fc82014-04-24 18:50:12 -04003294 } catch (RemoteException ex) {
3295 Log.e(TAG, "unable to notify listener (posted): " + listener, ex);
3296 }
3297 }
3298
Christoph Studercef37cf2014-07-25 14:18:17 +02003299 private void notifyRemoved(ManagedServiceInfo info, StatusBarNotification sbn,
Christoph Studer05ad4822014-05-16 14:16:03 +02003300 NotificationRankingUpdate rankingUpdate) {
John Spurlock7340fc82014-04-24 18:50:12 -04003301 if (!info.enabledAndUserMatches(sbn.getUserId())) {
3302 return;
3303 }
Christoph Studer05ad4822014-05-16 14:16:03 +02003304 final INotificationListener listener = (INotificationListener) info.service;
Griff Hazen84a00ea2014-09-02 17:10:47 -07003305 StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
John Spurlock7340fc82014-04-24 18:50:12 -04003306 try {
Griff Hazen84a00ea2014-09-02 17:10:47 -07003307 listener.onNotificationRemoved(sbnHolder, rankingUpdate);
John Spurlock7340fc82014-04-24 18:50:12 -04003308 } catch (RemoteException ex) {
3309 Log.e(TAG, "unable to notify listener (removed): " + listener, ex);
John Spurlockb408e8e2014-04-23 21:12:45 -04003310 }
Kenny Guya263e4e2014-03-03 18:24:03 +00003311 }
Chris Wrenf9536642014-04-17 10:01:54 -04003312
Christoph Studer05ad4822014-05-16 14:16:03 +02003313 private void notifyRankingUpdate(ManagedServiceInfo info,
3314 NotificationRankingUpdate rankingUpdate) {
3315 final INotificationListener listener = (INotificationListener) info.service;
Chris Wrenf9536642014-04-17 10:01:54 -04003316 try {
Christoph Studer05ad4822014-05-16 14:16:03 +02003317 listener.onNotificationRankingUpdate(rankingUpdate);
Chris Wrenf9536642014-04-17 10:01:54 -04003318 } catch (RemoteException ex) {
3319 Log.e(TAG, "unable to notify listener (ranking update): " + listener, ex);
3320 }
3321 }
John Spurlock1fa865f2014-07-21 14:56:39 -04003322
John Spurlockd8afe3c2014-08-01 14:04:07 -04003323 private void notifyListenerHintsChanged(ManagedServiceInfo info, int hints) {
John Spurlock1fa865f2014-07-21 14:56:39 -04003324 final INotificationListener listener = (INotificationListener) info.service;
3325 try {
John Spurlockd8afe3c2014-08-01 14:04:07 -04003326 listener.onListenerHintsChanged(hints);
John Spurlock1fa865f2014-07-21 14:56:39 -04003327 } catch (RemoteException ex) {
John Spurlockd8afe3c2014-08-01 14:04:07 -04003328 Log.e(TAG, "unable to notify listener (listener hints): " + listener, ex);
John Spurlock1fa865f2014-07-21 14:56:39 -04003329 }
3330 }
Justin Koh38156c52014-06-04 13:57:49 -07003331
Christoph Studer85a384b2014-08-27 20:16:15 +02003332 private void notifyInterruptionFilterChanged(ManagedServiceInfo info,
3333 int interruptionFilter) {
3334 final INotificationListener listener = (INotificationListener) info.service;
3335 try {
3336 listener.onInterruptionFilterChanged(interruptionFilter);
3337 } catch (RemoteException ex) {
3338 Log.e(TAG, "unable to notify listener (interruption filter): " + listener, ex);
3339 }
3340 }
3341
Justin Koh38156c52014-06-04 13:57:49 -07003342 private boolean isListenerPackage(String packageName) {
3343 if (packageName == null) {
3344 return false;
3345 }
3346 // TODO: clean up locking object later
3347 synchronized (mNotificationList) {
3348 for (final ManagedServiceInfo serviceInfo : mServices) {
3349 if (packageName.equals(serviceInfo.component.getPackageName())) {
3350 return true;
3351 }
3352 }
3353 }
3354 return false;
3355 }
Christoph Studer265c1052014-07-23 17:14:33 +02003356
3357 /**
3358 * Returns whether any of the currently registered listeners wants to receive notification
3359 * groups.
3360 *
3361 * <p>Currently we assume groups are desired by non-SystemUI listeners.</p>
3362 */
3363 public boolean notificationGroupsDesired() {
3364 return mNotificationGroupsDesired;
3365 }
3366
3367 private void updateNotificationGroupsDesiredLocked() {
3368 mNotificationGroupsDesired = true;
3369 // No listeners, no groups.
3370 if (mServices.isEmpty()) {
3371 mNotificationGroupsDesired = false;
3372 return;
3373 }
3374 // One listener: Check whether it's SysUI.
3375 if (mServices.size() == 1 &&
3376 mServices.get(0).component.getPackageName().equals("com.android.systemui")) {
3377 mNotificationGroupsDesired = false;
3378 return;
3379 }
3380 }
Kenny Guya263e4e2014-03-03 18:24:03 +00003381 }
John Spurlock25e2d242014-06-27 13:58:23 -04003382
3383 public static final class DumpFilter {
3384 public String pkgFilter;
John Spurlock50806fc2014-07-15 10:22:02 -04003385 public boolean zen;
John Spurlock25e2d242014-06-27 13:58:23 -04003386
3387 public static DumpFilter parseFromArguments(String[] args) {
John Spurlock50806fc2014-07-15 10:22:02 -04003388 if (args != null && args.length == 2 && "p".equals(args[0])
3389 && args[1] != null && !args[1].trim().isEmpty()) {
3390 final DumpFilter filter = new DumpFilter();
3391 filter.pkgFilter = args[1].trim().toLowerCase();
3392 return filter;
John Spurlock25e2d242014-06-27 13:58:23 -04003393 }
John Spurlock50806fc2014-07-15 10:22:02 -04003394 if (args != null && args.length == 1 && "zen".equals(args[0])) {
3395 final DumpFilter filter = new DumpFilter();
3396 filter.zen = true;
3397 return filter;
3398 }
3399 return null;
John Spurlock25e2d242014-06-27 13:58:23 -04003400 }
3401
3402 public boolean matches(StatusBarNotification sbn) {
John Spurlock50806fc2014-07-15 10:22:02 -04003403 return zen ? true : sbn != null
3404 && (matches(sbn.getPackageName()) || matches(sbn.getOpPkg()));
John Spurlock25e2d242014-06-27 13:58:23 -04003405 }
3406
3407 public boolean matches(ComponentName component) {
John Spurlock50806fc2014-07-15 10:22:02 -04003408 return zen ? true : component != null && matches(component.getPackageName());
John Spurlock25e2d242014-06-27 13:58:23 -04003409 }
3410
3411 public boolean matches(String pkg) {
John Spurlock50806fc2014-07-15 10:22:02 -04003412 return zen ? true : pkg != null && pkg.toLowerCase().contains(pkgFilter);
3413 }
3414
3415 @Override
3416 public String toString() {
3417 return zen ? "zen" : ('\'' + pkgFilter + '\'');
John Spurlock25e2d242014-06-27 13:58:23 -04003418 }
3419 }
Griff Hazen84a00ea2014-09-02 17:10:47 -07003420
3421 /**
3422 * Wrapper for a StatusBarNotification object that allows transfer across a oneway
3423 * binder without sending large amounts of data over a oneway transaction.
3424 */
3425 private static final class StatusBarNotificationHolder
3426 extends IStatusBarNotificationHolder.Stub {
Griff Hazene9aac5f2014-09-05 20:04:09 -07003427 private StatusBarNotification mValue;
Griff Hazen84a00ea2014-09-02 17:10:47 -07003428
3429 public StatusBarNotificationHolder(StatusBarNotification value) {
3430 mValue = value;
3431 }
3432
Griff Hazene9aac5f2014-09-05 20:04:09 -07003433 /** Get the held value and clear it. This function should only be called once per holder */
Griff Hazen84a00ea2014-09-02 17:10:47 -07003434 @Override
3435 public StatusBarNotification get() {
Griff Hazene9aac5f2014-09-05 20:04:09 -07003436 StatusBarNotification value = mValue;
3437 mValue = null;
3438 return value;
Griff Hazen84a00ea2014-09-02 17:10:47 -07003439 }
3440 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003441}