blob: 3929004518f1b6ee7fbb95d3975476c237349009 [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
Chris Wrene0ba7eb2016-03-04 17:30:43 -050019import static android.service.notification.NotificationRankerService.REASON_APP_CANCEL;
20import static android.service.notification.NotificationRankerService.REASON_APP_CANCEL_ALL;
21import static android.service.notification.NotificationRankerService.REASON_DELEGATE_CANCEL;
22import static android.service.notification.NotificationRankerService.REASON_DELEGATE_CANCEL_ALL;
23import static android.service.notification.NotificationRankerService.REASON_DELEGATE_CLICK;
24import static android.service.notification.NotificationRankerService.REASON_DELEGATE_ERROR;
25import static android.service.notification.NotificationRankerService.REASON_GROUP_OPTIMIZATION;
26import static android.service.notification.NotificationRankerService.REASON_GROUP_SUMMARY_CANCELED;
27import static android.service.notification.NotificationRankerService.REASON_LISTENER_CANCEL;
28import static android.service.notification.NotificationRankerService.REASON_LISTENER_CANCEL_ALL;
29import static android.service.notification.NotificationRankerService.REASON_PACKAGE_BANNED;
30import static android.service.notification.NotificationRankerService.REASON_PACKAGE_CHANGED;
31import static android.service.notification.NotificationRankerService.REASON_PACKAGE_SUSPENDED;
32import static android.service.notification.NotificationRankerService.REASON_PROFILE_TURNED_OFF;
Julia Reynoldse46bb372016-03-17 11:05:58 -040033import static android.service.notification.NotificationRankerService.REASON_UNAUTOBUNDLED;
Chris Wrene0ba7eb2016-03-04 17:30:43 -050034import static android.service.notification.NotificationRankerService.REASON_USER_STOPPED;
Jason Monk63506742015-12-16 12:06:51 -050035import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_EFFECTS;
Bryce Lee7219ada2016-04-08 10:54:23 -070036import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_NOTIFICATION_EFFECTS;
37import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS;
Julia Reynoldsd5607292016-02-05 15:25:58 -050038import static android.service.notification.NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_OFF;
Julia Reynolds61721582016-01-05 08:35:25 -050039import static android.service.notification.NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_ON;
Christoph Studerb82bc782014-08-20 14:29:43 +020040import static android.service.notification.NotificationListenerService.TRIM_FULL;
41import static android.service.notification.NotificationListenerService.TRIM_LIGHT;
Julia Reynoldsf0f629f2016-02-25 09:34:04 -050042import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_DEFAULT;
Chris Wren4a4b49d2016-02-09 11:25:08 -050043import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_NONE;
Jeff Sharkey098d5802012-04-26 17:30:34 -070044import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
svetoslavganov75986cf2009-05-14 22:28:01 -070045
Chris Wren51017d02015-12-15 15:34:46 -050046import android.Manifest;
Wei Liu97e56662016-03-04 10:52:33 -080047import android.annotation.Nullable;
Dianne Hackborn41203752012-08-31 14:05:51 -070048import android.app.ActivityManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080049import android.app.ActivityManagerNative;
John Spurlock7340fc82014-04-24 18:50:12 -040050import android.app.AppGlobals;
Daniel Sandler4a900ac2013-01-30 14:04:10 -050051import android.app.AppOpsManager;
Julia Reynoldsa47a27f2015-08-24 08:31:47 -040052import android.app.AutomaticZenRule;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080053import android.app.IActivityManager;
54import android.app.INotificationManager;
55import android.app.ITransientNotification;
56import android.app.Notification;
John Spurlockb4782522014-08-22 14:54:46 -040057import android.app.NotificationManager;
John Spurlock1fc476d2015-04-14 16:05:20 -040058import android.app.NotificationManager.Policy;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080059import android.app.PendingIntent;
60import android.app.StatusBarManager;
John Spurlock35ef0a62015-05-28 11:24:10 -040061import android.app.backup.BackupManager;
Amith Yamasanif47e51e2015-04-17 10:02:15 -070062import android.app.usage.UsageEvents;
Amith Yamasanif47e51e2015-04-17 10:02:15 -070063import android.app.usage.UsageStatsManagerInternal;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080064import android.content.BroadcastReceiver;
Daniel Sandler5feceeb2013-03-22 18:29:23 -070065import android.content.ComponentName;
Dianne Hackborn1dac2772009-06-26 18:16:48 -070066import android.content.ContentResolver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080067import android.content.Context;
68import android.content.Intent;
69import android.content.IntentFilter;
John Spurlock7340fc82014-04-24 18:50:12 -040070import android.content.pm.ApplicationInfo;
Kenny Guy70058402014-10-28 20:45:06 +000071import android.content.pm.IPackageManager;
Daniel Sandler4a900ac2013-01-30 14:04:10 -050072import android.content.pm.PackageInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080073import android.content.pm.PackageManager;
74import android.content.pm.PackageManager.NameNotFoundException;
Christoph Studercee44ba2014-05-20 18:36:43 +020075import android.content.pm.ParceledListSlice;
Chris Wren66189fc2015-06-25 14:04:33 -040076import android.content.pm.UserInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080077import android.content.res.Resources;
Dianne Hackborn1dac2772009-06-26 18:16:48 -070078import android.database.ContentObserver;
John Spurlock7b414672014-07-18 13:02:39 -040079import android.media.AudioAttributes;
svetoslavganov75986cf2009-05-14 22:28:01 -070080import android.media.AudioManager;
John Spurlockcdb57ae2015-02-11 19:04:11 -050081import android.media.AudioManagerInternal;
Jean-Michel Triviceb79bc2014-09-05 11:09:14 -070082import android.media.AudioSystem;
Jeff Sharkey098d5802012-04-26 17:30:34 -070083import android.media.IRingtonePlayer;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080084import android.net.Uri;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080085import android.os.Binder;
John Spurlock2b122f42014-08-27 16:29:47 -040086import android.os.Bundle;
John Spurlock056c5192014-04-20 21:52:01 -040087import android.os.Environment;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080088import android.os.Handler;
Chris Wrenf9536642014-04-17 10:01:54 -040089import android.os.HandlerThread;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080090import android.os.IBinder;
John Spurlock7340fc82014-04-24 18:50:12 -040091import android.os.IInterface;
Chris Wrenf9536642014-04-17 10:01:54 -040092import android.os.Looper;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080093import android.os.Message;
Dianne Hackbornd8a43f62009-08-17 23:33:56 -070094import android.os.Process;
svetoslavganov75986cf2009-05-14 22:28:01 -070095import android.os.RemoteException;
Selim Cinekb5605e52015-02-20 18:21:41 +010096import android.os.SystemProperties;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -070097import android.os.UserHandle;
Chris Wren66189fc2015-06-25 14:04:33 -040098import android.os.UserManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080099import android.os.Vibrator;
100import android.provider.Settings;
Julia Reynoldse46bb372016-03-17 11:05:58 -0400101import android.service.notification.Adjustment;
Chris Wren333a61c2014-05-28 16:40:57 -0400102import android.service.notification.Condition;
John Spurlock7340fc82014-04-24 18:50:12 -0400103import android.service.notification.IConditionProvider;
Chris Wren333a61c2014-05-28 16:40:57 -0400104import android.service.notification.INotificationListener;
Griff Hazen84a00ea2014-09-02 17:10:47 -0700105import android.service.notification.IStatusBarNotificationHolder;
Chris Wrene0ba7eb2016-03-04 17:30:43 -0500106import android.service.notification.NotificationRankerService;
John Spurlock7340fc82014-04-24 18:50:12 -0400107import android.service.notification.NotificationListenerService;
Christoph Studer05ad4822014-05-16 14:16:03 +0200108import android.service.notification.NotificationRankingUpdate;
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700109import android.service.notification.StatusBarNotification;
John Spurlock056c5192014-04-20 21:52:01 -0400110import android.service.notification.ZenModeConfig;
John Spurlock32fe4c62014-10-02 12:16:02 -0400111import android.telephony.PhoneStateListener;
Daniel Sandlere96ffb12010-03-11 13:38:06 -0500112import android.telephony.TelephonyManager;
svetoslavganov75986cf2009-05-14 22:28:01 -0700113import android.text.TextUtils;
John Spurlocka4294292014-03-24 18:02:32 -0400114import android.util.ArrayMap;
John Spurlock1fa865f2014-07-21 14:56:39 -0400115import android.util.ArraySet;
Dianne Hackborn39606a02012-07-31 17:54:35 -0700116import android.util.AtomicFile;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800117import android.util.Log;
Andy Stadler110988c2010-12-03 14:29:16 -0800118import android.util.Slog;
Bryce Lee7219ada2016-04-08 10:54:23 -0700119import android.util.SparseArray;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400120import android.util.Xml;
svetoslavganov75986cf2009-05-14 22:28:01 -0700121import android.view.accessibility.AccessibilityEvent;
122import android.view.accessibility.AccessibilityManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800123import android.widget.Toast;
Rubin Xu7eadc1b2016-02-01 16:13:45 +0000124
Scott Greenwald9a05b312013-06-28 00:37:54 -0400125import com.android.internal.R;
Chris Wren93bb8b82016-03-29 14:35:05 -0400126import com.android.internal.annotations.VisibleForTesting;
Chris Wrend1dbc922015-06-19 17:51:16 -0400127import com.android.internal.statusbar.NotificationVisibility;
John Spurlock056c5192014-04-20 21:52:01 -0400128import com.android.internal.util.FastXmlSerializer;
Julia Reynoldsa47a27f2015-08-24 08:31:47 -0400129import com.android.internal.util.Preconditions;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800130import com.android.server.EventLogTags;
Amith Yamasanif47e51e2015-04-17 10:02:15 -0700131import com.android.server.LocalServices;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800132import com.android.server.SystemService;
133import com.android.server.lights.Light;
134import com.android.server.lights.LightsManager;
John Spurlock7340fc82014-04-24 18:50:12 -0400135import com.android.server.notification.ManagedServices.ManagedServiceInfo;
John Spurlockb408e8e2014-04-23 21:12:45 -0400136import com.android.server.statusbar.StatusBarManagerInternal;
Ruben Brunkdd18a0b2015-12-04 16:16:31 -0800137import com.android.server.vr.VrManagerInternal;
Ruben Brunke24b9a62016-02-16 21:38:24 -0800138import com.android.server.notification.ManagedServices.UserProfiles;
Ruben Brunkdd18a0b2015-12-04 16:16:31 -0800139
John Spurlockb408e8e2014-04-23 21:12:45 -0400140import libcore.io.IoUtils;
Rubin Xu7eadc1b2016-02-01 16:13:45 +0000141
Chris Wrene4b38802015-07-07 15:54:19 -0400142import org.json.JSONException;
143import org.json.JSONObject;
Jeff Sharkey098d5802012-04-26 17:30:34 -0700144import org.xmlpull.v1.XmlPullParser;
145import org.xmlpull.v1.XmlPullParserException;
John Spurlock056c5192014-04-20 21:52:01 -0400146import org.xmlpull.v1.XmlSerializer;
Jeff Sharkey098d5802012-04-26 17:30:34 -0700147
John Spurlock35ef0a62015-05-28 11:24:10 -0400148import java.io.ByteArrayInputStream;
149import java.io.ByteArrayOutputStream;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400150import java.io.File;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800151import java.io.FileDescriptor;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400152import java.io.FileInputStream;
153import java.io.FileNotFoundException;
John Spurlock056c5192014-04-20 21:52:01 -0400154import java.io.FileOutputStream;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400155import java.io.IOException;
John Spurlock35ef0a62015-05-28 11:24:10 -0400156import java.io.InputStream;
157import java.io.OutputStream;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800158import java.io.PrintWriter;
Wojciech Staszkiewicz9e9e2e72015-05-08 14:58:46 +0100159import java.nio.charset.StandardCharsets;
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500160import java.util.ArrayDeque;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800161import java.util.ArrayList;
Julia Reynolds1c9bd422016-03-15 09:25:56 -0400162import java.util.Arrays;
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500163import java.util.Iterator;
John Spurlock7c74f782015-06-04 13:01:42 -0400164import java.util.List;
Chris Wrenacf424a2016-03-15 12:48:55 -0400165import java.util.Map;
Christoph Studer265c1052014-07-23 17:14:33 +0200166import java.util.Map.Entry;
John Spurlockb4782522014-08-22 14:54:46 -0400167import java.util.Objects;
Chris Wren0efdb882016-03-01 17:17:47 -0500168import java.util.Set;
Chris Wren51017d02015-12-15 15:34:46 -0500169import java.util.concurrent.TimeUnit;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400170
Daniel Sandlerd0a2f862010-08-03 15:29:31 -0400171/** {@hide} */
Adam Lesinski182f73f2013-12-05 16:48:06 -0800172public class NotificationManagerService extends SystemService {
173 static final String TAG = "NotificationService";
Christoph Studer1f32c652014-11-26 15:32:20 +0100174 static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
Selim Cinek40412492015-12-08 18:03:22 -0800175 public static final boolean ENABLE_CHILD_NOTIFICATIONS
176 = SystemProperties.getBoolean("debug.child_notifs", true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800177
Adam Lesinski182f73f2013-12-05 16:48:06 -0800178 static final int MAX_PACKAGE_NOTIFICATIONS = 50;
Joe Onoratobd73d012010-06-04 11:44:54 -0700179
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800180 // message codes
Adam Lesinski182f73f2013-12-05 16:48:06 -0800181 static final int MESSAGE_TIMEOUT = 2;
John Spurlock056c5192014-04-20 21:52:01 -0400182 static final int MESSAGE_SAVE_POLICY_FILE = 3;
Chris Wren51017d02015-12-15 15:34:46 -0500183 static final int MESSAGE_SEND_RANKING_UPDATE = 4;
184 static final int MESSAGE_LISTENER_HINTS_CHANGED = 5;
185 static final int MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED = 6;
186
187 // ranking thread messages
188 private static final int MESSAGE_RECONSIDER_RANKING = 1000;
189 private static final int MESSAGE_RANKING_SORT = 1001;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800190
Adam Lesinski182f73f2013-12-05 16:48:06 -0800191 static final int LONG_DELAY = 3500; // 3.5 seconds
192 static final int SHORT_DELAY = 2000; // 2 seconds
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800193
Adam Lesinski182f73f2013-12-05 16:48:06 -0800194 static final long[] DEFAULT_VIBRATE_PATTERN = {0, 250, 250, 250};
Christoph Studer265c1052014-07-23 17:14:33 +0200195
Adam Lesinski182f73f2013-12-05 16:48:06 -0800196 static final int VIBRATE_PATTERN_MAXLEN = 8 * 2 + 1; // up to eight bumps
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800197
Adam Lesinski182f73f2013-12-05 16:48:06 -0800198 static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_NOTIFICATION;
Daniel Sandler526fa0e2012-12-04 14:51:50 -0500199
Adam Lesinski182f73f2013-12-05 16:48:06 -0800200 static final boolean ENABLE_BLOCKED_NOTIFICATIONS = true;
201 static final boolean ENABLE_BLOCKED_TOASTS = true;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400202
Christoph Studer12aeda82014-09-23 19:08:56 +0200203 // When #matchesCallFilter is called from the ringer, wait at most
204 // 3s to resolve the contacts. This timeout is required since
205 // ContactsProvider might take a long time to start up.
206 //
207 // Return STARRED_CONTACT when the timeout is hit in order to avoid
208 // missed calls in ZEN mode "Important".
209 static final int MATCHES_CALL_FILTER_CONTACTS_TIMEOUT_MS = 3000;
210 static final float MATCHES_CALL_FILTER_TIMEOUT_AFFINITY =
211 ValidateNotificationPeople.STARRED_CONTACT;
212
Christoph Studer265c1052014-07-23 17:14:33 +0200213 /** notification_enqueue status value for a newly enqueued notification. */
214 private static final int EVENTLOG_ENQUEUE_STATUS_NEW = 0;
215
216 /** notification_enqueue status value for an existing notification. */
217 private static final int EVENTLOG_ENQUEUE_STATUS_UPDATE = 1;
218
219 /** notification_enqueue status value for an ignored notification. */
220 private static final int EVENTLOG_ENQUEUE_STATUS_IGNORED = 2;
Chris Wren0efdb882016-03-01 17:17:47 -0500221 private String mRankerServicePackageName;
Christoph Studer265c1052014-07-23 17:14:33 +0200222
Adam Lesinski182f73f2013-12-05 16:48:06 -0800223 private IActivityManager mAm;
224 AudioManager mAudioManager;
John Spurlockcdb57ae2015-02-11 19:04:11 -0500225 AudioManagerInternal mAudioManagerInternal;
Wei Liu97e56662016-03-04 10:52:33 -0800226 @Nullable StatusBarManagerInternal mStatusBar;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800227 Vibrator mVibrator;
Ruben Brunkdd18a0b2015-12-04 16:16:31 -0800228 private VrManagerInternal mVrManagerInternal;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800229
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800230 final IBinder mForegroundToken = new Binder();
Chris Wren93bb8b82016-03-29 14:35:05 -0400231 private Handler mHandler;
Chris Wrenf9536642014-04-17 10:01:54 -0400232 private final HandlerThread mRankingThread = new HandlerThread("ranker",
233 Process.THREAD_PRIORITY_BACKGROUND);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800234
Adam Lesinski182f73f2013-12-05 16:48:06 -0800235 private Light mNotificationLight;
236 Light mAttentionLight;
Mike Lockwood670f9322010-01-20 12:13:36 -0500237 private int mDefaultNotificationColor;
238 private int mDefaultNotificationLedOn;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800239
Mike Lockwood670f9322010-01-20 12:13:36 -0500240 private int mDefaultNotificationLedOff;
Daniel Sandleredbb3802012-11-13 20:49:47 -0800241 private long[] mDefaultVibrationPattern;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800242
Daniel Sandleredbb3802012-11-13 20:49:47 -0800243 private long[] mFallbackVibrationPattern;
Chris Wren5116a822014-06-04 15:59:50 -0400244 private boolean mUseAttentionLight;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800245 boolean mSystemReady;
Daniel Sandleredbb3802012-11-13 20:49:47 -0800246
John Spurlockd8afe3c2014-08-01 14:04:07 -0400247 private boolean mDisableNotificationEffects;
John Spurlock32fe4c62014-10-02 12:16:02 -0400248 private int mCallState;
Chris Wren6054e612014-11-25 17:16:46 -0500249 private String mSoundNotificationKey;
250 private String mVibrateNotificationKey;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800251
Bryce Lee7219ada2016-04-08 10:54:23 -0700252 private final SparseArray<ArraySet<ManagedServiceInfo>> mListenersDisablingEffects =
253 new SparseArray<ArraySet<ManagedServiceInfo>>();
254 private List<ComponentName> mEffectsSuppressors = new ArrayList<ComponentName>();
John Spurlockd8afe3c2014-08-01 14:04:07 -0400255 private int mListenerHints; // right now, all hints are global
John Spurlock83104102015-02-12 23:25:12 -0500256 private int mInterruptionFilter = NotificationListenerService.INTERRUPTION_FILTER_UNKNOWN;
John Spurlock1fa865f2014-07-21 14:56:39 -0400257
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500258 // for enabling and disabling notification pulse behavior
Mike Lockwood63b5ad92011-08-30 09:55:30 -0400259 private boolean mScreenOn = true;
Daniel Sandlere96ffb12010-03-11 13:38:06 -0500260 private boolean mInCall = false;
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500261 private boolean mNotificationPulseEnabled;
262
Daniel Sandler09a247e2013-02-14 10:24:17 -0500263 // used as a mutex for access to all active notifications & listeners
Adam Lesinski182f73f2013-12-05 16:48:06 -0800264 final ArrayList<NotificationRecord> mNotificationList =
Fred Quintana6ecaff12009-09-25 14:23:13 -0700265 new ArrayList<NotificationRecord>();
John Spurlocka4294292014-03-24 18:02:32 -0400266 final ArrayMap<String, NotificationRecord> mNotificationsByKey =
267 new ArrayMap<String, NotificationRecord>();
Julia Reynoldseae43fb2016-05-09 12:42:58 -0400268 final ArrayMap<Integer, ArrayMap<String, String>> mAutobundledSummaries = new ArrayMap<>();
Adam Lesinski182f73f2013-12-05 16:48:06 -0800269 final ArrayList<ToastRecord> mToastQueue = new ArrayList<ToastRecord>();
Christoph Studer265c1052014-07-23 17:14:33 +0200270 final ArrayMap<String, NotificationRecord> mSummaryByGroupKey = new ArrayMap<>();
John Spurlock7c74f782015-06-04 13:01:42 -0400271 final PolicyAccess mPolicyAccess = new PolicyAccess();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800272
Chris Wren6054e612014-11-25 17:16:46 -0500273 // The last key in this list owns the hardware.
274 ArrayList<String> mLights = new ArrayList<>();
svetoslavganov75986cf2009-05-14 22:28:01 -0700275
Adam Lesinski182f73f2013-12-05 16:48:06 -0800276 private AppOpsManager mAppOps;
Amith Yamasanif47e51e2015-04-17 10:02:15 -0700277 private UsageStatsManagerInternal mAppUsageStats;
Daniel Sandler4a900ac2013-01-30 14:04:10 -0500278
Griff Hazen9f637d12014-06-10 11:13:51 -0700279 private Archive mArchive;
280
John Spurlock21258a32015-05-27 18:22:55 -0400281 // Persistent storage for notification policy
Daniel Sandler0da673f2012-04-11 12:33:16 -0400282 private AtomicFile mPolicyFile;
John Spurlock21258a32015-05-27 18:22:55 -0400283
Daniel Sandler0da673f2012-04-11 12:33:16 -0400284 private static final int DB_VERSION = 1;
285
John Spurlock21258a32015-05-27 18:22:55 -0400286 private static final String TAG_NOTIFICATION_POLICY = "notification-policy";
Daniel Sandler0da673f2012-04-11 12:33:16 -0400287 private static final String ATTR_VERSION = "version";
288
Chris Wren54bbef42014-07-09 18:37:56 -0400289 private RankingHelper mRankingHelper;
Scott Greenwald9a05b312013-06-28 00:37:54 -0400290
John Spurlockb408e8e2014-04-23 21:12:45 -0400291 private final UserProfiles mUserProfiles = new UserProfiles();
John Spurlock7340fc82014-04-24 18:50:12 -0400292 private NotificationListeners mListeners;
Chris Wren0efdb882016-03-01 17:17:47 -0500293 private NotificationRankers mRankerServices;
John Spurlock7340fc82014-04-24 18:50:12 -0400294 private ConditionProviders mConditionProviders;
Christoph Studer1c3f81f2014-04-16 15:05:56 +0200295 private NotificationUsageStats mUsageStats;
Christoph Studer546bec82014-03-14 12:17:12 +0100296
John Spurlocke6a7d932014-03-13 12:29:00 -0400297 private static final int MY_UID = Process.myUid();
298 private static final int MY_PID = Process.myPid();
Chris Wren51017d02015-12-15 15:34:46 -0500299 private RankingHandler mRankingHandler;
John Spurlocke6a7d932014-03-13 12:29:00 -0400300
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500301 private static class Archive {
Griff Hazen9f637d12014-06-10 11:13:51 -0700302 final int mBufferSize;
303 final ArrayDeque<StatusBarNotification> mBuffer;
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500304
Griff Hazen9f637d12014-06-10 11:13:51 -0700305 public Archive(int size) {
306 mBufferSize = size;
307 mBuffer = new ArrayDeque<StatusBarNotification>(mBufferSize);
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500308 }
Jeff Sharkey0c1baf92013-04-03 13:08:52 -0700309
Daniel Sandler5e62e3a2013-04-15 20:57:02 -0400310 public String toString() {
311 final StringBuilder sb = new StringBuilder();
312 final int N = mBuffer.size();
313 sb.append("Archive (");
314 sb.append(N);
315 sb.append(" notification");
316 sb.append((N==1)?")":"s)");
317 return sb.toString();
318 }
319
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500320 public void record(StatusBarNotification nr) {
Griff Hazen9f637d12014-06-10 11:13:51 -0700321 if (mBuffer.size() == mBufferSize) {
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500322 mBuffer.removeFirst();
323 }
Daniel Sandler26b81d52013-05-20 20:56:43 -0400324
325 // We don't want to store the heavy bits of the notification in the archive,
326 // but other clients in the system process might be using the object, so we
327 // store a (lightened) copy.
328 mBuffer.addLast(nr.cloneLight());
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500329 }
330
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500331 public Iterator<StatusBarNotification> descendingIterator() {
332 return mBuffer.descendingIterator();
333 }
Daniel Sandler78d0d252013-02-12 08:14:52 -0500334
335 public StatusBarNotification[] getArray(int count) {
Griff Hazen9f637d12014-06-10 11:13:51 -0700336 if (count == 0) count = mBufferSize;
Daniel Sandler78d0d252013-02-12 08:14:52 -0500337 final StatusBarNotification[] a
338 = new StatusBarNotification[Math.min(count, mBuffer.size())];
339 Iterator<StatusBarNotification> iter = descendingIterator();
340 int i=0;
341 while (iter.hasNext() && i < count) {
342 a[i++] = iter.next();
343 }
344 return a;
345 }
346
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500347 }
348
John Spurlock35ef0a62015-05-28 11:24:10 -0400349 private void readPolicyXml(InputStream stream, boolean forRestore)
350 throws XmlPullParserException, NumberFormatException, IOException {
351 final XmlPullParser parser = Xml.newPullParser();
352 parser.setInput(stream, StandardCharsets.UTF_8.name());
353
Chris Wrenacf424a2016-03-15 12:48:55 -0400354 while (parser.next() != END_DOCUMENT) {
John Spurlock35ef0a62015-05-28 11:24:10 -0400355 mZenModeHelper.readXml(parser, forRestore);
356 mRankingHelper.readXml(parser, forRestore);
357 }
358 }
359
John Spurlock056c5192014-04-20 21:52:01 -0400360 private void loadPolicyFile() {
John Spurlock21258a32015-05-27 18:22:55 -0400361 if (DBG) Slog.d(TAG, "loadPolicyFile");
John Spurlock056c5192014-04-20 21:52:01 -0400362 synchronized(mPolicyFile) {
Daniel Sandler0da673f2012-04-11 12:33:16 -0400363
John Spurlock056c5192014-04-20 21:52:01 -0400364 FileInputStream infile = null;
365 try {
366 infile = mPolicyFile.openRead();
John Spurlock35ef0a62015-05-28 11:24:10 -0400367 readPolicyXml(infile, false /*forRestore*/);
John Spurlock056c5192014-04-20 21:52:01 -0400368 } catch (FileNotFoundException e) {
369 // No data yet
370 } catch (IOException e) {
371 Log.wtf(TAG, "Unable to read notification policy", e);
372 } catch (NumberFormatException e) {
373 Log.wtf(TAG, "Unable to parse notification policy", e);
374 } catch (XmlPullParserException e) {
375 Log.wtf(TAG, "Unable to parse notification policy", e);
376 } finally {
377 IoUtils.closeQuietly(infile);
378 }
379 }
380 }
381
382 public void savePolicyFile() {
383 mHandler.removeMessages(MESSAGE_SAVE_POLICY_FILE);
384 mHandler.sendEmptyMessage(MESSAGE_SAVE_POLICY_FILE);
385 }
386
387 private void handleSavePolicyFile() {
John Spurlock21258a32015-05-27 18:22:55 -0400388 if (DBG) Slog.d(TAG, "handleSavePolicyFile");
John Spurlock056c5192014-04-20 21:52:01 -0400389 synchronized (mPolicyFile) {
390 final FileOutputStream stream;
391 try {
392 stream = mPolicyFile.startWrite();
393 } catch (IOException e) {
394 Slog.w(TAG, "Failed to save policy file", e);
395 return;
396 }
397
398 try {
John Spurlock35ef0a62015-05-28 11:24:10 -0400399 writePolicyXml(stream, false /*forBackup*/);
John Spurlock056c5192014-04-20 21:52:01 -0400400 mPolicyFile.finishWrite(stream);
401 } catch (IOException e) {
402 Slog.w(TAG, "Failed to save policy file, restoring backup", e);
403 mPolicyFile.failWrite(stream);
Daniel Sandler0da673f2012-04-11 12:33:16 -0400404 }
405 }
John Spurlock35ef0a62015-05-28 11:24:10 -0400406 BackupManager.dataChanged(getContext().getPackageName());
407 }
408
409 private void writePolicyXml(OutputStream stream, boolean forBackup) throws IOException {
410 final XmlSerializer out = new FastXmlSerializer();
411 out.setOutput(stream, StandardCharsets.UTF_8.name());
412 out.startDocument(null, true);
413 out.startTag(null, TAG_NOTIFICATION_POLICY);
414 out.attribute(null, ATTR_VERSION, Integer.toString(DB_VERSION));
415 mZenModeHelper.writeXml(out, forBackup);
416 mRankingHelper.writeXml(out, forBackup);
417 out.endTag(null, TAG_NOTIFICATION_POLICY);
418 out.endDocument();
Daniel Sandler0da673f2012-04-11 12:33:16 -0400419 }
420
Daniel Sandler4a900ac2013-01-30 14:04:10 -0500421 /** Use this when you actually want to post a notification or toast.
422 *
423 * Unchecked. Not exposed via Binder, but can be called in the course of enqueue*().
424 */
425 private boolean noteNotificationOp(String pkg, int uid) {
426 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
427 != AppOpsManager.MODE_ALLOWED) {
428 Slog.v(TAG, "notifications are disabled by AppOps for " + pkg);
429 return false;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400430 }
Daniel Sandler4a900ac2013-01-30 14:04:10 -0500431 return true;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400432 }
433
Chris Wren66189fc2015-06-25 14:04:33 -0400434 /** Use this to check if a package can post a notification or toast. */
435 private boolean checkNotificationOp(String pkg, int uid) {
436 return mAppOps.checkOp(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
Andrei Stingaceanu355b2322016-02-12 16:43:51 +0000437 == AppOpsManager.MODE_ALLOWED && !isPackageSuspendedForUser(pkg, uid);
Chris Wren66189fc2015-06-25 14:04:33 -0400438 }
439
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800440 private static final class ToastRecord
441 {
442 final int pid;
443 final String pkg;
444 final ITransientNotification callback;
445 int duration;
446
447 ToastRecord(int pid, String pkg, ITransientNotification callback, int duration)
448 {
449 this.pid = pid;
450 this.pkg = pkg;
451 this.callback = callback;
452 this.duration = duration;
453 }
454
455 void update(int duration) {
456 this.duration = duration;
457 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800458
John Spurlock25e2d242014-06-27 13:58:23 -0400459 void dump(PrintWriter pw, String prefix, DumpFilter filter) {
460 if (filter != null && !filter.matches(pkg)) return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800461 pw.println(prefix + this);
462 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800463
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800464 @Override
465 public final String toString()
466 {
467 return "ToastRecord{"
468 + Integer.toHexString(System.identityHashCode(this))
469 + " pkg=" + pkg
470 + " callback=" + callback
471 + " duration=" + duration;
472 }
473 }
474
Adam Lesinski182f73f2013-12-05 16:48:06 -0800475 private final NotificationDelegate mNotificationDelegate = new NotificationDelegate() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800476
Adam Lesinski182f73f2013-12-05 16:48:06 -0800477 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800478 public void onSetDisabled(int status) {
479 synchronized (mNotificationList) {
John Spurlockd8afe3c2014-08-01 14:04:07 -0400480 mDisableNotificationEffects =
481 (status & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0;
John Spurlock32fe4c62014-10-02 12:16:02 -0400482 if (disableNotificationEffects(null) != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800483 // cancel whatever's going on
484 long identity = Binder.clearCallingIdentity();
485 try {
Adam Lesinski182f73f2013-12-05 16:48:06 -0800486 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
Jeff Sharkey098d5802012-04-26 17:30:34 -0700487 if (player != null) {
488 player.stopAsync();
489 }
490 } catch (RemoteException e) {
491 } finally {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800492 Binder.restoreCallingIdentity(identity);
493 }
494
495 identity = Binder.clearCallingIdentity();
496 try {
497 mVibrator.cancel();
Jeff Sharkey098d5802012-04-26 17:30:34 -0700498 } finally {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800499 Binder.restoreCallingIdentity(identity);
500 }
501 }
502 }
503 }
504
Adam Lesinski182f73f2013-12-05 16:48:06 -0800505 @Override
John Spurlocke6a7d932014-03-13 12:29:00 -0400506 public void onClearAll(int callingUid, int callingPid, int userId) {
Adam Lesinskie8240262014-03-26 16:01:00 -0700507 synchronized (mNotificationList) {
Kenny Guya263e4e2014-03-03 18:24:03 +0000508 cancelAllLocked(callingUid, callingPid, userId, REASON_DELEGATE_CANCEL_ALL, null,
509 /*includeCurrentProfiles*/ true);
Adam Lesinskie8240262014-03-26 16:01:00 -0700510 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800511 }
512
Adam Lesinski182f73f2013-12-05 16:48:06 -0800513 @Override
Christoph Studer03b87a22014-04-30 17:33:27 +0200514 public void onNotificationClick(int callingUid, int callingPid, String key) {
515 synchronized (mNotificationList) {
Christoph Studer03b87a22014-04-30 17:33:27 +0200516 NotificationRecord r = mNotificationsByKey.get(key);
517 if (r == null) {
518 Log.w(TAG, "No notification with key: " + key);
519 return;
520 }
Chris Wrene6ddb8a2015-05-27 15:21:00 -0400521 final long now = System.currentTimeMillis();
522 EventLogTags.writeNotificationClicked(key,
523 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
524
Christoph Studer03b87a22014-04-30 17:33:27 +0200525 StatusBarNotification sbn = r.sbn;
526 cancelNotification(callingUid, callingPid, sbn.getPackageName(), sbn.getTag(),
527 sbn.getId(), Notification.FLAG_AUTO_CANCEL,
528 Notification.FLAG_FOREGROUND_SERVICE, false, r.getUserId(),
529 REASON_DELEGATE_CLICK, null);
530 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800531 }
532
Adam Lesinski182f73f2013-12-05 16:48:06 -0800533 @Override
Christoph Studer4da84cd2014-10-21 17:24:20 +0200534 public void onNotificationActionClick(int callingUid, int callingPid, String key,
535 int actionIndex) {
536 synchronized (mNotificationList) {
Christoph Studer4da84cd2014-10-21 17:24:20 +0200537 NotificationRecord r = mNotificationsByKey.get(key);
538 if (r == null) {
539 Log.w(TAG, "No notification with key: " + key);
540 return;
541 }
Chris Wrene6ddb8a2015-05-27 15:21:00 -0400542 final long now = System.currentTimeMillis();
543 EventLogTags.writeNotificationActionClicked(key, actionIndex,
544 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
Christoph Studer4da84cd2014-10-21 17:24:20 +0200545 // TODO: Log action click via UsageStats.
546 }
547 }
548
549 @Override
John Spurlocke6a7d932014-03-13 12:29:00 -0400550 public void onNotificationClear(int callingUid, int callingPid,
551 String pkg, String tag, int id, int userId) {
552 cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
Kenny Guy3a7c4a52014-03-03 18:24:03 +0000553 Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
John Spurlocke6a7d932014-03-13 12:29:00 -0400554 true, userId, REASON_DELEGATE_CANCEL, null);
Daniel Sandler0f0b11c2010-08-04 15:54:58 -0400555 }
556
Adam Lesinski182f73f2013-12-05 16:48:06 -0800557 @Override
Chris Wrenb659c4f2015-06-25 17:12:27 -0400558 public void onPanelRevealed(boolean clearEffects, int items) {
559 EventLogTags.writeNotificationPanelRevealed(items);
Christoph Studer1f32c652014-11-26 15:32:20 +0100560 if (clearEffects) {
561 clearEffects();
562 }
563 }
564
565 @Override
566 public void onPanelHidden() {
567 EventLogTags.writeNotificationPanelHidden();
568 }
569
570 @Override
571 public void clearEffects() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800572 synchronized (mNotificationList) {
Christoph Studer1f32c652014-11-26 15:32:20 +0100573 if (DBG) Slog.d(TAG, "clearEffects");
Chris Wren93bb8b82016-03-29 14:35:05 -0400574 clearSoundLocked();
575 clearVibrateLocked();
576 clearLightsLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800577 }
578 }
Joe Onorato005847b2010-06-04 16:08:02 -0400579
Adam Lesinski182f73f2013-12-05 16:48:06 -0800580 @Override
John Spurlocke6a7d932014-03-13 12:29:00 -0400581 public void onNotificationError(int callingUid, int callingPid, String pkg, String tag, int id,
Kenny Guy3a7c4a52014-03-03 18:24:03 +0000582 int uid, int initialPid, String message, int userId) {
Daniel Sandlerd0a2f862010-08-03 15:29:31 -0400583 Slog.d(TAG, "onNotification error pkg=" + pkg + " tag=" + tag + " id=" + id
584 + "; will crashApplication(uid=" + uid + ", pid=" + initialPid + ")");
John Spurlocke6a7d932014-03-13 12:29:00 -0400585 cancelNotification(callingUid, callingPid, pkg, tag, id, 0, 0, false, userId,
586 REASON_DELEGATE_ERROR, null);
Dianne Hackborn9d39d0c2010-06-24 15:57:42 -0700587 long ident = Binder.clearCallingIdentity();
588 try {
589 ActivityManagerNative.getDefault().crashApplication(uid, initialPid, pkg,
590 "Bad notification posted from package " + pkg
591 + ": " + message);
592 } catch (RemoteException e) {
593 }
594 Binder.restoreCallingIdentity(ident);
Joe Onorato005847b2010-06-04 16:08:02 -0400595 }
John Spurlocke677d712014-02-13 12:52:19 -0500596
597 @Override
Chris Wrend1dbc922015-06-19 17:51:16 -0400598 public void onNotificationVisibilityChanged(NotificationVisibility[] newlyVisibleKeys,
599 NotificationVisibility[] noLongerVisibleKeys) {
Christoph Studerffeb0c32014-05-07 22:23:56 +0200600 synchronized (mNotificationList) {
Chris Wrend1dbc922015-06-19 17:51:16 -0400601 for (NotificationVisibility nv : newlyVisibleKeys) {
602 NotificationRecord r = mNotificationsByKey.get(nv.key);
Christoph Studerffeb0c32014-05-07 22:23:56 +0200603 if (r == null) continue;
Chris Wrend1dbc922015-06-19 17:51:16 -0400604 r.setVisibility(true, nv.rank);
605 nv.recycle();
Christoph Studerffeb0c32014-05-07 22:23:56 +0200606 }
607 // Note that we might receive this event after notifications
608 // have already left the system, e.g. after dismissing from the
609 // shade. Hence not finding notifications in
610 // mNotificationsByKey is not an exceptional condition.
Chris Wrend1dbc922015-06-19 17:51:16 -0400611 for (NotificationVisibility nv : noLongerVisibleKeys) {
612 NotificationRecord r = mNotificationsByKey.get(nv.key);
Christoph Studerffeb0c32014-05-07 22:23:56 +0200613 if (r == null) continue;
Chris Wrend1dbc922015-06-19 17:51:16 -0400614 r.setVisibility(false, nv.rank);
615 nv.recycle();
Christoph Studerffeb0c32014-05-07 22:23:56 +0200616 }
617 }
Christoph Studer92b389d2014-04-01 18:44:40 +0200618 }
Chris Wren78403d72014-07-28 10:23:24 +0100619
620 @Override
621 public void onNotificationExpansionChanged(String key,
622 boolean userAction, boolean expanded) {
Chris Wren78403d72014-07-28 10:23:24 +0100623 synchronized (mNotificationList) {
624 NotificationRecord r = mNotificationsByKey.get(key);
625 if (r != null) {
626 r.stats.onExpansionChanged(userAction, expanded);
Chris Wrene6ddb8a2015-05-27 15:21:00 -0400627 final long now = System.currentTimeMillis();
628 EventLogTags.writeNotificationExpansion(key,
629 userAction ? 1 : 0, expanded ? 1 : 0,
630 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
Chris Wren78403d72014-07-28 10:23:24 +0100631 }
632 }
633 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800634 };
635
Chris Wren93bb8b82016-03-29 14:35:05 -0400636 private void clearSoundLocked() {
637 mSoundNotificationKey = null;
638 long identity = Binder.clearCallingIdentity();
639 try {
640 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
641 if (player != null) {
642 player.stopAsync();
643 }
644 } catch (RemoteException e) {
645 } finally {
646 Binder.restoreCallingIdentity(identity);
647 }
648 }
649
650 private void clearVibrateLocked() {
651 mVibrateNotificationKey = null;
652 long identity = Binder.clearCallingIdentity();
653 try {
654 mVibrator.cancel();
655 } finally {
656 Binder.restoreCallingIdentity(identity);
657 }
658 }
659
660 private void clearLightsLocked() {
661 // light
662 mLights.clear();
663 updateLightsLocked();
664 }
665
Kenny Guy70058402014-10-28 20:45:06 +0000666 private final BroadcastReceiver mPackageIntentReceiver = new BroadcastReceiver() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800667 @Override
668 public void onReceive(Context context, Intent intent) {
669 String action = intent.getAction();
Dianne Hackborn29cd7f12015-01-08 10:37:05 -0800670 if (action == null) {
671 return;
672 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800673
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800674 boolean queryRestart = false;
Chris Wrenae9bb572013-05-15 14:50:28 -0400675 boolean queryRemove = false;
Daniel Sandler26ece572012-06-01 15:38:46 -0400676 boolean packageChanged = false;
John Spurlock79f78922013-05-16 09:10:05 -0400677 boolean cancelNotifications = true;
Andrei Stingaceanu0122f6512016-01-22 15:33:03 +0000678 int reason = REASON_PACKAGE_CHANGED;
Chris Wrenf9536642014-04-17 10:01:54 -0400679
Chris Wren3da73022013-05-10 14:41:21 -0400680 if (action.equals(Intent.ACTION_PACKAGE_ADDED)
Chris Wrenae9bb572013-05-15 14:50:28 -0400681 || (queryRemove=action.equals(Intent.ACTION_PACKAGE_REMOVED))
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800682 || action.equals(Intent.ACTION_PACKAGE_RESTARTED)
Daniel Sandler26ece572012-06-01 15:38:46 -0400683 || (packageChanged=action.equals(Intent.ACTION_PACKAGE_CHANGED))
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800684 || (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART))
Andrei Stingaceanu0122f6512016-01-22 15:33:03 +0000685 || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)
686 || action.equals(Intent.ACTION_PACKAGES_SUSPENDED)) {
Kenny Guy70058402014-10-28 20:45:06 +0000687 int changeUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
688 UserHandle.USER_ALL);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800689 String pkgList[] = null;
Chris Wrenae9bb572013-05-15 14:50:28 -0400690 boolean queryReplace = queryRemove &&
691 intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
John Spurlocke77bb362014-04-26 10:24:59 -0400692 if (DBG) Slog.i(TAG, "action=" + action + " queryReplace=" + queryReplace);
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800693 if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800694 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
Andrei Stingaceanu0122f6512016-01-22 15:33:03 +0000695 } else if (action.equals(Intent.ACTION_PACKAGES_SUSPENDED)) {
696 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
697 reason = REASON_PACKAGE_SUSPENDED;
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800698 } else if (queryRestart) {
699 pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800700 } else {
701 Uri uri = intent.getData();
702 if (uri == null) {
703 return;
704 }
705 String pkgName = uri.getSchemeSpecificPart();
706 if (pkgName == null) {
707 return;
708 }
Daniel Sandler26ece572012-06-01 15:38:46 -0400709 if (packageChanged) {
710 // We cancel notifications for packages which have just been disabled
Christopher Tate06e5fed2013-10-09 14:39:15 -0700711 try {
Kenny Guy70058402014-10-28 20:45:06 +0000712 final IPackageManager pm = AppGlobals.getPackageManager();
713 final int enabled = pm.getApplicationEnabledSetting(pkgName,
714 changeUserId != UserHandle.USER_ALL ? changeUserId :
Xiaohui Chenddbe4ca2015-08-13 16:20:56 -0700715 UserHandle.USER_SYSTEM);
Christopher Tate06e5fed2013-10-09 14:39:15 -0700716 if (enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED
717 || enabled == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
718 cancelNotifications = false;
719 }
720 } catch (IllegalArgumentException e) {
721 // Package doesn't exist; probably racing with uninstall.
722 // cancelNotifications is already true, so nothing to do here.
723 if (DBG) {
724 Slog.i(TAG, "Exception trying to look up app enabled setting", e);
725 }
Kenny Guy70058402014-10-28 20:45:06 +0000726 } catch (RemoteException e) {
727 // Failed to talk to PackageManagerService Should never happen!
Daniel Sandler26ece572012-06-01 15:38:46 -0400728 }
729 }
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800730 pkgList = new String[]{pkgName};
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800731 }
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700732
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800733 if (pkgList != null && (pkgList.length > 0)) {
734 for (String pkgName : pkgList) {
John Spurlock79f78922013-05-16 09:10:05 -0400735 if (cancelNotifications) {
John Spurlocke6a7d932014-03-13 12:29:00 -0400736 cancelAllNotificationsInt(MY_UID, MY_PID, pkgName, 0, 0, !queryRestart,
Julia Reynoldsef37f282016-02-12 09:11:27 -0500737 changeUserId, reason, null);
John Spurlock79f78922013-05-16 09:10:05 -0400738 }
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800739 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800740 }
John Spurlockb408e8e2014-04-23 21:12:45 -0400741 mListeners.onPackagesChanged(queryReplace, pkgList);
Chris Wrene0ba7eb2016-03-04 17:30:43 -0500742 mRankerServices.onPackagesChanged(queryReplace, pkgList);
John Spurlock7340fc82014-04-24 18:50:12 -0400743 mConditionProviders.onPackagesChanged(queryReplace, pkgList);
John Spurlock35ef0a62015-05-28 11:24:10 -0400744 mRankingHelper.onPackagesChanged(queryReplace, pkgList);
Kenny Guy70058402014-10-28 20:45:06 +0000745 }
746 }
747 };
748
749 private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
750 @Override
751 public void onReceive(Context context, Intent intent) {
752 String action = intent.getAction();
753
754 if (action.equals(Intent.ACTION_SCREEN_ON)) {
Mike Lockwood63b5ad92011-08-30 09:55:30 -0400755 // Keep track of screen on/off state, but do not turn off the notification light
756 // until user passes through the lock screen or views the notification.
757 mScreenOn = true;
Christoph Studer1f32c652014-11-26 15:32:20 +0100758 updateNotificationPulse();
Mike Lockwood63b5ad92011-08-30 09:55:30 -0400759 } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
760 mScreenOn = false;
Christoph Studer1f32c652014-11-26 15:32:20 +0100761 updateNotificationPulse();
Daniel Sandlere96ffb12010-03-11 13:38:06 -0500762 } else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) {
John Spurlock5d2eeb12014-01-16 10:46:36 -0500763 mInCall = TelephonyManager.EXTRA_STATE_OFFHOOK
764 .equals(intent.getStringExtra(TelephonyManager.EXTRA_STATE));
Daniel Sandlere96ffb12010-03-11 13:38:06 -0500765 updateNotificationPulse();
Dianne Hackborn80a4af22012-08-27 19:18:31 -0700766 } else if (action.equals(Intent.ACTION_USER_STOPPED)) {
767 int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
768 if (userHandle >= 0) {
John Spurlocke6a7d932014-03-13 12:29:00 -0400769 cancelAllNotificationsInt(MY_UID, MY_PID, null, 0, 0, true, userHandle,
Julia Reynoldsef37f282016-02-12 09:11:27 -0500770 REASON_USER_STOPPED, null);
Dianne Hackborn80a4af22012-08-27 19:18:31 -0700771 }
Rubin Xue95057a2016-04-01 16:49:25 +0100772 } else if (action.equals(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)) {
Rubin Xu7eadc1b2016-02-01 16:13:45 +0000773 int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
Rubin Xue95057a2016-04-01 16:49:25 +0100774 if (userHandle >= 0) {
Rubin Xu7eadc1b2016-02-01 16:13:45 +0000775 cancelAllNotificationsInt(MY_UID, MY_PID, null, 0, 0, true, userHandle,
Julia Reynoldsef37f282016-02-12 09:11:27 -0500776 REASON_PROFILE_TURNED_OFF, null);
Rubin Xu7eadc1b2016-02-01 16:13:45 +0000777 }
Mike Lockwood63b5ad92011-08-30 09:55:30 -0400778 } else if (action.equals(Intent.ACTION_USER_PRESENT)) {
779 // turn off LED when user passes through lock screen
780 mNotificationLight.turnOff();
Wei Liu97e56662016-03-04 10:52:33 -0800781 if (mStatusBar != null) {
782 mStatusBar.notificationLightOff();
783 }
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400784 } else if (action.equals(Intent.ACTION_USER_SWITCHED)) {
John Spurlock1b8b22b2015-05-20 09:47:13 -0400785 final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400786 // reload per-user settings
787 mSettingsObserver.update(null);
John Spurlockb408e8e2014-04-23 21:12:45 -0400788 mUserProfiles.updateCache(context);
Christoph Studerb53dfd42014-09-12 14:45:59 +0200789 // Refresh managed services
John Spurlock1b8b22b2015-05-20 09:47:13 -0400790 mConditionProviders.onUserSwitched(user);
791 mListeners.onUserSwitched(user);
Chris Wrene0ba7eb2016-03-04 17:30:43 -0500792 mRankerServices.onUserSwitched(user);
John Spurlock21258a32015-05-27 18:22:55 -0400793 mZenModeHelper.onUserSwitched(user);
Kenny Guy3a7c4a52014-03-03 18:24:03 +0000794 } else if (action.equals(Intent.ACTION_USER_ADDED)) {
John Spurlockb408e8e2014-04-23 21:12:45 -0400795 mUserProfiles.updateCache(context);
John Spurlock21258a32015-05-27 18:22:55 -0400796 } else if (action.equals(Intent.ACTION_USER_REMOVED)) {
797 final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
798 mZenModeHelper.onUserRemoved(user);
Julia Reynoldsa3dcaff2016-02-03 15:04:05 -0500799 } else if (action.equals(Intent.ACTION_USER_UNLOCKED)) {
800 final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
801 mConditionProviders.onUserUnlocked(user);
802 mListeners.onUserUnlocked(user);
Chris Wrene0ba7eb2016-03-04 17:30:43 -0500803 mRankerServices.onUserUnlocked(user);
Julia Reynoldsa3dcaff2016-02-03 15:04:05 -0500804 mZenModeHelper.onUserUnlocked(user);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800805 }
806 }
807 };
808
John Spurlock7c74f782015-06-04 13:01:42 -0400809 private final class SettingsObserver extends ContentObserver {
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400810 private final Uri NOTIFICATION_LIGHT_PULSE_URI
811 = Settings.System.getUriFor(Settings.System.NOTIFICATION_LIGHT_PULSE);
812
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700813 SettingsObserver(Handler handler) {
814 super(handler);
815 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800816
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700817 void observe() {
Adam Lesinski182f73f2013-12-05 16:48:06 -0800818 ContentResolver resolver = getContext().getContentResolver();
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400819 resolver.registerContentObserver(NOTIFICATION_LIGHT_PULSE_URI,
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700820 false, this, UserHandle.USER_ALL);
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400821 update(null);
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700822 }
823
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400824 @Override public void onChange(boolean selfChange, Uri uri) {
825 update(uri);
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700826 }
827
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400828 public void update(Uri uri) {
Adam Lesinski182f73f2013-12-05 16:48:06 -0800829 ContentResolver resolver = getContext().getContentResolver();
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400830 if (uri == null || NOTIFICATION_LIGHT_PULSE_URI.equals(uri)) {
831 boolean pulseEnabled = Settings.System.getInt(resolver,
832 Settings.System.NOTIFICATION_LIGHT_PULSE, 0) != 0;
833 if (mNotificationPulseEnabled != pulseEnabled) {
834 mNotificationPulseEnabled = pulseEnabled;
835 updateNotificationPulse();
836 }
837 }
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700838 }
839 }
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500840
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400841 private SettingsObserver mSettingsObserver;
John Spurlock056c5192014-04-20 21:52:01 -0400842 private ZenModeHelper mZenModeHelper;
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400843
John Spurlockcad57682014-07-26 17:09:56 -0400844 private final Runnable mBuzzBeepBlinked = new Runnable() {
845 @Override
846 public void run() {
Wei Liu97e56662016-03-04 10:52:33 -0800847 if (mStatusBar != null) {
848 mStatusBar.buzzBeepBlinked();
849 }
John Spurlockcad57682014-07-26 17:09:56 -0400850 }
851 };
852
Daniel Sandleredbb3802012-11-13 20:49:47 -0800853 static long[] getLongArray(Resources r, int resid, int maxlen, long[] def) {
854 int[] ar = r.getIntArray(resid);
855 if (ar == null) {
856 return def;
857 }
858 final int len = ar.length > maxlen ? maxlen : ar.length;
859 long[] out = new long[len];
860 for (int i=0; i<len; i++) {
861 out[i] = ar[i];
862 }
863 return out;
864 }
865
Jeff Brownb880d882014-02-10 19:47:07 -0800866 public NotificationManagerService(Context context) {
867 super(context);
868 }
869
Chris Wren93bb8b82016-03-29 14:35:05 -0400870 @VisibleForTesting
871 void setAudioManager(AudioManager audioMananger) {
872 mAudioManager = audioMananger;
873 }
874
875 @VisibleForTesting
876 void setVibrator(Vibrator vibrator) {
877 mVibrator = vibrator;
878 }
879
880 @VisibleForTesting
881 void setSystemReady(boolean systemReady) {
882 mSystemReady = systemReady;
883 }
884
885 @VisibleForTesting
886 void setHandler(Handler handler) {
887 mHandler = handler;
888 }
889
Adam Lesinski182f73f2013-12-05 16:48:06 -0800890 @Override
891 public void onStart() {
Chris Wren54bbef42014-07-09 18:37:56 -0400892 Resources resources = getContext().getResources();
893
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800894 mAm = ActivityManagerNative.getDefault();
Adam Lesinski182f73f2013-12-05 16:48:06 -0800895 mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
896 mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
Amith Yamasanif47e51e2015-04-17 10:02:15 -0700897 mAppUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
San Mehat3ee13172010-02-04 20:54:43 -0800898
Chris Wren0efdb882016-03-01 17:17:47 -0500899 // This is the package that contains the AOSP framework update.
900 mRankerServicePackageName = getContext().getPackageManager()
901 .getServicesSystemSharedLibraryPackageName();
902
Adam Lesinski182f73f2013-12-05 16:48:06 -0800903 mHandler = new WorkerHandler();
Chris Wrenf9536642014-04-17 10:01:54 -0400904 mRankingThread.start();
Chris Wren54bbef42014-07-09 18:37:56 -0400905 String[] extractorNames;
906 try {
907 extractorNames = resources.getStringArray(R.array.config_notificationSignalExtractors);
908 } catch (Resources.NotFoundException e) {
909 extractorNames = new String[0];
910 }
Chris Wren5eab2b72015-06-16 13:56:22 -0400911 mUsageStats = new NotificationUsageStats(getContext());
Chris Wren51017d02015-12-15 15:34:46 -0500912 mRankingHandler = new RankingHandlerWorker(mRankingThread.getLooper());
Chris Wren54bbef42014-07-09 18:37:56 -0400913 mRankingHelper = new RankingHelper(getContext(),
Chris Wren51017d02015-12-15 15:34:46 -0500914 mRankingHandler,
Chris Wren5eab2b72015-06-16 13:56:22 -0400915 mUsageStats,
Chris Wren54bbef42014-07-09 18:37:56 -0400916 extractorNames);
John Spurlockb2278d62015-04-07 12:47:12 -0400917 mConditionProviders = new ConditionProviders(getContext(), mHandler, mUserProfiles);
918 mZenModeHelper = new ZenModeHelper(getContext(), mHandler.getLooper(), mConditionProviders);
John Spurlock1c923a32014-04-27 16:42:29 -0400919 mZenModeHelper.addCallback(new ZenModeHelper.Callback() {
John Spurlock056c5192014-04-20 21:52:01 -0400920 @Override
921 public void onConfigChanged() {
922 savePolicyFile();
923 }
John Spurlockd8afe3c2014-08-01 14:04:07 -0400924
925 @Override
926 void onZenModeChanged() {
John Spurlock80774932015-05-07 17:38:50 -0400927 sendRegisteredOnlyBroadcast(NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED);
Jason Monka9927322015-12-13 16:22:37 -0500928 getContext().sendBroadcastAsUser(
Jason Monk63506742015-12-16 12:06:51 -0500929 new Intent(NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED_INTERNAL)
930 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT),
Jason Monka9927322015-12-13 16:22:37 -0500931 UserHandle.ALL, android.Manifest.permission.MANAGE_NOTIFICATIONS);
John Spurlockd8afe3c2014-08-01 14:04:07 -0400932 synchronized(mNotificationList) {
Christoph Studer85a384b2014-08-27 20:16:15 +0200933 updateInterruptionFilterLocked();
John Spurlockd8afe3c2014-08-01 14:04:07 -0400934 }
935 }
John Spurlock1fc476d2015-04-14 16:05:20 -0400936
937 @Override
938 void onPolicyChanged() {
John Spurlock80774932015-05-07 17:38:50 -0400939 sendRegisteredOnlyBroadcast(NotificationManager.ACTION_NOTIFICATION_POLICY_CHANGED);
940 }
John Spurlock056c5192014-04-20 21:52:01 -0400941 });
942 final File systemDir = new File(Environment.getDataDirectory(), "system");
943 mPolicyFile = new AtomicFile(new File(systemDir, "notification_policy.xml"));
Daniel Sandler4a900ac2013-01-30 14:04:10 -0500944
Chris Wrenacf424a2016-03-15 12:48:55 -0400945 syncBlockDb();
Daniel Sandler0da673f2012-04-11 12:33:16 -0400946
Chris Wren0efdb882016-03-01 17:17:47 -0500947 // This is a MangedServices object that keeps track of the listeners.
John Spurlock7340fc82014-04-24 18:50:12 -0400948 mListeners = new NotificationListeners();
Chris Wren0efdb882016-03-01 17:17:47 -0500949
950 // This is a MangedServices object that keeps track of the ranker.
951 mRankerServices = new NotificationRankers();
952 // Find the updatable ranker and register it.
Julia Reynolds1c9bd422016-03-15 09:25:56 -0400953 mRankerServices.registerRanker();
Chris Wren0efdb882016-03-01 17:17:47 -0500954
Adam Lesinski182f73f2013-12-05 16:48:06 -0800955 mStatusBar = getLocalService(StatusBarManagerInternal.class);
Wei Liu97e56662016-03-04 10:52:33 -0800956 if (mStatusBar != null) {
957 mStatusBar.setNotificationDelegate(mNotificationDelegate);
958 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800959
Adam Lesinski182f73f2013-12-05 16:48:06 -0800960 final LightsManager lights = getLocalService(LightsManager.class);
961 mNotificationLight = lights.getLight(LightsManager.LIGHT_ID_NOTIFICATIONS);
962 mAttentionLight = lights.getLight(LightsManager.LIGHT_ID_ATTENTION);
Mike Lockwood3cb67a32009-11-27 14:25:58 -0500963
Mike Lockwood670f9322010-01-20 12:13:36 -0500964 mDefaultNotificationColor = resources.getColor(
Scott Greenwald9a05b312013-06-28 00:37:54 -0400965 R.color.config_defaultNotificationColor);
Mike Lockwood670f9322010-01-20 12:13:36 -0500966 mDefaultNotificationLedOn = resources.getInteger(
Scott Greenwald9a05b312013-06-28 00:37:54 -0400967 R.integer.config_defaultNotificationLedOn);
Mike Lockwood670f9322010-01-20 12:13:36 -0500968 mDefaultNotificationLedOff = resources.getInteger(
Scott Greenwald9a05b312013-06-28 00:37:54 -0400969 R.integer.config_defaultNotificationLedOff);
Mike Lockwood670f9322010-01-20 12:13:36 -0500970
Daniel Sandleredbb3802012-11-13 20:49:47 -0800971 mDefaultVibrationPattern = getLongArray(resources,
Scott Greenwald9a05b312013-06-28 00:37:54 -0400972 R.array.config_defaultNotificationVibePattern,
Daniel Sandleredbb3802012-11-13 20:49:47 -0800973 VIBRATE_PATTERN_MAXLEN,
974 DEFAULT_VIBRATE_PATTERN);
975
976 mFallbackVibrationPattern = getLongArray(resources,
Scott Greenwald9a05b312013-06-28 00:37:54 -0400977 R.array.config_notificationFallbackVibePattern,
Daniel Sandleredbb3802012-11-13 20:49:47 -0800978 VIBRATE_PATTERN_MAXLEN,
979 DEFAULT_VIBRATE_PATTERN);
980
Chris Wren5116a822014-06-04 15:59:50 -0400981 mUseAttentionLight = resources.getBoolean(R.bool.config_useAttentionLight);
982
Joe Onorato39f5b6a2009-07-23 12:29:19 -0400983 // Don't start allowing notifications until the setup wizard has run once.
984 // After that, including subsequent boots, init with notifications turned on.
985 // This works on the first boot because the setup wizard will toggle this
986 // flag at least once and we'll go back to 0 after that.
Adam Lesinski182f73f2013-12-05 16:48:06 -0800987 if (0 == Settings.Global.getInt(getContext().getContentResolver(),
Jeff Brownbf6f6f92012-09-25 15:03:20 -0700988 Settings.Global.DEVICE_PROVISIONED, 0)) {
John Spurlockd8afe3c2014-08-01 14:04:07 -0400989 mDisableNotificationEffects = true;
Joe Onorato39f5b6a2009-07-23 12:29:19 -0400990 }
John Spurlockb2278d62015-04-07 12:47:12 -0400991 mZenModeHelper.initZenMode();
John Spurlockf3701772015-02-12 13:29:37 -0500992 mInterruptionFilter = mZenModeHelper.getZenModeListenerInterruptionFilter();
Joe Onorato39f5b6a2009-07-23 12:29:19 -0400993
John Spurlockb408e8e2014-04-23 21:12:45 -0400994 mUserProfiles.updateCache(getContext());
John Spurlock32fe4c62014-10-02 12:16:02 -0400995 listenForCallState();
Kenny Guya263e4e2014-03-03 18:24:03 +0000996
Mike Lockwood35e16bf2010-11-30 19:53:36 -0500997 // register for various Intents
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800998 IntentFilter filter = new IntentFilter();
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500999 filter.addAction(Intent.ACTION_SCREEN_ON);
1000 filter.addAction(Intent.ACTION_SCREEN_OFF);
Daniel Sandlere96ffb12010-03-11 13:38:06 -05001001 filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
Mike Lockwood63b5ad92011-08-30 09:55:30 -04001002 filter.addAction(Intent.ACTION_USER_PRESENT);
Dianne Hackborn80a4af22012-08-27 19:18:31 -07001003 filter.addAction(Intent.ACTION_USER_STOPPED);
Daniel Sandler4b749ef2013-03-18 21:53:04 -04001004 filter.addAction(Intent.ACTION_USER_SWITCHED);
Kenny Guy3a7c4a52014-03-03 18:24:03 +00001005 filter.addAction(Intent.ACTION_USER_ADDED);
John Spurlock21258a32015-05-27 18:22:55 -04001006 filter.addAction(Intent.ACTION_USER_REMOVED);
Julia Reynoldsa3dcaff2016-02-03 15:04:05 -05001007 filter.addAction(Intent.ACTION_USER_UNLOCKED);
Rubin Xue95057a2016-04-01 16:49:25 +01001008 filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001009 getContext().registerReceiver(mIntentReceiver, filter);
Kenny Guy70058402014-10-28 20:45:06 +00001010
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08001011 IntentFilter pkgFilter = new IntentFilter();
Chris Wren3da73022013-05-10 14:41:21 -04001012 pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08001013 pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
Daniel Sandleraac0eb02011-08-06 22:51:56 -04001014 pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08001015 pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
1016 pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
1017 pkgFilter.addDataScheme("package");
Kenny Guy70058402014-10-28 20:45:06 +00001018 getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, pkgFilter, null,
1019 null);
1020
Andrei Stingaceanu0122f6512016-01-22 15:33:03 +00001021 IntentFilter suspendedPkgFilter = new IntentFilter();
1022 suspendedPkgFilter.addAction(Intent.ACTION_PACKAGES_SUSPENDED);
1023 getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL,
1024 suspendedPkgFilter, null, null);
1025
Suchi Amalapurapub56ae202010-02-04 22:51:07 -08001026 IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
Kenny Guy70058402014-10-28 20:45:06 +00001027 getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, sdFilter, null,
1028 null);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001029
Daniel Sandler4b749ef2013-03-18 21:53:04 -04001030 mSettingsObserver = new SettingsObserver(mHandler);
Scott Greenwald9a05b312013-06-28 00:37:54 -04001031
Griff Hazen9f637d12014-06-10 11:13:51 -07001032 mArchive = new Archive(resources.getInteger(
1033 R.integer.config_notificationServiceArchiveSize));
1034
Adam Lesinski182f73f2013-12-05 16:48:06 -08001035 publishBinderService(Context.NOTIFICATION_SERVICE, mService);
1036 publishLocalService(NotificationManagerInternal.class, mInternalService);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001037 }
1038
John Spurlocke7a835b2015-05-13 10:47:05 -04001039 private void sendRegisteredOnlyBroadcast(String action) {
1040 getContext().sendBroadcastAsUser(new Intent(action)
1041 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), UserHandle.ALL, null);
1042 }
1043
Daniel Sandler4a900ac2013-01-30 14:04:10 -05001044 /**
Chris Wrenacf424a2016-03-15 12:48:55 -04001045 * Make sure the XML config and the the AppOps system agree about blocks.
Daniel Sandler4a900ac2013-01-30 14:04:10 -05001046 */
Chris Wrenacf424a2016-03-15 12:48:55 -04001047 private void syncBlockDb() {
John Spurlock056c5192014-04-20 21:52:01 -04001048 loadPolicyFile();
Daniel Sandler4a900ac2013-01-30 14:04:10 -05001049
Chris Wrenacf424a2016-03-15 12:48:55 -04001050 // sync bans from ranker into app opps
1051 Map<Integer, String> packageBans = mRankingHelper.getPackageBans();
1052 for(Entry<Integer, String> ban : packageBans.entrySet()) {
1053 final int uid = ban.getKey();
1054 final String packageName = ban.getValue();
1055 setNotificationsEnabledForPackageImpl(packageName, uid, false);
1056 }
1057
1058 // sync bans from app opps into ranker
1059 packageBans.clear();
1060 for (UserInfo user : UserManager.get(getContext()).getUsers()) {
1061 final int userId = user.getUserHandle().getIdentifier();
1062 final PackageManager packageManager = getContext().getPackageManager();
1063 List<PackageInfo> packages = packageManager.getInstalledPackagesAsUser(0, userId);
1064 final int packageCount = packages.size();
1065 for (int p = 0; p < packageCount; p++) {
1066 final String packageName = packages.get(p).packageName;
1067 try {
1068 final int uid = packageManager.getPackageUidAsUser(packageName, userId);
1069 if (!checkNotificationOp(packageName, uid)) {
1070 packageBans.put(uid, packageName);
1071 }
1072 } catch (NameNotFoundException e) {
1073 // forget you
1074 }
Daniel Sandler4a900ac2013-01-30 14:04:10 -05001075 }
1076 }
Chris Wrenacf424a2016-03-15 12:48:55 -04001077 for (Entry<Integer, String> ban : packageBans.entrySet()) {
1078 mRankingHelper.setImportance(ban.getValue(), ban.getKey(), IMPORTANCE_NONE);
1079 }
1080
1081 savePolicyFile();
Daniel Sandler4a900ac2013-01-30 14:04:10 -05001082 }
1083
Adam Lesinski182f73f2013-12-05 16:48:06 -08001084 @Override
1085 public void onBootPhase(int phase) {
1086 if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
1087 // no beeping until we're basically done booting
1088 mSystemReady = true;
Jeff Sharkey098d5802012-04-26 17:30:34 -07001089
Adam Lesinski182f73f2013-12-05 16:48:06 -08001090 // Grab our optional AudioService
1091 mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
John Spurlockcdb57ae2015-02-11 19:04:11 -05001092 mAudioManagerInternal = getLocalService(AudioManagerInternal.class);
Ruben Brunkdd18a0b2015-12-04 16:16:31 -08001093 mVrManagerInternal = getLocalService(VrManagerInternal.class);
John Spurlock661f2cf2014-11-17 10:29:10 -05001094 mZenModeHelper.onSystemReady();
Adam Lesinskia6db4ab2014-03-24 12:31:45 -07001095 } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
1096 // This observer will force an update when observe is called, causing us to
1097 // bind to listener services.
1098 mSettingsObserver.observe();
John Spurlockb408e8e2014-04-23 21:12:45 -04001099 mListeners.onBootPhaseAppsCanStart();
Chris Wrene0ba7eb2016-03-04 17:30:43 -05001100 mRankerServices.onBootPhaseAppsCanStart();
John Spurlock7340fc82014-04-24 18:50:12 -04001101 mConditionProviders.onBootPhaseAppsCanStart();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001102 }
1103 }
1104
Adam Lesinski182f73f2013-12-05 16:48:06 -08001105 void setNotificationsEnabledForPackageImpl(String pkg, int uid, boolean enabled) {
1106 Slog.v(TAG, (enabled?"en":"dis") + "abling notifications for " + pkg);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001107
Adam Lesinski182f73f2013-12-05 16:48:06 -08001108 mAppOps.setMode(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg,
1109 enabled ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001110
Adam Lesinski182f73f2013-12-05 16:48:06 -08001111 // Now, cancel any outstanding notifications that are part of a just-disabled app
1112 if (ENABLE_BLOCKED_NOTIFICATIONS && !enabled) {
John Spurlocke6a7d932014-03-13 12:29:00 -04001113 cancelAllNotificationsInt(MY_UID, MY_PID, pkg, 0, 0, true, UserHandle.getUserId(uid),
Julia Reynoldsef37f282016-02-12 09:11:27 -05001114 REASON_PACKAGE_BANNED, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001115 }
1116 }
1117
John Spurlockd8afe3c2014-08-01 14:04:07 -04001118 private void updateListenerHintsLocked() {
Bryce Lee7219ada2016-04-08 10:54:23 -07001119 final int hints = calculateHints();
John Spurlockd8afe3c2014-08-01 14:04:07 -04001120 if (hints == mListenerHints) return;
Bryce Lee7219ada2016-04-08 10:54:23 -07001121 ZenLog.traceListenerHintsChanged(mListenerHints, hints, mEffectsSuppressors.size());
John Spurlockd8afe3c2014-08-01 14:04:07 -04001122 mListenerHints = hints;
1123 scheduleListenerHintsChanged(hints);
John Spurlock1fa865f2014-07-21 14:56:39 -04001124 }
1125
John Spurlockb4782522014-08-22 14:54:46 -04001126 private void updateEffectsSuppressorLocked() {
Bryce Lee7219ada2016-04-08 10:54:23 -07001127 final long updatedSuppressedEffects = calculateSuppressedEffects();
1128 if (updatedSuppressedEffects == mZenModeHelper.getSuppressedEffects()) return;
1129 final List<ComponentName> suppressors = getSuppressors();
1130 ZenLog.traceEffectsSuppressorChanged(mEffectsSuppressors, suppressors, updatedSuppressedEffects);
1131 mEffectsSuppressors = suppressors;
1132 mZenModeHelper.setSuppressedEffects(updatedSuppressedEffects);
John Spurlocke7a835b2015-05-13 10:47:05 -04001133 sendRegisteredOnlyBroadcast(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED);
John Spurlockb4782522014-08-22 14:54:46 -04001134 }
1135
Bryce Lee7219ada2016-04-08 10:54:23 -07001136 private ArrayList<ComponentName> getSuppressors() {
1137 ArrayList<ComponentName> names = new ArrayList<ComponentName>();
1138 for (int i = mListenersDisablingEffects.size() - 1; i >= 0; --i) {
1139 ArraySet<ManagedServiceInfo> serviceInfoList = mListenersDisablingEffects.valueAt(i);
1140
1141 for (ManagedServiceInfo info : serviceInfoList) {
1142 names.add(info.component);
1143 }
1144 }
1145
1146 return names;
1147 }
1148
1149 private boolean removeDisabledHints(ManagedServiceInfo info) {
1150 return removeDisabledHints(info, 0);
1151 }
1152
1153 private boolean removeDisabledHints(ManagedServiceInfo info, int hints) {
1154 boolean removed = false;
1155
1156 for (int i = mListenersDisablingEffects.size() - 1; i >= 0; --i) {
1157 final int hint = mListenersDisablingEffects.keyAt(i);
1158 final ArraySet<ManagedServiceInfo> listeners =
1159 mListenersDisablingEffects.valueAt(i);
1160
1161 if (hints == 0 || (hint & hints) == hint) {
1162 removed = removed || listeners.remove(info);
1163 }
1164 }
1165
1166 return removed;
1167 }
1168
1169 private void addDisabledHints(ManagedServiceInfo info, int hints) {
1170 if ((hints & HINT_HOST_DISABLE_EFFECTS) != 0) {
1171 addDisabledHint(info, HINT_HOST_DISABLE_EFFECTS);
1172 }
1173
1174 if ((hints & HINT_HOST_DISABLE_NOTIFICATION_EFFECTS) != 0) {
1175 addDisabledHint(info, HINT_HOST_DISABLE_NOTIFICATION_EFFECTS);
1176 }
1177
1178 if ((hints & HINT_HOST_DISABLE_CALL_EFFECTS) != 0) {
1179 addDisabledHint(info, HINT_HOST_DISABLE_CALL_EFFECTS);
1180 }
1181 }
1182
1183 private void addDisabledHint(ManagedServiceInfo info, int hint) {
1184 if (mListenersDisablingEffects.indexOfKey(hint) < 0) {
1185 mListenersDisablingEffects.put(hint, new ArraySet<ManagedServiceInfo>());
1186 }
1187
1188 ArraySet<ManagedServiceInfo> hintListeners = mListenersDisablingEffects.get(hint);
1189 hintListeners.add(info);
1190 }
1191
1192 private int calculateHints() {
1193 int hints = 0;
1194 for (int i = mListenersDisablingEffects.size() - 1; i >= 0; --i) {
1195 int hint = mListenersDisablingEffects.keyAt(i);
1196 ArraySet<ManagedServiceInfo> serviceInfoList = mListenersDisablingEffects.valueAt(i);
1197
1198 if (!serviceInfoList.isEmpty()) {
1199 hints |= hint;
1200 }
1201 }
1202
1203 return hints;
1204 }
1205
1206 private long calculateSuppressedEffects() {
1207 int hints = calculateHints();
1208 long suppressedEffects = 0;
1209
1210 if ((hints & HINT_HOST_DISABLE_EFFECTS) != 0) {
1211 suppressedEffects |= ZenModeHelper.SUPPRESSED_EFFECT_ALL;
1212 }
1213
1214 if ((hints & HINT_HOST_DISABLE_NOTIFICATION_EFFECTS) != 0) {
1215 suppressedEffects |= ZenModeHelper.SUPPRESSED_EFFECT_NOTIFICATIONS;
1216 }
1217
1218 if ((hints & HINT_HOST_DISABLE_CALL_EFFECTS) != 0) {
1219 suppressedEffects |= ZenModeHelper.SUPPRESSED_EFFECT_CALLS;
1220 }
1221
1222 return suppressedEffects;
1223 }
1224
Christoph Studer85a384b2014-08-27 20:16:15 +02001225 private void updateInterruptionFilterLocked() {
1226 int interruptionFilter = mZenModeHelper.getZenModeListenerInterruptionFilter();
1227 if (interruptionFilter == mInterruptionFilter) return;
1228 mInterruptionFilter = interruptionFilter;
1229 scheduleInterruptionFilterChanged(interruptionFilter);
1230 }
1231
Adam Lesinski182f73f2013-12-05 16:48:06 -08001232 private final IBinder mService = new INotificationManager.Stub() {
1233 // Toasts
1234 // ============================================================================
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001235
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001236 @Override
Adam Lesinski182f73f2013-12-05 16:48:06 -08001237 public void enqueueToast(String pkg, ITransientNotification callback, int duration)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001238 {
Adam Lesinski182f73f2013-12-05 16:48:06 -08001239 if (DBG) {
1240 Slog.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback
1241 + " duration=" + duration);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001242 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001243
1244 if (pkg == null || callback == null) {
1245 Slog.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback);
1246 return ;
1247 }
1248
John Spurlock7340fc82014-04-24 18:50:12 -04001249 final boolean isSystemToast = isCallerSystem() || ("android".equals(pkg));
Andrei Stingaceanu355b2322016-02-12 16:43:51 +00001250 final boolean isPackageSuspended =
1251 isPackageSuspendedForUser(pkg, Binder.getCallingUid());
Adam Lesinski182f73f2013-12-05 16:48:06 -08001252
Andrei Stingaceanu0122f6512016-01-22 15:33:03 +00001253 if (ENABLE_BLOCKED_TOASTS && (!noteNotificationOp(pkg, Binder.getCallingUid())
Andrei Stingaceanu355b2322016-02-12 16:43:51 +00001254 || isPackageSuspended)) {
Adam Lesinski182f73f2013-12-05 16:48:06 -08001255 if (!isSystemToast) {
Andrei Stingaceanu0122f6512016-01-22 15:33:03 +00001256 Slog.e(TAG, "Suppressing toast from package " + pkg
Andrei Stingaceanu355b2322016-02-12 16:43:51 +00001257 + (isPackageSuspended
Andrei Stingaceanu0122f6512016-01-22 15:33:03 +00001258 ? " due to package suspended by administrator."
1259 : " by user request."));
Adam Lesinski182f73f2013-12-05 16:48:06 -08001260 return;
1261 }
1262 }
1263
1264 synchronized (mToastQueue) {
1265 int callingPid = Binder.getCallingPid();
1266 long callingId = Binder.clearCallingIdentity();
1267 try {
1268 ToastRecord record;
1269 int index = indexOfToastLocked(pkg, callback);
1270 // If it's already in the queue, we update it in place, we don't
1271 // move it to the end of the queue.
1272 if (index >= 0) {
1273 record = mToastQueue.get(index);
1274 record.update(duration);
1275 } else {
1276 // Limit the number of toasts that any given package except the android
1277 // package can enqueue. Prevents DOS attacks and deals with leaks.
1278 if (!isSystemToast) {
1279 int count = 0;
1280 final int N = mToastQueue.size();
1281 for (int i=0; i<N; i++) {
1282 final ToastRecord r = mToastQueue.get(i);
1283 if (r.pkg.equals(pkg)) {
1284 count++;
1285 if (count >= MAX_PACKAGE_NOTIFICATIONS) {
1286 Slog.e(TAG, "Package has already posted " + count
1287 + " toasts. Not showing more. Package=" + pkg);
1288 return;
1289 }
1290 }
1291 }
1292 }
1293
1294 record = new ToastRecord(callingPid, pkg, callback, duration);
1295 mToastQueue.add(record);
1296 index = mToastQueue.size() - 1;
1297 keepProcessAliveLocked(callingPid);
1298 }
1299 // If it's at index 0, it's the current toast. It doesn't matter if it's
1300 // new or just been updated. Call back and tell it to show itself.
1301 // If the callback fails, this will remove it from the list, so don't
1302 // assume that it's valid after this.
1303 if (index == 0) {
1304 showNextToastLocked();
1305 }
1306 } finally {
1307 Binder.restoreCallingIdentity(callingId);
1308 }
1309 }
1310 }
1311
1312 @Override
1313 public void cancelToast(String pkg, ITransientNotification callback) {
1314 Slog.i(TAG, "cancelToast pkg=" + pkg + " callback=" + callback);
1315
1316 if (pkg == null || callback == null) {
1317 Slog.e(TAG, "Not cancelling notification. pkg=" + pkg + " callback=" + callback);
1318 return ;
1319 }
1320
1321 synchronized (mToastQueue) {
1322 long callingId = Binder.clearCallingIdentity();
1323 try {
1324 int index = indexOfToastLocked(pkg, callback);
1325 if (index >= 0) {
1326 cancelToastLocked(index);
1327 } else {
1328 Slog.w(TAG, "Toast already cancelled. pkg=" + pkg
1329 + " callback=" + callback);
1330 }
1331 } finally {
1332 Binder.restoreCallingIdentity(callingId);
1333 }
1334 }
1335 }
1336
1337 @Override
Christoph Studer8fd7f1e2014-04-11 17:35:05 -04001338 public void enqueueNotificationWithTag(String pkg, String opPkg, String tag, int id,
Adam Lesinski182f73f2013-12-05 16:48:06 -08001339 Notification notification, int[] idOut, int userId) throws RemoteException {
Christoph Studer8fd7f1e2014-04-11 17:35:05 -04001340 enqueueNotificationInternal(pkg, opPkg, Binder.getCallingUid(),
Adam Lesinski182f73f2013-12-05 16:48:06 -08001341 Binder.getCallingPid(), tag, id, notification, idOut, userId);
1342 }
1343
1344 @Override
1345 public void cancelNotificationWithTag(String pkg, String tag, int id, int userId) {
John Spurlock7340fc82014-04-24 18:50:12 -04001346 checkCallerIsSystemOrSameApp(pkg);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001347 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1348 Binder.getCallingUid(), userId, true, false, "cancelNotificationWithTag", pkg);
Julia Reynoldse46bb372016-03-17 11:05:58 -04001349 // Don't allow client applications to cancel foreground service notis or autobundled
1350 // summaries.
John Spurlocke6a7d932014-03-13 12:29:00 -04001351 cancelNotification(Binder.getCallingUid(), Binder.getCallingPid(), pkg, tag, id, 0,
Julia Reynoldse46bb372016-03-17 11:05:58 -04001352 (Binder.getCallingUid() == Process.SYSTEM_UID
1353 ? 0 : Notification.FLAG_FOREGROUND_SERVICE)
1354 | (Binder.getCallingUid() == Process.SYSTEM_UID
1355 ? 0 : Notification.FLAG_AUTOGROUP_SUMMARY), false, userId,
Chris Wren9fa689f2015-11-20 16:44:53 -05001356 REASON_APP_CANCEL, null);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001357 }
1358
1359 @Override
1360 public void cancelAllNotifications(String pkg, int userId) {
John Spurlock7340fc82014-04-24 18:50:12 -04001361 checkCallerIsSystemOrSameApp(pkg);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001362
1363 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1364 Binder.getCallingUid(), userId, true, false, "cancelAllNotifications", pkg);
1365
1366 // Calling from user space, don't allow the canceling of actively
1367 // running foreground services.
John Spurlocke6a7d932014-03-13 12:29:00 -04001368 cancelAllNotificationsInt(Binder.getCallingUid(), Binder.getCallingPid(),
1369 pkg, 0, Notification.FLAG_FOREGROUND_SERVICE, true, userId,
Julia Reynoldsef37f282016-02-12 09:11:27 -05001370 REASON_APP_CANCEL_ALL, null);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001371 }
1372
1373 @Override
1374 public void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) {
John Spurlock7340fc82014-04-24 18:50:12 -04001375 checkCallerIsSystem();
Adam Lesinski182f73f2013-12-05 16:48:06 -08001376
1377 setNotificationsEnabledForPackageImpl(pkg, uid, enabled);
Chris Wrenacf424a2016-03-15 12:48:55 -04001378 mRankingHelper.setEnabled(pkg, uid, enabled);
1379 savePolicyFile();
Adam Lesinski182f73f2013-12-05 16:48:06 -08001380 }
1381
1382 /**
1383 * Use this when you just want to know if notifications are OK for this package.
1384 */
1385 @Override
Julia Reynolds81afbcd2016-02-09 14:54:08 -05001386 public boolean areNotificationsEnabled(String pkg) {
1387 return areNotificationsEnabledForPackage(pkg, Binder.getCallingUid());
1388 }
1389
1390 /**
1391 * Use this when you just want to know if notifications are OK for this package.
1392 */
1393 @Override
Adam Lesinski182f73f2013-12-05 16:48:06 -08001394 public boolean areNotificationsEnabledForPackage(String pkg, int uid) {
Julia Reynolds81afbcd2016-02-09 14:54:08 -05001395 checkCallerIsSystemOrSameApp(pkg);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001396 return (mAppOps.checkOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
Andrei Stingaceanu355b2322016-02-12 16:43:51 +00001397 == AppOpsManager.MODE_ALLOWED) && !isPackageSuspendedForUser(pkg, uid);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001398 }
1399
Chris Wren54bbef42014-07-09 18:37:56 -04001400 @Override
Julia Reynoldsef37f282016-02-12 09:11:27 -05001401 public void setPriority(String pkg, int uid, int priority) {
Julia Reynoldsdd3e86b2016-02-02 10:24:30 -05001402 checkCallerIsSystem();
Julia Reynoldsef37f282016-02-12 09:11:27 -05001403 mRankingHelper.setPriority(pkg, uid, priority);
Chris Wren54bbef42014-07-09 18:37:56 -04001404 savePolicyFile();
1405 }
1406
1407 @Override
Julia Reynoldsef37f282016-02-12 09:11:27 -05001408 public int getPriority(String pkg, int uid) {
Chris Wren54bbef42014-07-09 18:37:56 -04001409 checkCallerIsSystem();
Julia Reynoldsef37f282016-02-12 09:11:27 -05001410 return mRankingHelper.getPriority(pkg, uid);
Chris Wren54bbef42014-07-09 18:37:56 -04001411 }
1412
Chris Wren3ad4e3a2014-09-02 17:23:51 -04001413 @Override
Julia Reynoldsef37f282016-02-12 09:11:27 -05001414 public void setVisibilityOverride(String pkg, int uid, int visibility) {
Chris Wren3ad4e3a2014-09-02 17:23:51 -04001415 checkCallerIsSystem();
Julia Reynoldsef37f282016-02-12 09:11:27 -05001416 mRankingHelper.setVisibilityOverride(pkg, uid, visibility);
Chris Wren3ad4e3a2014-09-02 17:23:51 -04001417 savePolicyFile();
1418 }
1419
1420 @Override
Julia Reynoldsef37f282016-02-12 09:11:27 -05001421 public int getVisibilityOverride(String pkg, int uid) {
Chris Wren3ad4e3a2014-09-02 17:23:51 -04001422 checkCallerIsSystem();
Julia Reynoldsef37f282016-02-12 09:11:27 -05001423 return mRankingHelper.getVisibilityOverride(pkg, uid);
Chris Wren3ad4e3a2014-09-02 17:23:51 -04001424 }
1425
Julia Reynolds5d25ee72015-11-20 15:38:20 -05001426 @Override
Julia Reynolds0edb50c2016-02-26 14:08:25 -05001427 public void setImportance(String pkg, int uid, int importance) {
Julia Reynoldsead00aa2015-12-07 08:23:48 -05001428 enforceSystemOrSystemUI("Caller not system or systemui");
Julia Reynoldsef37f282016-02-12 09:11:27 -05001429 setNotificationsEnabledForPackageImpl(pkg, uid,
1430 importance != NotificationListenerService.Ranking.IMPORTANCE_NONE);
1431 mRankingHelper.setImportance(pkg, uid, importance);
Julia Reynolds5d25ee72015-11-20 15:38:20 -05001432 savePolicyFile();
1433 }
1434
1435 @Override
Julia Reynoldsef37f282016-02-12 09:11:27 -05001436 public int getPackageImportance(String pkg) {
Julia Reynolds81afbcd2016-02-09 14:54:08 -05001437 checkCallerIsSystemOrSameApp(pkg);
Julia Reynoldsef37f282016-02-12 09:11:27 -05001438 return mRankingHelper.getImportance(pkg, Binder.getCallingUid());
Julia Reynolds81afbcd2016-02-09 14:54:08 -05001439 }
1440
1441 @Override
Julia Reynoldsef37f282016-02-12 09:11:27 -05001442 public int getImportance(String pkg, int uid) {
Julia Reynoldscac88622016-03-03 09:28:19 -05001443 enforceSystemOrSystemUI("Caller not system or systemui");
Julia Reynoldsef37f282016-02-12 09:11:27 -05001444 return mRankingHelper.getImportance(pkg, uid);
Julia Reynoldsbe8fdee2015-12-18 09:04:34 -05001445 }
1446
Adam Lesinski182f73f2013-12-05 16:48:06 -08001447 /**
1448 * System-only API for getting a list of current (i.e. not cleared) notifications.
1449 *
1450 * Requires ACCESS_NOTIFICATIONS which is signature|system.
Chris Wrenf9536642014-04-17 10:01:54 -04001451 * @returns A list of all the notifications, in natural order.
Adam Lesinski182f73f2013-12-05 16:48:06 -08001452 */
1453 @Override
1454 public StatusBarNotification[] getActiveNotifications(String callingPkg) {
1455 // enforce() will ensure the calling uid has the correct permission
1456 getContext().enforceCallingOrSelfPermission(
1457 android.Manifest.permission.ACCESS_NOTIFICATIONS,
1458 "NotificationManagerService.getActiveNotifications");
1459
1460 StatusBarNotification[] tmp = null;
1461 int uid = Binder.getCallingUid();
1462
1463 // noteOp will check to make sure the callingPkg matches the uid
1464 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
1465 == AppOpsManager.MODE_ALLOWED) {
1466 synchronized (mNotificationList) {
1467 tmp = new StatusBarNotification[mNotificationList.size()];
1468 final int N = mNotificationList.size();
1469 for (int i=0; i<N; i++) {
1470 tmp[i] = mNotificationList.get(i).sbn;
1471 }
1472 }
1473 }
1474 return tmp;
1475 }
1476
1477 /**
Dan Sandler994349c2015-04-15 11:02:54 -04001478 * Public API for getting a list of current notifications for the calling package/uid.
1479 *
1480 * @returns A list of all the package's notifications, in natural order.
1481 */
1482 @Override
1483 public ParceledListSlice<StatusBarNotification> getAppActiveNotifications(String pkg,
1484 int incomingUserId) {
1485 checkCallerIsSystemOrSameApp(pkg);
1486 int userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1487 Binder.getCallingUid(), incomingUserId, true, false,
1488 "getAppActiveNotifications", pkg);
1489
Erik Wolsheimer2242b4d2015-11-24 13:22:04 -08001490 final ArrayList<StatusBarNotification> list
1491 = new ArrayList<StatusBarNotification>(mNotificationList.size());
Dan Sandler994349c2015-04-15 11:02:54 -04001492
1493 synchronized (mNotificationList) {
Erik Wolsheimer2242b4d2015-11-24 13:22:04 -08001494 final int N = mNotificationList.size();
Dan Sandler994349c2015-04-15 11:02:54 -04001495 for (int i = 0; i < N; i++) {
1496 final StatusBarNotification sbn = mNotificationList.get(i).sbn;
Julia Reynoldse46bb372016-03-17 11:05:58 -04001497 if (sbn.getPackageName().equals(pkg) && sbn.getUserId() == userId
1498 && (sbn.getNotification().flags
Julia Reynolds4c4ad592016-04-12 11:16:37 -04001499 & Notification.FLAG_AUTOGROUP_SUMMARY) == 0) {
Dan Sandler994349c2015-04-15 11:02:54 -04001500 // We could pass back a cloneLight() but clients might get confused and
1501 // try to send this thing back to notify() again, which would not work
1502 // very well.
1503 final StatusBarNotification sbnOut = new StatusBarNotification(
1504 sbn.getPackageName(),
1505 sbn.getOpPkg(),
1506 sbn.getId(), sbn.getTag(), sbn.getUid(), sbn.getInitialPid(),
1507 0, // hide score from apps
1508 sbn.getNotification().clone(),
1509 sbn.getUser(), sbn.getPostTime());
1510 list.add(sbnOut);
1511 }
1512 }
1513 }
1514
1515 return new ParceledListSlice<StatusBarNotification>(list);
1516 }
1517
1518 /**
Adam Lesinski182f73f2013-12-05 16:48:06 -08001519 * System-only API for getting a list of recent (cleared, no longer shown) notifications.
1520 *
1521 * Requires ACCESS_NOTIFICATIONS which is signature|system.
1522 */
1523 @Override
1524 public StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count) {
1525 // enforce() will ensure the calling uid has the correct permission
1526 getContext().enforceCallingOrSelfPermission(
1527 android.Manifest.permission.ACCESS_NOTIFICATIONS,
1528 "NotificationManagerService.getHistoricalNotifications");
1529
1530 StatusBarNotification[] tmp = null;
1531 int uid = Binder.getCallingUid();
1532
1533 // noteOp will check to make sure the callingPkg matches the uid
1534 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
1535 == AppOpsManager.MODE_ALLOWED) {
1536 synchronized (mArchive) {
1537 tmp = mArchive.getArray(count);
1538 }
1539 }
1540 return tmp;
1541 }
1542
1543 /**
1544 * Register a listener binder directly with the notification manager.
1545 *
1546 * Only works with system callers. Apps should extend
1547 * {@link android.service.notification.NotificationListenerService}.
1548 */
1549 @Override
1550 public void registerListener(final INotificationListener listener,
Chris Wren0efdb882016-03-01 17:17:47 -05001551 final ComponentName component, final int userid) {
Christoph Studer3e144d32014-05-22 16:48:40 +02001552 enforceSystemOrSystemUI("INotificationManager.registerListener");
Chris Wren0efdb882016-03-01 17:17:47 -05001553 mListeners.registerService(listener, component, userid);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001554 }
1555
1556 /**
1557 * Remove a listener binder directly
1558 */
1559 @Override
Chris Wrene0ba7eb2016-03-04 17:30:43 -05001560 public void unregisterListener(INotificationListener token, int userid) {
Chris Wrenb7c81092016-03-10 11:41:10 -05001561 mListeners.unregisterService(token, userid);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001562 }
1563
1564 /**
1565 * Allow an INotificationListener to simulate a "clear all" operation.
1566 *
1567 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onClearAllNotifications}
1568 *
1569 * @param token The binder for the listener, to check that the caller is allowed
1570 */
1571 @Override
John Spurlocka4294292014-03-24 18:02:32 -04001572 public void cancelNotificationsFromListener(INotificationListener token, String[] keys) {
John Spurlocke6a7d932014-03-13 12:29:00 -04001573 final int callingUid = Binder.getCallingUid();
1574 final int callingPid = Binder.getCallingPid();
Adam Lesinski182f73f2013-12-05 16:48:06 -08001575 long identity = Binder.clearCallingIdentity();
1576 try {
Adam Lesinskie8240262014-03-26 16:01:00 -07001577 synchronized (mNotificationList) {
John Spurlock7340fc82014-04-24 18:50:12 -04001578 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
John Spurlocka4294292014-03-24 18:02:32 -04001579 if (keys != null) {
1580 final int N = keys.length;
1581 for (int i = 0; i < N; i++) {
1582 NotificationRecord r = mNotificationsByKey.get(keys[i]);
Griff Hazen335e1f02014-09-11 14:49:31 -07001583 if (r == null) continue;
Kenny Guya263e4e2014-03-03 18:24:03 +00001584 final int userId = r.sbn.getUserId();
1585 if (userId != info.userid && userId != UserHandle.USER_ALL &&
John Spurlockb408e8e2014-04-23 21:12:45 -04001586 !mUserProfiles.isCurrentProfile(userId)) {
Kenny Guya263e4e2014-03-03 18:24:03 +00001587 throw new SecurityException("Disallowed call from listener: "
John Spurlock7340fc82014-04-24 18:50:12 -04001588 + info.service);
Kenny Guya263e4e2014-03-03 18:24:03 +00001589 }
Griff Hazen335e1f02014-09-11 14:49:31 -07001590 cancelNotificationFromListenerLocked(info, callingUid, callingPid,
1591 r.sbn.getPackageName(), r.sbn.getTag(), r.sbn.getId(),
1592 userId);
John Spurlocka4294292014-03-24 18:02:32 -04001593 }
1594 } else {
1595 cancelAllLocked(callingUid, callingPid, info.userid,
Kenny Guya263e4e2014-03-03 18:24:03 +00001596 REASON_LISTENER_CANCEL_ALL, info, info.supportsProfiles());
John Spurlocka4294292014-03-24 18:02:32 -04001597 }
Adam Lesinskie8240262014-03-26 16:01:00 -07001598 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001599 } finally {
1600 Binder.restoreCallingIdentity(identity);
1601 }
1602 }
1603
Chris Wrenab41eec2016-01-04 18:01:27 -05001604 /**
1605 * Handle request from an approved listener to re-enable itself.
1606 *
1607 * @param component The componenet to be re-enabled, caller must match package.
1608 */
1609 @Override
1610 public void requestBindListener(ComponentName component) {
1611 checkCallerIsSystemOrSameApp(component.getPackageName());
1612 long identity = Binder.clearCallingIdentity();
1613 try {
Julia Reynoldse46bb372016-03-17 11:05:58 -04001614 ManagedServices manager =
1615 mRankerServices.isComponentEnabledForCurrentProfiles(component)
Chris Wrene0ba7eb2016-03-04 17:30:43 -05001616 ? mRankerServices
Chris Wrenab41eec2016-01-04 18:01:27 -05001617 : mListeners;
1618 manager.setComponentState(component, true);
1619 } finally {
1620 Binder.restoreCallingIdentity(identity);
1621 }
1622 }
1623
1624 @Override
1625 public void requestUnbindListener(INotificationListener token) {
1626 long identity = Binder.clearCallingIdentity();
1627 try {
1628 // allow bound services to disable themselves
1629 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1630 info.getOwner().setComponentState(info.component, false);
1631 } finally {
1632 Binder.restoreCallingIdentity(identity);
1633 }
1634 }
1635
Amith Yamasanif47e51e2015-04-17 10:02:15 -07001636 @Override
1637 public void setNotificationsShownFromListener(INotificationListener token, String[] keys) {
Amith Yamasanif47e51e2015-04-17 10:02:15 -07001638 long identity = Binder.clearCallingIdentity();
1639 try {
1640 synchronized (mNotificationList) {
1641 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1642 if (keys != null) {
1643 final int N = keys.length;
1644 for (int i = 0; i < N; i++) {
1645 NotificationRecord r = mNotificationsByKey.get(keys[i]);
1646 if (r == null) continue;
1647 final int userId = r.sbn.getUserId();
1648 if (userId != info.userid && userId != UserHandle.USER_ALL &&
1649 !mUserProfiles.isCurrentProfile(userId)) {
1650 throw new SecurityException("Disallowed call from listener: "
1651 + info.service);
1652 }
1653 if (!r.isSeen()) {
1654 if (DBG) Slog.d(TAG, "Marking notification as seen " + keys[i]);
1655 mAppUsageStats.reportEvent(r.sbn.getPackageName(),
Xiaohui Chenddbe4ca2015-08-13 16:20:56 -07001656 userId == UserHandle.USER_ALL ? UserHandle.USER_SYSTEM
Amith Yamasanif47e51e2015-04-17 10:02:15 -07001657 : userId,
Adam Lesinskic8e87292015-06-10 15:33:45 -07001658 UsageEvents.Event.USER_INTERACTION);
Amith Yamasanif47e51e2015-04-17 10:02:15 -07001659 r.setSeen();
1660 }
1661 }
1662 }
1663 }
1664 } finally {
1665 Binder.restoreCallingIdentity(identity);
1666 }
1667 }
1668
John Spurlock7340fc82014-04-24 18:50:12 -04001669 private void cancelNotificationFromListenerLocked(ManagedServiceInfo info,
Kenny Guya263e4e2014-03-03 18:24:03 +00001670 int callingUid, int callingPid, String pkg, String tag, int id, int userId) {
John Spurlocka4294292014-03-24 18:02:32 -04001671 cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
1672 Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
1673 true,
Kenny Guya263e4e2014-03-03 18:24:03 +00001674 userId, REASON_LISTENER_CANCEL, info);
John Spurlocka4294292014-03-24 18:02:32 -04001675 }
1676
Adam Lesinski182f73f2013-12-05 16:48:06 -08001677 /**
1678 * Allow an INotificationListener to simulate clearing (dismissing) a single notification.
1679 *
1680 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onNotificationClear}
1681 *
1682 * @param token The binder for the listener, to check that the caller is allowed
1683 */
1684 @Override
1685 public void cancelNotificationFromListener(INotificationListener token, String pkg,
1686 String tag, int id) {
John Spurlocke6a7d932014-03-13 12:29:00 -04001687 final int callingUid = Binder.getCallingUid();
1688 final int callingPid = Binder.getCallingPid();
Adam Lesinski182f73f2013-12-05 16:48:06 -08001689 long identity = Binder.clearCallingIdentity();
1690 try {
Adam Lesinskie8240262014-03-26 16:01:00 -07001691 synchronized (mNotificationList) {
John Spurlock7340fc82014-04-24 18:50:12 -04001692 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
Kenny Guya263e4e2014-03-03 18:24:03 +00001693 if (info.supportsProfiles()) {
1694 Log.e(TAG, "Ignoring deprecated cancelNotification(pkg, tag, id) "
1695 + "from " + info.component
1696 + " use cancelNotification(key) instead.");
1697 } else {
1698 cancelNotificationFromListenerLocked(info, callingUid, callingPid,
1699 pkg, tag, id, info.userid);
1700 }
Adam Lesinskie8240262014-03-26 16:01:00 -07001701 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001702 } finally {
1703 Binder.restoreCallingIdentity(identity);
1704 }
1705 }
1706
1707 /**
1708 * Allow an INotificationListener to request the list of outstanding notifications seen by
1709 * the current user. Useful when starting up, after which point the listener callbacks
1710 * should be used.
1711 *
1712 * @param token The binder for the listener, to check that the caller is allowed
Dan Sandlerea75fdd2014-08-12 12:29:19 -04001713 * @param keys An array of notification keys to fetch, or null to fetch everything
Chris Wrenf9536642014-04-17 10:01:54 -04001714 * @returns The return value will contain the notifications specified in keys, in that
1715 * order, or if keys is null, all the notifications, in natural order.
Adam Lesinski182f73f2013-12-05 16:48:06 -08001716 */
1717 @Override
Christoph Studercee44ba2014-05-20 18:36:43 +02001718 public ParceledListSlice<StatusBarNotification> getActiveNotificationsFromListener(
Christoph Studerb82bc782014-08-20 14:29:43 +02001719 INotificationListener token, String[] keys, int trim) {
Adam Lesinski182f73f2013-12-05 16:48:06 -08001720 synchronized (mNotificationList) {
John Spurlock7340fc82014-04-24 18:50:12 -04001721 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
Dan Sandlerea75fdd2014-08-12 12:29:19 -04001722 final boolean getKeys = keys != null;
1723 final int N = getKeys ? keys.length : mNotificationList.size();
Christoph Studerb82bc782014-08-20 14:29:43 +02001724 final ArrayList<StatusBarNotification> list
1725 = new ArrayList<StatusBarNotification>(N);
Christoph Studercee44ba2014-05-20 18:36:43 +02001726 for (int i=0; i<N; i++) {
Dan Sandlerea75fdd2014-08-12 12:29:19 -04001727 final NotificationRecord r = getKeys
1728 ? mNotificationsByKey.get(keys[i])
1729 : mNotificationList.get(i);
Christoph Studerb82bc782014-08-20 14:29:43 +02001730 if (r == null) continue;
1731 StatusBarNotification sbn = r.sbn;
1732 if (!isVisibleToListener(sbn, info)) continue;
1733 StatusBarNotification sbnToSend =
1734 (trim == TRIM_FULL) ? sbn : sbn.cloneLight();
1735 list.add(sbnToSend);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001736 }
Christoph Studercee44ba2014-05-20 18:36:43 +02001737 return new ParceledListSlice<StatusBarNotification>(list);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001738 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001739 }
1740
1741 @Override
John Spurlockd8afe3c2014-08-01 14:04:07 -04001742 public void requestHintsFromListener(INotificationListener token, int hints) {
1743 final long identity = Binder.clearCallingIdentity();
1744 try {
1745 synchronized (mNotificationList) {
1746 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
Bryce Lee7219ada2016-04-08 10:54:23 -07001747 final int disableEffectsMask = HINT_HOST_DISABLE_EFFECTS
1748 | HINT_HOST_DISABLE_NOTIFICATION_EFFECTS
1749 | HINT_HOST_DISABLE_CALL_EFFECTS;
1750 final boolean disableEffects = (hints & disableEffectsMask) != 0;
John Spurlockd8afe3c2014-08-01 14:04:07 -04001751 if (disableEffects) {
Bryce Lee7219ada2016-04-08 10:54:23 -07001752 addDisabledHints(info, hints);
John Spurlockd8afe3c2014-08-01 14:04:07 -04001753 } else {
Bryce Lee7219ada2016-04-08 10:54:23 -07001754 removeDisabledHints(info, hints);
John Spurlockd8afe3c2014-08-01 14:04:07 -04001755 }
John Spurlockd8afe3c2014-08-01 14:04:07 -04001756 updateListenerHintsLocked();
John Spurlockb4782522014-08-22 14:54:46 -04001757 updateEffectsSuppressorLocked();
John Spurlock1fa865f2014-07-21 14:56:39 -04001758 }
John Spurlockd8afe3c2014-08-01 14:04:07 -04001759 } finally {
1760 Binder.restoreCallingIdentity(identity);
John Spurlock1fa865f2014-07-21 14:56:39 -04001761 }
1762 }
1763
1764 @Override
John Spurlockd8afe3c2014-08-01 14:04:07 -04001765 public int getHintsFromListener(INotificationListener token) {
John Spurlock1fa865f2014-07-21 14:56:39 -04001766 synchronized (mNotificationList) {
John Spurlockd8afe3c2014-08-01 14:04:07 -04001767 return mListenerHints;
John Spurlock1fa865f2014-07-21 14:56:39 -04001768 }
1769 }
1770
1771 @Override
Christoph Studer85a384b2014-08-27 20:16:15 +02001772 public void requestInterruptionFilterFromListener(INotificationListener token,
1773 int interruptionFilter) throws RemoteException {
1774 final long identity = Binder.clearCallingIdentity();
1775 try {
1776 synchronized (mNotificationList) {
John Spurlock661f2cf2014-11-17 10:29:10 -05001777 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1778 mZenModeHelper.requestFromListener(info.component, interruptionFilter);
Christoph Studer85a384b2014-08-27 20:16:15 +02001779 updateInterruptionFilterLocked();
1780 }
1781 } finally {
1782 Binder.restoreCallingIdentity(identity);
1783 }
1784 }
1785
1786 @Override
1787 public int getInterruptionFilterFromListener(INotificationListener token)
1788 throws RemoteException {
1789 synchronized (mNotificationLight) {
1790 return mInterruptionFilter;
1791 }
1792 }
1793
1794 @Override
Christoph Studerb82bc782014-08-20 14:29:43 +02001795 public void setOnNotificationPostedTrimFromListener(INotificationListener token, int trim)
1796 throws RemoteException {
1797 synchronized (mNotificationList) {
1798 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1799 if (info == null) return;
1800 mListeners.setOnNotificationPostedTrimLocked(info, trim);
1801 }
1802 }
1803
1804 @Override
John Spurlockb2278d62015-04-07 12:47:12 -04001805 public int getZenMode() {
1806 return mZenModeHelper.getZenMode();
1807 }
1808
1809 @Override
John Spurlock056c5192014-04-20 21:52:01 -04001810 public ZenModeConfig getZenModeConfig() {
John Spurlockcdb57ae2015-02-11 19:04:11 -05001811 enforceSystemOrSystemUIOrVolume("INotificationManager.getZenModeConfig");
John Spurlock056c5192014-04-20 21:52:01 -04001812 return mZenModeHelper.getConfig();
1813 }
1814
1815 @Override
John Spurlockb2278d62015-04-07 12:47:12 -04001816 public void setZenMode(int mode, Uri conditionId, String reason) throws RemoteException {
John Spurlockcdb57ae2015-02-11 19:04:11 -05001817 enforceSystemOrSystemUIOrVolume("INotificationManager.setZenMode");
1818 final long identity = Binder.clearCallingIdentity();
1819 try {
John Spurlockb2278d62015-04-07 12:47:12 -04001820 mZenModeHelper.setManualZenMode(mode, conditionId, reason);
John Spurlockcdb57ae2015-02-11 19:04:11 -05001821 } finally {
1822 Binder.restoreCallingIdentity(identity);
1823 }
1824 }
1825
1826 @Override
Julia Reynolds361e82d32016-02-26 18:19:49 -05001827 public List<ZenModeConfig.ZenRule> getZenRules() throws RemoteException {
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04001828 enforcePolicyAccess(Binder.getCallingUid(), "getAutomaticZenRules");
Julia Reynolds361e82d32016-02-26 18:19:49 -05001829 return mZenModeHelper.getZenRules();
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04001830 }
1831
1832 @Override
Julia Reynolds4fe98d62015-10-06 16:23:41 -04001833 public AutomaticZenRule getAutomaticZenRule(String id) throws RemoteException {
1834 Preconditions.checkNotNull(id, "Id is null");
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04001835 enforcePolicyAccess(Binder.getCallingUid(), "getAutomaticZenRule");
Julia Reynolds4fe98d62015-10-06 16:23:41 -04001836 return mZenModeHelper.getAutomaticZenRule(id);
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04001837 }
1838
1839 @Override
Julia Reynolds361e82d32016-02-26 18:19:49 -05001840 public String addAutomaticZenRule(AutomaticZenRule automaticZenRule)
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04001841 throws RemoteException {
1842 Preconditions.checkNotNull(automaticZenRule, "automaticZenRule is null");
1843 Preconditions.checkNotNull(automaticZenRule.getName(), "Name is null");
1844 Preconditions.checkNotNull(automaticZenRule.getOwner(), "Owner is null");
1845 Preconditions.checkNotNull(automaticZenRule.getConditionId(), "ConditionId is null");
Julia Reynolds4fe98d62015-10-06 16:23:41 -04001846 enforcePolicyAccess(Binder.getCallingUid(), "addAutomaticZenRule");
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04001847
Julia Reynolds4fe98d62015-10-06 16:23:41 -04001848 return mZenModeHelper.addAutomaticZenRule(automaticZenRule,
1849 "addAutomaticZenRule");
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04001850 }
1851
1852 @Override
Julia Reynolds361e82d32016-02-26 18:19:49 -05001853 public boolean updateAutomaticZenRule(String id, AutomaticZenRule automaticZenRule)
Julia Reynolds4fe98d62015-10-06 16:23:41 -04001854 throws RemoteException {
1855 Preconditions.checkNotNull(automaticZenRule, "automaticZenRule is null");
1856 Preconditions.checkNotNull(automaticZenRule.getName(), "Name is null");
1857 Preconditions.checkNotNull(automaticZenRule.getOwner(), "Owner is null");
1858 Preconditions.checkNotNull(automaticZenRule.getConditionId(), "ConditionId is null");
1859 enforcePolicyAccess(Binder.getCallingUid(), "updateAutomaticZenRule");
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04001860
Julia Reynolds361e82d32016-02-26 18:19:49 -05001861 return mZenModeHelper.updateAutomaticZenRule(id, automaticZenRule,
Julia Reynolds4fe98d62015-10-06 16:23:41 -04001862 "updateAutomaticZenRule");
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04001863 }
1864
1865 @Override
Julia Reynolds4fe98d62015-10-06 16:23:41 -04001866 public boolean removeAutomaticZenRule(String id) throws RemoteException {
1867 Preconditions.checkNotNull(id, "Id is null");
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04001868 // Verify that they can modify zen rules.
1869 enforcePolicyAccess(Binder.getCallingUid(), "removeAutomaticZenRule");
1870
Julia Reynolds4fe98d62015-10-06 16:23:41 -04001871 return mZenModeHelper.removeAutomaticZenRule(id, "removeAutomaticZenRule");
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04001872 }
1873
1874 @Override
Julia Reynoldsc8e54e82015-11-30 16:43:05 -05001875 public boolean removeAutomaticZenRules(String packageName) throws RemoteException {
1876 Preconditions.checkNotNull(packageName, "Package name is null");
1877 enforceSystemOrSystemUI("removeAutomaticZenRules");
1878
1879 return mZenModeHelper.removeAutomaticZenRules(packageName, "removeAutomaticZenRules");
1880 }
1881
1882 @Override
Julia Reynolds43b70cd2016-01-14 15:05:34 -05001883 public int getRuleInstanceCount(ComponentName owner) throws RemoteException {
1884 Preconditions.checkNotNull(owner, "Owner is null");
1885 enforceSystemOrSystemUI("getRuleInstanceCount");
1886
1887 return mZenModeHelper.getCurrentInstanceCount(owner);
1888 }
1889
1890 @Override
John Spurlock80774932015-05-07 17:38:50 -04001891 public void setInterruptionFilter(String pkg, int filter) throws RemoteException {
1892 enforcePolicyAccess(pkg, "setInterruptionFilter");
1893 final int zen = NotificationManager.zenModeFromInterruptionFilter(filter, -1);
1894 if (zen == -1) throw new IllegalArgumentException("Invalid filter: " + filter);
1895 final long identity = Binder.clearCallingIdentity();
1896 try {
1897 mZenModeHelper.setManualZenMode(zen, null, "setInterruptionFilter");
1898 } finally {
1899 Binder.restoreCallingIdentity(identity);
1900 }
1901 }
1902
1903 @Override
John Spurlocka7d92b12015-05-13 14:48:02 -04001904 public void notifyConditions(final String pkg, IConditionProvider provider,
1905 final Condition[] conditions) {
John Spurlocke77bb362014-04-26 10:24:59 -04001906 final ManagedServiceInfo info = mConditionProviders.checkServiceToken(provider);
1907 checkCallerIsSystemOrSameApp(pkg);
John Spurlocka7d92b12015-05-13 14:48:02 -04001908 mHandler.post(new Runnable() {
1909 @Override
1910 public void run() {
1911 mConditionProviders.notifyConditions(pkg, info, conditions);
1912 }
1913 });
John Spurlocke77bb362014-04-26 10:24:59 -04001914 }
1915
John Spurlockcdb57ae2015-02-11 19:04:11 -05001916 private void enforceSystemOrSystemUIOrVolume(String message) {
1917 if (mAudioManagerInternal != null) {
1918 final int vcuid = mAudioManagerInternal.getVolumeControllerUid();
1919 if (vcuid > 0 && Binder.getCallingUid() == vcuid) {
1920 return;
1921 }
1922 }
1923 enforceSystemOrSystemUI(message);
1924 }
1925
John Spurlocke77bb362014-04-26 10:24:59 -04001926 private void enforceSystemOrSystemUI(String message) {
1927 if (isCallerSystem()) return;
1928 getContext().enforceCallingPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
1929 message);
John Spurlock7340fc82014-04-24 18:50:12 -04001930 }
1931
Julia Reynolds48034f82016-03-09 10:15:16 -05001932 private void enforceSystemOrSystemUIOrSamePackage(String pkg, String message) {
1933 try {
1934 checkCallerIsSystemOrSameApp(pkg);
1935 } catch (SecurityException e) {
1936 getContext().enforceCallingPermission(
1937 android.Manifest.permission.STATUS_BAR_SERVICE,
1938 message);
1939 }
1940 }
1941
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04001942 private void enforcePolicyAccess(int uid, String method) {
1943 if (PackageManager.PERMISSION_GRANTED == getContext().checkCallingPermission(
1944 android.Manifest.permission.MANAGE_NOTIFICATIONS)) {
1945 return;
1946 }
1947 boolean accessAllowed = false;
1948 String[] packages = getContext().getPackageManager().getPackagesForUid(uid);
1949 final int packageCount = packages.length;
1950 for (int i = 0; i < packageCount; i++) {
1951 if (checkPolicyAccess(packages[i])) {
1952 accessAllowed = true;
1953 }
1954 }
1955 if (!accessAllowed) {
1956 Slog.w(TAG, "Notification policy access denied calling " + method);
1957 throw new SecurityException("Notification policy access denied");
1958 }
1959 }
1960
John Spurlock80774932015-05-07 17:38:50 -04001961 private void enforcePolicyAccess(String pkg, String method) {
Julia Reynolds6ee26172015-09-28 11:34:48 -04001962 if (PackageManager.PERMISSION_GRANTED == getContext().checkCallingPermission(
1963 android.Manifest.permission.MANAGE_NOTIFICATIONS)) {
1964 return;
1965 }
John Spurlock80774932015-05-07 17:38:50 -04001966 if (!checkPolicyAccess(pkg)) {
1967 Slog.w(TAG, "Notification policy access denied calling " + method);
1968 throw new SecurityException("Notification policy access denied");
John Spurlock1fc476d2015-04-14 16:05:20 -04001969 }
1970 }
1971
John Spurlock80774932015-05-07 17:38:50 -04001972 private boolean checkPackagePolicyAccess(String pkg) {
John Spurlock7c74f782015-06-04 13:01:42 -04001973 return mPolicyAccess.isPackageGranted(pkg);
John Spurlock80774932015-05-07 17:38:50 -04001974 }
1975
1976 private boolean checkPolicyAccess(String pkg) {
Julia Reynolds0867b3a2016-03-30 17:29:54 -04001977 try {
1978 int uid = getContext().getPackageManager().getPackageUidAsUser(
1979 pkg, UserHandle.getCallingUserId());
1980 if (PackageManager.PERMISSION_GRANTED == ActivityManager.checkComponentPermission(
1981 android.Manifest.permission.MANAGE_NOTIFICATIONS, uid,
1982 -1, true)) {
1983 return true;
1984 }
1985 } catch (NameNotFoundException e) {
1986 return false;
Julia Reynoldsa2d01022016-03-18 15:03:43 -04001987 }
John Spurlock80774932015-05-07 17:38:50 -04001988 return checkPackagePolicyAccess(pkg) || mListeners.isComponentEnabledForPackage(pkg);
John Spurlock1fc476d2015-04-14 16:05:20 -04001989 }
1990
John Spurlock7340fc82014-04-24 18:50:12 -04001991 @Override
Adam Lesinski182f73f2013-12-05 16:48:06 -08001992 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1993 if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
1994 != PackageManager.PERMISSION_GRANTED) {
John Spurlock2b122f42014-08-27 16:29:47 -04001995 pw.println("Permission Denial: can't dump NotificationManager from pid="
Adam Lesinski182f73f2013-12-05 16:48:06 -08001996 + Binder.getCallingPid()
1997 + ", uid=" + Binder.getCallingUid());
1998 return;
1999 }
2000
Chris Wrene4b38802015-07-07 15:54:19 -04002001 final DumpFilter filter = DumpFilter.parseFromArguments(args);
2002 if (filter != null && filter.stats) {
2003 dumpJson(pw, filter);
2004 } else {
2005 dumpImpl(pw, filter);
2006 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08002007 }
John Spurlockb4782522014-08-22 14:54:46 -04002008
2009 @Override
2010 public ComponentName getEffectsSuppressor() {
John Spurlockcdb57ae2015-02-11 19:04:11 -05002011 enforceSystemOrSystemUIOrVolume("INotificationManager.getEffectsSuppressor");
Bryce Leeba3d8952016-04-12 12:39:15 -07002012 return !mEffectsSuppressors.isEmpty() ? mEffectsSuppressors.get(0) : null;
John Spurlockb4782522014-08-22 14:54:46 -04002013 }
John Spurlock2b122f42014-08-27 16:29:47 -04002014
2015 @Override
2016 public boolean matchesCallFilter(Bundle extras) {
2017 enforceSystemOrSystemUI("INotificationManager.matchesCallFilter");
Christoph Studer12aeda82014-09-23 19:08:56 +02002018 return mZenModeHelper.matchesCallFilter(
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07002019 Binder.getCallingUserHandle(),
Christoph Studer12aeda82014-09-23 19:08:56 +02002020 extras,
2021 mRankingHelper.findExtractor(ValidateNotificationPeople.class),
2022 MATCHES_CALL_FILTER_CONTACTS_TIMEOUT_MS,
2023 MATCHES_CALL_FILTER_TIMEOUT_AFFINITY);
John Spurlock2b122f42014-08-27 16:29:47 -04002024 }
John Spurlock530052a2014-11-30 16:26:19 -05002025
2026 @Override
2027 public boolean isSystemConditionProviderEnabled(String path) {
John Spurlockcdb57ae2015-02-11 19:04:11 -05002028 enforceSystemOrSystemUIOrVolume("INotificationManager.isSystemConditionProviderEnabled");
John Spurlockb2278d62015-04-07 12:47:12 -04002029 return mConditionProviders.isSystemProviderEnabled(path);
John Spurlock530052a2014-11-30 16:26:19 -05002030 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08002031
Christopher Tatef9767d62015-04-08 14:35:43 -07002032 // Backup/restore interface
2033 @Override
2034 public byte[] getBackupPayload(int user) {
John Spurlock35ef0a62015-05-28 11:24:10 -04002035 if (DBG) Slog.d(TAG, "getBackupPayload u=" + user);
Xiaohui Chenddbe4ca2015-08-13 16:20:56 -07002036 //TODO: http://b/22388012
2037 if (user != UserHandle.USER_SYSTEM) {
John Spurlock35ef0a62015-05-28 11:24:10 -04002038 Slog.w(TAG, "getBackupPayload: cannot backup policy for user " + user);
2039 return null;
2040 }
2041 final ByteArrayOutputStream baos = new ByteArrayOutputStream();
2042 try {
2043 writePolicyXml(baos, true /*forBackup*/);
2044 return baos.toByteArray();
2045 } catch (IOException e) {
2046 Slog.w(TAG, "getBackupPayload: error writing payload for user " + user, e);
2047 }
Christopher Tatef9767d62015-04-08 14:35:43 -07002048 return null;
2049 }
2050
2051 @Override
2052 public void applyRestore(byte[] payload, int user) {
John Spurlock35ef0a62015-05-28 11:24:10 -04002053 if (DBG) Slog.d(TAG, "applyRestore u=" + user + " payload="
2054 + (payload != null ? new String(payload, StandardCharsets.UTF_8) : null));
2055 if (payload == null) {
2056 Slog.w(TAG, "applyRestore: no payload to restore for user " + user);
2057 return;
2058 }
Xiaohui Chenddbe4ca2015-08-13 16:20:56 -07002059 //TODO: http://b/22388012
2060 if (user != UserHandle.USER_SYSTEM) {
John Spurlock35ef0a62015-05-28 11:24:10 -04002061 Slog.w(TAG, "applyRestore: cannot restore policy for user " + user);
2062 return;
2063 }
2064 final ByteArrayInputStream bais = new ByteArrayInputStream(payload);
2065 try {
2066 readPolicyXml(bais, true /*forRestore*/);
2067 savePolicyFile();
2068 } catch (NumberFormatException | XmlPullParserException | IOException e) {
2069 Slog.w(TAG, "applyRestore: error reading payload", e);
2070 }
Christopher Tatef9767d62015-04-08 14:35:43 -07002071 }
2072
John Spurlock1fc476d2015-04-14 16:05:20 -04002073 @Override
John Spurlock80774932015-05-07 17:38:50 -04002074 public boolean isNotificationPolicyAccessGranted(String pkg) {
2075 return checkPolicyAccess(pkg);
John Spurlock1fc476d2015-04-14 16:05:20 -04002076 }
2077
2078 @Override
Julia Reynolds48034f82016-03-09 10:15:16 -05002079 public boolean isNotificationPolicyAccessGrantedForPackage(String pkg) {;
2080 enforceSystemOrSystemUIOrSamePackage(pkg,
2081 "request policy access status for another package");
Julia Reynoldsa2d01022016-03-18 15:03:43 -04002082 return checkPolicyAccess(pkg);
John Spurlock80774932015-05-07 17:38:50 -04002083 }
2084
2085 @Override
2086 public String[] getPackagesRequestingNotificationPolicyAccess()
2087 throws RemoteException {
2088 enforceSystemOrSystemUI("request policy access packages");
2089 final long identity = Binder.clearCallingIdentity();
2090 try {
John Spurlock7c74f782015-06-04 13:01:42 -04002091 return mPolicyAccess.getRequestingPackages();
John Spurlock80774932015-05-07 17:38:50 -04002092 } finally {
2093 Binder.restoreCallingIdentity(identity);
2094 }
2095 }
2096
2097 @Override
2098 public void setNotificationPolicyAccessGranted(String pkg, boolean granted)
2099 throws RemoteException {
2100 enforceSystemOrSystemUI("grant notification policy access");
2101 final long identity = Binder.clearCallingIdentity();
2102 try {
2103 synchronized (mNotificationList) {
2104 mPolicyAccess.put(pkg, granted);
2105 }
2106 } finally {
2107 Binder.restoreCallingIdentity(identity);
2108 }
2109 }
2110
2111 @Override
2112 public Policy getNotificationPolicy(String pkg) {
2113 enforcePolicyAccess(pkg, "getNotificationPolicy");
John Spurlock1fc476d2015-04-14 16:05:20 -04002114 final long identity = Binder.clearCallingIdentity();
2115 try {
2116 return mZenModeHelper.getNotificationPolicy();
2117 } finally {
2118 Binder.restoreCallingIdentity(identity);
2119 }
2120 }
2121
2122 @Override
John Spurlock80774932015-05-07 17:38:50 -04002123 public void setNotificationPolicy(String pkg, Policy policy) {
2124 enforcePolicyAccess(pkg, "setNotificationPolicy");
John Spurlock1fc476d2015-04-14 16:05:20 -04002125 final long identity = Binder.clearCallingIdentity();
2126 try {
2127 mZenModeHelper.setNotificationPolicy(policy);
2128 } finally {
2129 Binder.restoreCallingIdentity(identity);
2130 }
2131 }
Chris Wren51017d02015-12-15 15:34:46 -05002132
2133 @Override
Julia Reynoldse46bb372016-03-17 11:05:58 -04002134 public void applyAdjustmentFromRankerService(INotificationListener token,
2135 Adjustment adjustment) throws RemoteException {
Chris Wren51017d02015-12-15 15:34:46 -05002136 final long identity = Binder.clearCallingIdentity();
2137 try {
2138 synchronized (mNotificationList) {
Chris Wrene0ba7eb2016-03-04 17:30:43 -05002139 mRankerServices.checkServiceTokenLocked(token);
Julia Reynoldse46bb372016-03-17 11:05:58 -04002140 applyAdjustmentLocked(adjustment);
Chris Wren51017d02015-12-15 15:34:46 -05002141 }
Julia Reynoldse46bb372016-03-17 11:05:58 -04002142 maybeAddAutobundleSummary(adjustment);
2143 mRankingHandler.requestSort();
2144 } finally {
2145 Binder.restoreCallingIdentity(identity);
2146 }
2147 }
2148
2149 @Override
2150 public void applyAdjustmentsFromRankerService(INotificationListener token,
2151 List<Adjustment> adjustments) throws RemoteException {
2152
2153 final long identity = Binder.clearCallingIdentity();
2154 try {
2155 synchronized (mNotificationList) {
2156 mRankerServices.checkServiceTokenLocked(token);
2157 for (Adjustment adjustment : adjustments) {
2158 applyAdjustmentLocked(adjustment);
2159 }
2160 }
2161 for (Adjustment adjustment : adjustments) {
2162 maybeAddAutobundleSummary(adjustment);
2163 }
2164 mRankingHandler.requestSort();
Chris Wren51017d02015-12-15 15:34:46 -05002165 } finally {
2166 Binder.restoreCallingIdentity(identity);
2167 }
2168 }
John Spurlock1fc476d2015-04-14 16:05:20 -04002169 };
John Spurlocka4294292014-03-24 18:02:32 -04002170
Julia Reynoldse46bb372016-03-17 11:05:58 -04002171 private void applyAdjustmentLocked(Adjustment adjustment) {
2172 maybeClearAutobundleSummaryLocked(adjustment);
2173 NotificationRecord n = mNotificationsByKey.get(adjustment.getKey());
2174 if (n == null) {
2175 return;
2176 }
2177 if (adjustment.getImportance() != IMPORTANCE_NONE) {
2178 n.setImportance(adjustment.getImportance(), adjustment.getExplanation());
2179 }
2180 if (adjustment.getSignals() != null) {
2181 Bundle.setDefusable(adjustment.getSignals(), true);
Julia Reynoldsb1ebc3b2016-04-15 15:12:36 -04002182 final String autoGroupKey = adjustment.getSignals().getString(
2183 Adjustment.GROUP_KEY_OVERRIDE_KEY, null);
2184 if (autoGroupKey == null) {
2185 EventLogTags.writeNotificationUnautogrouped(adjustment.getKey());
2186 } else {
2187 EventLogTags.writeNotificationAutogrouped(adjustment.getKey());
2188 }
2189 n.sbn.setOverrideGroupKey(autoGroupKey);
Julia Reynoldse46bb372016-03-17 11:05:58 -04002190 }
2191 }
2192
2193 // Clears the 'fake' auto-bunding summary.
2194 private void maybeClearAutobundleSummaryLocked(Adjustment adjustment) {
Julia Reynoldsdbf44812016-04-14 08:54:45 -04002195 if (adjustment.getSignals() != null) {
2196 Bundle.setDefusable(adjustment.getSignals(), true);
2197 if (adjustment.getSignals().containsKey(Adjustment.NEEDS_AUTOGROUPING_KEY)
Julia Reynoldse46bb372016-03-17 11:05:58 -04002198 && !adjustment.getSignals().getBoolean(Adjustment.NEEDS_AUTOGROUPING_KEY, false)) {
Julia Reynoldseae43fb2016-05-09 12:42:58 -04002199 ArrayMap<String, String> summaries =
2200 mAutobundledSummaries.get(adjustment.getUser());
2201 if (summaries != null && summaries.containsKey(adjustment.getPackage())) {
Julia Reynoldsdbf44812016-04-14 08:54:45 -04002202 // Clear summary.
2203 final NotificationRecord removed = mNotificationsByKey.get(
Julia Reynoldseae43fb2016-05-09 12:42:58 -04002204 summaries.remove(adjustment.getPackage()));
Julia Reynoldsdbf44812016-04-14 08:54:45 -04002205 if (removed != null) {
2206 mNotificationList.remove(removed);
2207 cancelNotificationLocked(removed, false, REASON_UNAUTOBUNDLED);
2208 }
Julia Reynoldse46bb372016-03-17 11:05:58 -04002209 }
2210 }
2211 }
2212 }
2213
2214 // Posts a 'fake' summary for a package that has exceeded the solo-notification limit.
2215 private void maybeAddAutobundleSummary(Adjustment adjustment) {
Julia Reynoldsdbf44812016-04-14 08:54:45 -04002216 if (adjustment.getSignals() != null) {
2217 Bundle.setDefusable(adjustment.getSignals(), true);
2218 if (adjustment.getSignals().getBoolean(Adjustment.NEEDS_AUTOGROUPING_KEY, false)) {
2219 final String newAutoBundleKey =
2220 adjustment.getSignals().getString(Adjustment.GROUP_KEY_OVERRIDE_KEY, null);
2221 int userId = -1;
2222 NotificationRecord summaryRecord = null;
2223 synchronized (mNotificationList) {
Julia Reynoldseae43fb2016-05-09 12:42:58 -04002224 final StatusBarNotification adjustedSbn
2225 = mNotificationsByKey.get(adjustment.getKey()).sbn;
2226 userId = adjustedSbn.getUser().getIdentifier();
2227 ArrayMap<String, String> summaries = mAutobundledSummaries.get(userId);
2228 if (summaries == null) {
2229 summaries = new ArrayMap<>();
2230 }
2231 mAutobundledSummaries.put(userId, summaries);
2232 if (!summaries.containsKey(adjustment.getPackage())
Julia Reynoldsdbf44812016-04-14 08:54:45 -04002233 && newAutoBundleKey != null) {
2234 // Add summary
Julia Reynoldsdbf44812016-04-14 08:54:45 -04002235 final ApplicationInfo appInfo =
2236 adjustedSbn.getNotification().extras.getParcelable(
2237 Notification.EXTRA_BUILDER_APPLICATION_INFO);
2238 final Bundle extras = new Bundle();
2239 extras.putParcelable(Notification.EXTRA_BUILDER_APPLICATION_INFO, appInfo);
2240 final Notification summaryNotification =
2241 new Notification.Builder(getContext()).setSmallIcon(
2242 adjustedSbn.getNotification().getSmallIcon())
2243 .setGroupSummary(true)
2244 .setGroup(newAutoBundleKey)
2245 .setFlag(Notification.FLAG_AUTOGROUP_SUMMARY, true)
2246 .setFlag(Notification.FLAG_GROUP_SUMMARY, true)
2247 .build();
2248 summaryNotification.extras.putAll(extras);
Julia Reynolds5bba3e72016-04-15 15:13:54 -04002249 Intent appIntent = getContext().getPackageManager()
2250 .getLaunchIntentForPackage(adjustment.getPackage());
2251 if (appIntent != null) {
2252 summaryNotification.contentIntent = PendingIntent.getActivityAsUser(
2253 getContext(), 0, appIntent, 0, null,
Julia Reynoldseae43fb2016-05-09 12:42:58 -04002254 UserHandle.of(userId));
Julia Reynolds5bba3e72016-04-15 15:13:54 -04002255 }
Julia Reynoldsdbf44812016-04-14 08:54:45 -04002256 final StatusBarNotification summarySbn =
2257 new StatusBarNotification(adjustedSbn.getPackageName(),
2258 adjustedSbn.getOpPkg(),
2259 Integer.MAX_VALUE, Adjustment.GROUP_KEY_OVERRIDE_KEY,
2260 adjustedSbn.getUid(), adjustedSbn.getInitialPid(),
2261 summaryNotification, adjustedSbn.getUser(),
2262 newAutoBundleKey,
2263 System.currentTimeMillis());
2264 summaryRecord = new NotificationRecord(getContext(), summarySbn);
Julia Reynoldseae43fb2016-05-09 12:42:58 -04002265 summaries.put(adjustment.getPackage(), summarySbn.getKey());
Julia Reynoldsdbf44812016-04-14 08:54:45 -04002266 }
Julia Reynoldse46bb372016-03-17 11:05:58 -04002267 }
Julia Reynoldsdbf44812016-04-14 08:54:45 -04002268 if (summaryRecord != null) {
2269 mHandler.post(new EnqueueNotificationRunnable(userId, summaryRecord));
2270 }
Julia Reynoldse46bb372016-03-17 11:05:58 -04002271 }
2272 }
2273 }
2274
John Spurlock32fe4c62014-10-02 12:16:02 -04002275 private String disableNotificationEffects(NotificationRecord record) {
2276 if (mDisableNotificationEffects) {
2277 return "booleanState";
2278 }
2279 if ((mListenerHints & HINT_HOST_DISABLE_EFFECTS) != 0) {
2280 return "listenerHints";
2281 }
2282 if (mCallState != TelephonyManager.CALL_STATE_IDLE && !mZenModeHelper.isCall(record)) {
2283 return "callState";
2284 }
2285 return null;
Chris Wrene4b38802015-07-07 15:54:19 -04002286 };
2287
2288 private void dumpJson(PrintWriter pw, DumpFilter filter) {
2289 JSONObject dump = new JSONObject();
2290 try {
2291 dump.put("service", "Notification Manager");
Chris Wrenacf424a2016-03-15 12:48:55 -04002292 dump.put("bans", mRankingHelper.dumpBansJson(filter));
2293 dump.put("ranking", mRankingHelper.dumpJson(filter));
Chris Wrene4b38802015-07-07 15:54:19 -04002294 dump.put("stats", mUsageStats.dumpJson(filter));
2295 } catch (JSONException e) {
2296 e.printStackTrace();
2297 }
2298 pw.println(dump);
John Spurlock1fa865f2014-07-21 14:56:39 -04002299 }
2300
John Spurlock25e2d242014-06-27 13:58:23 -04002301 void dumpImpl(PrintWriter pw, DumpFilter filter) {
2302 pw.print("Current Notification Manager state");
Dan Sandlera1770312015-07-10 13:59:29 -04002303 if (filter.filtered) {
John Spurlock50806fc2014-07-15 10:22:02 -04002304 pw.print(" (filtered to "); pw.print(filter); pw.print(")");
John Spurlock25e2d242014-06-27 13:58:23 -04002305 }
2306 pw.println(':');
Adam Lesinski182f73f2013-12-05 16:48:06 -08002307 int N;
Julia Reynoldse6b53e62015-07-31 09:25:10 -04002308 final boolean zenOnly = filter.filtered && filter.zen;
Adam Lesinski182f73f2013-12-05 16:48:06 -08002309
John Spurlock50806fc2014-07-15 10:22:02 -04002310 if (!zenOnly) {
2311 synchronized (mToastQueue) {
2312 N = mToastQueue.size();
2313 if (N > 0) {
2314 pw.println(" Toast Queue:");
2315 for (int i=0; i<N; i++) {
2316 mToastQueue.get(i).dump(pw, " ", filter);
2317 }
2318 pw.println(" ");
Adam Lesinski182f73f2013-12-05 16:48:06 -08002319 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08002320 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08002321 }
2322
2323 synchronized (mNotificationList) {
John Spurlock50806fc2014-07-15 10:22:02 -04002324 if (!zenOnly) {
2325 N = mNotificationList.size();
John Spurlock25e2d242014-06-27 13:58:23 -04002326 if (N > 0) {
John Spurlock50806fc2014-07-15 10:22:02 -04002327 pw.println(" Notification List:");
John Spurlock25e2d242014-06-27 13:58:23 -04002328 for (int i=0; i<N; i++) {
John Spurlock50806fc2014-07-15 10:22:02 -04002329 final NotificationRecord nr = mNotificationList.get(i);
Julia Reynoldse6b53e62015-07-31 09:25:10 -04002330 if (filter.filtered && !filter.matches(nr.sbn)) continue;
Dan Sandlera1770312015-07-10 13:59:29 -04002331 nr.dump(pw, " ", getContext(), filter.redact);
John Spurlock25e2d242014-06-27 13:58:23 -04002332 }
2333 pw.println(" ");
Adam Lesinski182f73f2013-12-05 16:48:06 -08002334 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08002335
Julia Reynoldse6b53e62015-07-31 09:25:10 -04002336 if (!filter.filtered) {
John Spurlock50806fc2014-07-15 10:22:02 -04002337 N = mLights.size();
2338 if (N > 0) {
2339 pw.println(" Lights List:");
2340 for (int i=0; i<N; i++) {
Chris Wren6054e612014-11-25 17:16:46 -05002341 if (i == N - 1) {
2342 pw.print(" > ");
2343 } else {
2344 pw.print(" ");
2345 }
2346 pw.println(mLights.get(i));
John Spurlock50806fc2014-07-15 10:22:02 -04002347 }
2348 pw.println(" ");
2349 }
John Spurlockcb566aa2014-08-03 22:58:28 -04002350 pw.println(" mUseAttentionLight=" + mUseAttentionLight);
2351 pw.println(" mNotificationPulseEnabled=" + mNotificationPulseEnabled);
Chris Wren6054e612014-11-25 17:16:46 -05002352 pw.println(" mSoundNotificationKey=" + mSoundNotificationKey);
2353 pw.println(" mVibrateNotificationKey=" + mVibrateNotificationKey);
John Spurlockd8afe3c2014-08-01 14:04:07 -04002354 pw.println(" mDisableNotificationEffects=" + mDisableNotificationEffects);
John Spurlock32fe4c62014-10-02 12:16:02 -04002355 pw.println(" mCallState=" + callStateToString(mCallState));
John Spurlock50806fc2014-07-15 10:22:02 -04002356 pw.println(" mSystemReady=" + mSystemReady);
2357 }
2358 pw.println(" mArchive=" + mArchive.toString());
2359 Iterator<StatusBarNotification> iter = mArchive.descendingIterator();
2360 int i=0;
2361 while (iter.hasNext()) {
2362 final StatusBarNotification sbn = iter.next();
2363 if (filter != null && !filter.matches(sbn)) continue;
2364 pw.println(" " + sbn);
2365 if (++i >= 5) {
2366 if (iter.hasNext()) pw.println(" ...");
2367 break;
2368 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08002369 }
2370 }
2371
John Spurlock50806fc2014-07-15 10:22:02 -04002372 if (!zenOnly) {
2373 pw.println("\n Usage Stats:");
2374 mUsageStats.dump(pw, " ", filter);
2375 }
Christoph Studer546bec82014-03-14 12:17:12 +01002376
Julia Reynoldse6b53e62015-07-31 09:25:10 -04002377 if (!filter.filtered || zenOnly) {
John Spurlock25e2d242014-06-27 13:58:23 -04002378 pw.println("\n Zen Mode:");
John Spurlockf3701772015-02-12 13:29:37 -05002379 pw.print(" mInterruptionFilter="); pw.println(mInterruptionFilter);
John Spurlock25e2d242014-06-27 13:58:23 -04002380 mZenModeHelper.dump(pw, " ");
John Spurlock6ae82a72014-07-16 16:23:01 -04002381
2382 pw.println("\n Zen Log:");
2383 ZenLog.dump(pw, " ");
John Spurlock25e2d242014-06-27 13:58:23 -04002384 }
John Spurlocke77bb362014-04-26 10:24:59 -04002385
John Spurlock50806fc2014-07-15 10:22:02 -04002386 if (!zenOnly) {
2387 pw.println("\n Ranking Config:");
2388 mRankingHelper.dump(pw, " ", filter);
Chris Wren54bbef42014-07-09 18:37:56 -04002389
John Spurlock50806fc2014-07-15 10:22:02 -04002390 pw.println("\n Notification listeners:");
2391 mListeners.dump(pw, filter);
John Spurlockd8afe3c2014-08-01 14:04:07 -04002392 pw.print(" mListenerHints: "); pw.println(mListenerHints);
2393 pw.print(" mListenersDisablingEffects: (");
2394 N = mListenersDisablingEffects.size();
John Spurlock1fa865f2014-07-21 14:56:39 -04002395 for (int i = 0; i < N; i++) {
Bryce Lee7219ada2016-04-08 10:54:23 -07002396 final int hint = mListenersDisablingEffects.keyAt(i);
2397 if (i > 0) pw.print(';');
2398 pw.print("hint[" + hint + "]:");
2399
2400 final ArraySet<ManagedServiceInfo> listeners =
2401 mListenersDisablingEffects.valueAt(i);
2402 final int listenerSize = listeners.size();
2403
2404 for (int j = 0; j < listenerSize; j++) {
2405 if (i > 0) pw.print(',');
2406 final ManagedServiceInfo listener = listeners.valueAt(i);
2407 pw.print(listener.component);
2408 }
John Spurlock1fa865f2014-07-21 14:56:39 -04002409 }
2410 pw.println(')');
Chris Wren0efdb882016-03-01 17:17:47 -05002411 pw.println("\n mRankerServicePackageName: " + mRankerServicePackageName);
Chris Wrene0ba7eb2016-03-04 17:30:43 -05002412 pw.println("\n Notification ranker services:");
2413 mRankerServices.dump(pw, filter);
John Spurlock50806fc2014-07-15 10:22:02 -04002414 }
John Spurlock80774932015-05-07 17:38:50 -04002415 pw.println("\n Policy access:");
2416 pw.print(" mPolicyAccess: "); pw.println(mPolicyAccess);
John Spurlocke77bb362014-04-26 10:24:59 -04002417
2418 pw.println("\n Condition providers:");
John Spurlock25e2d242014-06-27 13:58:23 -04002419 mConditionProviders.dump(pw, filter);
Christoph Studer265c1052014-07-23 17:14:33 +02002420
2421 pw.println("\n Group summaries:");
2422 for (Entry<String, NotificationRecord> entry : mSummaryByGroupKey.entrySet()) {
2423 NotificationRecord r = entry.getValue();
2424 pw.println(" " + entry.getKey() + " -> " + r.getKey());
2425 if (mNotificationsByKey.get(r.getKey()) != r) {
2426 pw.println("!!!!!!LEAK: Record not found in mNotificationsByKey.");
Dan Sandlera1770312015-07-10 13:59:29 -04002427 r.dump(pw, " ", getContext(), filter.redact);
Christoph Studer265c1052014-07-23 17:14:33 +02002428 }
2429 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002430 }
2431 }
2432
Adam Lesinski182f73f2013-12-05 16:48:06 -08002433 /**
2434 * The private API only accessible to the system process.
2435 */
2436 private final NotificationManagerInternal mInternalService = new NotificationManagerInternal() {
2437 @Override
Christoph Studer8fd7f1e2014-04-11 17:35:05 -04002438 public void enqueueNotification(String pkg, String opPkg, int callingUid, int callingPid,
Adam Lesinski182f73f2013-12-05 16:48:06 -08002439 String tag, int id, Notification notification, int[] idReceived, int userId) {
Christoph Studer8fd7f1e2014-04-11 17:35:05 -04002440 enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, notification,
Adam Lesinski182f73f2013-12-05 16:48:06 -08002441 idReceived, userId);
2442 }
Christoph Studer365e4c32014-09-18 20:35:36 +02002443
2444 @Override
2445 public void removeForegroundServiceFlagFromNotification(String pkg, int notificationId,
2446 int userId) {
2447 checkCallerIsSystem();
2448 synchronized (mNotificationList) {
2449 int i = indexOfNotificationLocked(pkg, null, notificationId, userId);
2450 if (i < 0) {
2451 Log.d(TAG, "stripForegroundServiceFlag: Could not find notification with "
2452 + "pkg=" + pkg + " / id=" + notificationId + " / userId=" + userId);
2453 return;
2454 }
2455 NotificationRecord r = mNotificationList.get(i);
2456 StatusBarNotification sbn = r.sbn;
2457 // NoMan adds flags FLAG_NO_CLEAR and FLAG_ONGOING_EVENT when it sees
2458 // FLAG_FOREGROUND_SERVICE. Hence it's not enough to remove FLAG_FOREGROUND_SERVICE,
2459 // we have to revert to the flags we received initially *and* force remove
2460 // FLAG_FOREGROUND_SERVICE.
2461 sbn.getNotification().flags =
2462 (r.mOriginalFlags & ~Notification.FLAG_FOREGROUND_SERVICE);
2463 mRankingHelper.sort(mNotificationList);
2464 mListeners.notifyPostedLocked(sbn, sbn /* oldSbn */);
2465 }
2466 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08002467 };
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002468
Christoph Studer8fd7f1e2014-04-11 17:35:05 -04002469 void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,
Scott Greenwald9b05c612013-06-25 23:44:05 -04002470 final int callingPid, final String tag, final int id, final Notification notification,
Adam Lesinski182f73f2013-12-05 16:48:06 -08002471 int[] idOut, int incomingUserId) {
Daniel Sandler0da673f2012-04-11 12:33:16 -04002472 if (DBG) {
Adam Lesinski182f73f2013-12-05 16:48:06 -08002473 Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id
2474 + " notification=" + notification);
Daniel Sandler0da673f2012-04-11 12:33:16 -04002475 }
John Spurlock7340fc82014-04-24 18:50:12 -04002476 checkCallerIsSystemOrSameApp(pkg);
2477 final boolean isSystemNotification = isUidSystem(callingUid) || ("android".equals(pkg));
Justin Koh38156c52014-06-04 13:57:49 -07002478 final boolean isNotificationFromListener = mListeners.isListenerPackage(pkg);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08002479
Scott Greenwald9b05c612013-06-25 23:44:05 -04002480 final int userId = ActivityManager.handleIncomingUser(callingPid,
2481 callingUid, incomingUserId, true, false, "enqueueNotification", pkg);
Jeff Sharkey65c4a2b2012-09-25 17:22:27 -07002482 final UserHandle user = new UserHandle(userId);
Dianne Hackborn41203752012-08-31 14:05:51 -07002483
Julia Reynoldse46bb372016-03-17 11:05:58 -04002484 // Fix the notification as best we can.
2485 try {
Jeff Sharkey012bc7b2016-04-11 16:30:27 -06002486 final ApplicationInfo ai = getContext().getPackageManager().getApplicationInfoAsUser(
2487 pkg, PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
2488 (userId == UserHandle.USER_ALL) ? UserHandle.USER_SYSTEM : userId);
2489 Notification.addFieldsFromContext(ai, userId, notification);
Julia Reynoldse46bb372016-03-17 11:05:58 -04002490 } catch (NameNotFoundException e) {
2491 Slog.e(TAG, "Cannot create a context for sending app", e);
2492 return;
2493 }
2494
Joe Onoratobd73d012010-06-04 11:44:54 -07002495 // Limit the number of notifications that any given package except the android
Justin Koh38156c52014-06-04 13:57:49 -07002496 // package or a registered listener can enqueue. Prevents DOS attacks and deals with leaks.
2497 if (!isSystemNotification && !isNotificationFromListener) {
Joe Onoratobd73d012010-06-04 11:44:54 -07002498 synchronized (mNotificationList) {
2499 int count = 0;
2500 final int N = mNotificationList.size();
2501 for (int i=0; i<N; i++) {
2502 final NotificationRecord r = mNotificationList.get(i);
Daniel Sandler4f91efd2013-04-25 16:38:41 -04002503 if (r.sbn.getPackageName().equals(pkg) && r.sbn.getUserId() == userId) {
Vladimir Marko2526f332013-09-11 11:13:55 +01002504 if (r.sbn.getId() == id && TextUtils.equals(r.sbn.getTag(), tag)) {
2505 break; // Allow updating existing notification
2506 }
Joe Onoratobd73d012010-06-04 11:44:54 -07002507 count++;
2508 if (count >= MAX_PACKAGE_NOTIFICATIONS) {
2509 Slog.e(TAG, "Package has already posted " + count
2510 + " notifications. Not showing more. package=" + pkg);
2511 return;
2512 }
2513 }
2514 }
2515 }
2516 }
2517
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002518 if (pkg == null || notification == null) {
2519 throw new IllegalArgumentException("null not allowed: pkg=" + pkg
2520 + " id=" + id + " notification=" + notification);
2521 }
Dan Sandlerd63f9322015-05-06 15:18:49 -04002522
Chris Wren47633422016-01-22 09:56:59 -05002523 // Sanitize inputs
2524 notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN,
2525 Notification.PRIORITY_MAX);
Daniel Sandler0da673f2012-04-11 12:33:16 -04002526
Chris Wren47633422016-01-22 09:56:59 -05002527 // setup local book-keeping
2528 final StatusBarNotification n = new StatusBarNotification(
2529 pkg, opPkg, id, tag, callingUid, callingPid, 0, notification,
2530 user);
2531 final NotificationRecord r = new NotificationRecord(getContext(), n);
2532 mHandler.post(new EnqueueNotificationRunnable(userId, r));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002533
2534 idOut[0] = id;
2535 }
2536
Chris Wren47633422016-01-22 09:56:59 -05002537 private class EnqueueNotificationRunnable implements Runnable {
2538 private final NotificationRecord r;
2539 private final int userId;
2540
2541 EnqueueNotificationRunnable(int userId, NotificationRecord r) {
2542 this.userId = userId;
2543 this.r = r;
2544 };
2545
2546 @Override
2547 public void run() {
2548
2549 synchronized (mNotificationList) {
2550 final StatusBarNotification n = r.sbn;
Chris Wren1ac52a92016-02-24 14:54:52 -05002551 if (DBG) Slog.d(TAG, "EnqueueNotificationRunnable.run for: " + n.getKey());
Chris Wren47633422016-01-22 09:56:59 -05002552 NotificationRecord old = mNotificationsByKey.get(n.getKey());
2553 if (old != null) {
2554 // Retain ranking information from previous record
2555 r.copyRankingInformation(old);
2556 }
2557
2558 final int callingUid = n.getUid();
2559 final int callingPid = n.getInitialPid();
2560 final Notification notification = n.getNotification();
2561 final String pkg = n.getPackageName();
2562 final int id = n.getId();
2563 final String tag = n.getTag();
2564 final boolean isSystemNotification = isUidSystem(callingUid) ||
2565 ("android".equals(pkg));
2566
2567 // Handle grouped notifications and bail out early if we
2568 // can to avoid extracting signals.
2569 handleGroupedNotificationLocked(r, old, callingUid, callingPid);
Chris Wren47633422016-01-22 09:56:59 -05002570
2571 // This conditional is a dirty hack to limit the logging done on
2572 // behalf of the download manager without affecting other apps.
2573 if (!pkg.equals("com.android.providers.downloads")
2574 || Log.isLoggable("DownloadManager", Log.VERBOSE)) {
2575 int enqueueStatus = EVENTLOG_ENQUEUE_STATUS_NEW;
Chris Wren8ab776c2016-04-11 16:48:24 -04002576 if (old != null) {
Chris Wren47633422016-01-22 09:56:59 -05002577 enqueueStatus = EVENTLOG_ENQUEUE_STATUS_UPDATE;
2578 }
2579 EventLogTags.writeNotificationEnqueue(callingUid, callingPid,
2580 pkg, id, tag, userId, notification.toString(),
2581 enqueueStatus);
2582 }
2583
Chris Wren47633422016-01-22 09:56:59 -05002584 mRankingHelper.extractSignals(r);
2585
Andrei Stingaceanu355b2322016-02-12 16:43:51 +00002586 final boolean isPackageSuspended = isPackageSuspendedForUser(pkg, callingUid);
Chris Wren47633422016-01-22 09:56:59 -05002587
Julia Reynoldsef37f282016-02-12 09:11:27 -05002588 // blocked apps
Chris Wren47633422016-01-22 09:56:59 -05002589 if (r.getImportance() == NotificationListenerService.Ranking.IMPORTANCE_NONE
Andrei Stingaceanu355b2322016-02-12 16:43:51 +00002590 || !noteNotificationOp(pkg, callingUid) || isPackageSuspended) {
Chris Wren47633422016-01-22 09:56:59 -05002591 if (!isSystemNotification) {
Andrei Stingaceanu355b2322016-02-12 16:43:51 +00002592 if (isPackageSuspended) {
Andrei Stingaceanu0122f6512016-01-22 15:33:03 +00002593 Slog.e(TAG, "Suppressing notification from package due to package "
2594 + "suspended by administrator.");
2595 mUsageStats.registerSuspendedByAdmin(r);
2596 } else {
2597 Slog.e(TAG, "Suppressing notification from package by user request.");
2598 mUsageStats.registerBlocked(r);
2599 }
Chris Wren47633422016-01-22 09:56:59 -05002600 return;
2601 }
2602 }
2603
Chris Wrene0ba7eb2016-03-04 17:30:43 -05002604 // tell the ranker service about the notification
2605 if (mRankerServices.isEnabled()) {
2606 mRankerServices.onNotificationEnqueued(r);
Chris Wren47633422016-01-22 09:56:59 -05002607 // TODO delay the code below here for 100ms or until there is an answer
2608 }
2609
2610
2611 int index = indexOfNotificationLocked(n.getKey());
2612 if (index < 0) {
2613 mNotificationList.add(r);
2614 mUsageStats.registerPostedByApp(r);
2615 } else {
2616 old = mNotificationList.get(index);
2617 mNotificationList.set(index, r);
2618 mUsageStats.registerUpdatedByApp(r, old);
2619 // Make sure we don't lose the foreground service state.
2620 notification.flags |=
2621 old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;
2622 r.isUpdate = true;
2623 }
2624
2625 mNotificationsByKey.put(n.getKey(), r);
2626
2627 // Ensure if this is a foreground service that the proper additional
2628 // flags are set.
2629 if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
2630 notification.flags |= Notification.FLAG_ONGOING_EVENT
2631 | Notification.FLAG_NO_CLEAR;
2632 }
2633
2634 applyZenModeLocked(r);
2635 mRankingHelper.sort(mNotificationList);
2636
2637 if (notification.getSmallIcon() != null) {
2638 StatusBarNotification oldSbn = (old != null) ? old.sbn : null;
2639 mListeners.notifyPostedLocked(n, oldSbn);
2640 } else {
2641 Slog.e(TAG, "Not posting notification without small icon: " + notification);
2642 if (old != null && !old.isCanceled) {
2643 mListeners.notifyRemovedLocked(n);
2644 }
2645 // ATTENTION: in a future release we will bail out here
2646 // so that we do not play sounds, show lights, etc. for invalid
2647 // notifications
2648 Slog.e(TAG, "WARNING: In a future release this will crash the app: "
2649 + n.getPackageName());
2650 }
2651
2652 buzzBeepBlinkLocked(r);
2653 }
2654 }
2655 }
2656
Christoph Studer265c1052014-07-23 17:14:33 +02002657 /**
2658 * Ensures that grouped notification receive their special treatment.
2659 *
2660 * <p>Cancels group children if the new notification causes a group to lose
2661 * its summary.</p>
2662 *
2663 * <p>Updates mSummaryByGroupKey.</p>
2664 */
2665 private void handleGroupedNotificationLocked(NotificationRecord r, NotificationRecord old,
2666 int callingUid, int callingPid) {
2667 StatusBarNotification sbn = r.sbn;
2668 Notification n = sbn.getNotification();
2669 String group = sbn.getGroupKey();
2670 boolean isSummary = n.isGroupSummary();
2671
2672 Notification oldN = old != null ? old.sbn.getNotification() : null;
2673 String oldGroup = old != null ? old.sbn.getGroupKey() : null;
2674 boolean oldIsSummary = old != null && oldN.isGroupSummary();
2675
2676 if (oldIsSummary) {
2677 NotificationRecord removedSummary = mSummaryByGroupKey.remove(oldGroup);
2678 if (removedSummary != old) {
2679 String removedKey =
2680 removedSummary != null ? removedSummary.getKey() : "<null>";
2681 Slog.w(TAG, "Removed summary didn't match old notification: old=" + old.getKey() +
2682 ", removed=" + removedKey);
2683 }
2684 }
2685 if (isSummary) {
2686 mSummaryByGroupKey.put(group, r);
2687 }
2688
2689 // Clear out group children of the old notification if the update
2690 // causes the group summary to go away. This happens when the old
2691 // notification was a summary and the new one isn't, or when the old
2692 // notification was a summary and its group key changed.
2693 if (oldIsSummary && (!isSummary || !oldGroup.equals(group))) {
2694 cancelGroupChildrenLocked(old, callingUid, callingPid, null,
2695 REASON_GROUP_SUMMARY_CANCELED);
2696 }
2697 }
2698
Chris Wren93bb8b82016-03-29 14:35:05 -04002699 @VisibleForTesting
2700 void buzzBeepBlinkLocked(NotificationRecord record) {
Chris Wren82ba59d2015-06-05 11:23:44 -04002701 boolean buzz = false;
2702 boolean beep = false;
2703 boolean blink = false;
2704
Chris Wrena3446562014-06-03 18:11:47 -04002705 final Notification notification = record.sbn.getNotification();
Chris Wren93bb8b82016-03-29 14:35:05 -04002706 final String key = record.getKey();
Chris Wrena3446562014-06-03 18:11:47 -04002707
2708 // Should this notification make noise, vibe, or use the LED?
Julia Reynoldsf0f629f2016-02-25 09:34:04 -05002709 final boolean aboveThreshold = record.getImportance() >= IMPORTANCE_DEFAULT;
Chris Wrence00a232014-11-21 16:25:19 -05002710 final boolean canInterrupt = aboveThreshold && !record.isIntercepted();
Chris Wrena3446562014-06-03 18:11:47 -04002711 if (DBG || record.isIntercepted())
2712 Slog.v(TAG,
2713 "pkg=" + record.sbn.getPackageName() + " canInterrupt=" + canInterrupt +
2714 " intercept=" + record.isIntercepted()
2715 );
2716
2717 final int currentUser;
2718 final long token = Binder.clearCallingIdentity();
2719 try {
2720 currentUser = ActivityManager.getCurrentUser();
2721 } finally {
2722 Binder.restoreCallingIdentity(token);
2723 }
2724
2725 // If we're not supposed to beep, vibrate, etc. then don't.
John Spurlock32fe4c62014-10-02 12:16:02 -04002726 final String disableEffects = disableNotificationEffects(record);
2727 if (disableEffects != null) {
2728 ZenLog.traceDisableEffects(record, disableEffects);
2729 }
Chris Wren93bb8b82016-03-29 14:35:05 -04002730
2731 // Remember if this notification already owns the notification channels.
2732 boolean wasBeep = key != null && key.equals(mSoundNotificationKey);
2733 boolean wasBuzz = key != null && key.equals(mVibrateNotificationKey);
2734
2735 // These are set inside the conditional if the notification is allowed to make noise.
2736 boolean hasValidVibrate = false;
2737 boolean hasValidSound = false;
John Spurlock32fe4c62014-10-02 12:16:02 -04002738 if (disableEffects == null
Chris Wrena3446562014-06-03 18:11:47 -04002739 && (record.getUserId() == UserHandle.USER_ALL ||
2740 record.getUserId() == currentUser ||
2741 mUserProfiles.isCurrentProfile(record.getUserId()))
2742 && canInterrupt
2743 && mSystemReady
2744 && mAudioManager != null) {
2745 if (DBG) Slog.v(TAG, "Interrupting!");
2746
Chris Wrena3446562014-06-03 18:11:47 -04002747 // should we use the default notification sound? (indicated either by
2748 // DEFAULT_SOUND or because notification.sound is pointing at
2749 // Settings.System.NOTIFICATION_SOUND)
2750 final boolean useDefaultSound =
2751 (notification.defaults & Notification.DEFAULT_SOUND) != 0 ||
2752 Settings.System.DEFAULT_NOTIFICATION_URI
2753 .equals(notification.sound);
2754
2755 Uri soundUri = null;
Chris Wrena3446562014-06-03 18:11:47 -04002756 if (useDefaultSound) {
2757 soundUri = Settings.System.DEFAULT_NOTIFICATION_URI;
2758
2759 // check to see if the default notification sound is silent
2760 ContentResolver resolver = getContext().getContentResolver();
2761 hasValidSound = Settings.System.getString(resolver,
2762 Settings.System.NOTIFICATION_SOUND) != null;
2763 } else if (notification.sound != null) {
2764 soundUri = notification.sound;
2765 hasValidSound = (soundUri != null);
2766 }
2767
Chris Wrena3446562014-06-03 18:11:47 -04002768 // Does the notification want to specify its own vibration?
2769 final boolean hasCustomVibrate = notification.vibrate != null;
2770
2771 // new in 4.2: if there was supposed to be a sound and we're in vibrate
2772 // mode, and no other vibration is specified, we fall back to vibration
2773 final boolean convertSoundToVibration =
Chris Wren93bb8b82016-03-29 14:35:05 -04002774 !hasCustomVibrate
2775 && hasValidSound
2776 && (mAudioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_VIBRATE);
Chris Wrena3446562014-06-03 18:11:47 -04002777
2778 // The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback.
2779 final boolean useDefaultVibrate =
2780 (notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
2781
Chris Wren93bb8b82016-03-29 14:35:05 -04002782 hasValidVibrate = useDefaultVibrate || convertSoundToVibration ||
2783 hasCustomVibrate;
Chris Wrena3446562014-06-03 18:11:47 -04002784
Chris Wren93bb8b82016-03-29 14:35:05 -04002785 // We can alert, and we're allowed to alert, but if the developer asked us to only do
2786 // it once, and we already have, then don't.
2787 if (!(record.isUpdate
2788 && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0)) {
2789
2790 sendAccessibilityEvent(notification, record.sbn.getPackageName());
2791
2792 if (hasValidSound) {
2793 boolean looping =
2794 (notification.flags & Notification.FLAG_INSISTENT) != 0;
2795 AudioAttributes audioAttributes = audioAttributesForNotification(notification);
2796 mSoundNotificationKey = key;
2797 // do not play notifications if stream volume is 0 (typically because
2798 // ringer mode is silent) or if there is a user of exclusive audio focus
2799 if ((mAudioManager.getStreamVolume(
2800 AudioAttributes.toLegacyStreamType(audioAttributes)) != 0)
2801 && !mAudioManager.isAudioFocusExclusive()) {
2802 final long identity = Binder.clearCallingIdentity();
2803 try {
2804 final IRingtonePlayer player =
2805 mAudioManager.getRingtonePlayer();
2806 if (player != null) {
2807 if (DBG) Slog.v(TAG, "Playing sound " + soundUri
2808 + " with attributes " + audioAttributes);
2809 player.playAsync(soundUri, record.sbn.getUser(), looping,
2810 audioAttributes);
2811 beep = true;
2812 }
2813 } catch (RemoteException e) {
2814 } finally {
2815 Binder.restoreCallingIdentity(identity);
2816 }
Chris Wrena3446562014-06-03 18:11:47 -04002817 }
Chris Wren93bb8b82016-03-29 14:35:05 -04002818 }
2819
2820 if (hasValidVibrate && !(mAudioManager.getRingerModeInternal()
2821 == AudioManager.RINGER_MODE_SILENT)) {
2822 mVibrateNotificationKey = key;
2823
2824 if (useDefaultVibrate || convertSoundToVibration) {
2825 // Escalate privileges so we can use the vibrator even if the
2826 // notifying app does not have the VIBRATE permission.
2827 long identity = Binder.clearCallingIdentity();
2828 try {
2829 mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
2830 useDefaultVibrate ? mDefaultVibrationPattern
2831 : mFallbackVibrationPattern,
2832 ((notification.flags & Notification.FLAG_INSISTENT) != 0)
2833 ? 0: -1, audioAttributesForNotification(notification));
2834 buzz = true;
2835 } finally {
2836 Binder.restoreCallingIdentity(identity);
2837 }
2838 } else if (notification.vibrate.length > 1) {
2839 // If you want your own vibration pattern, you need the VIBRATE
2840 // permission
2841 mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
2842 notification.vibrate,
2843 ((notification.flags & Notification.FLAG_INSISTENT) != 0)
2844 ? 0: -1, audioAttributesForNotification(notification));
2845 buzz = true;
2846 }
Chris Wrena3446562014-06-03 18:11:47 -04002847 }
2848 }
Chris Wren93bb8b82016-03-29 14:35:05 -04002849
2850 }
2851 // If a notification is updated to remove the actively playing sound or vibrate,
2852 // cancel that feedback now
2853 if (wasBeep && !hasValidSound) {
2854 clearSoundLocked();
2855 }
2856 if (wasBuzz && !hasValidVibrate) {
2857 clearVibrateLocked();
Chris Wrena3446562014-06-03 18:11:47 -04002858 }
2859
2860 // light
2861 // release the light
Chris Wren93bb8b82016-03-29 14:35:05 -04002862 boolean wasShowLights = mLights.remove(key);
Julia Reynoldsf612869ae2015-11-05 16:48:55 -05002863 if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0 && aboveThreshold
2864 && ((record.getSuppressedVisualEffects()
Julia Reynoldsd5607292016-02-05 15:25:58 -05002865 & NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_OFF) == 0)) {
Chris Wren93bb8b82016-03-29 14:35:05 -04002866 mLights.add(key);
Chris Wrena3446562014-06-03 18:11:47 -04002867 updateLightsLocked();
Chris Wren5116a822014-06-04 15:59:50 -04002868 if (mUseAttentionLight) {
2869 mAttentionLight.pulse();
2870 }
Chris Wren82ba59d2015-06-05 11:23:44 -04002871 blink = true;
Chris Wrena3446562014-06-03 18:11:47 -04002872 } else if (wasShowLights) {
2873 updateLightsLocked();
2874 }
Chris Wren82ba59d2015-06-05 11:23:44 -04002875 if (buzz || beep || blink) {
Julia Reynolds61721582016-01-05 08:35:25 -05002876 if (((record.getSuppressedVisualEffects()
Julia Reynoldsd5607292016-02-05 15:25:58 -05002877 & NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_OFF) != 0)) {
Julia Reynolds61721582016-01-05 08:35:25 -05002878 if (DBG) Slog.v(TAG, "Suppressed SystemUI from triggering screen on");
2879 } else {
Chris Wren93bb8b82016-03-29 14:35:05 -04002880 EventLogTags.writeNotificationAlert(key,
Julia Reynolds61721582016-01-05 08:35:25 -05002881 buzz ? 1 : 0, beep ? 1 : 0, blink ? 1 : 0);
2882 mHandler.post(mBuzzBeepBlinked);
2883 }
John Spurlockcad57682014-07-26 17:09:56 -04002884 }
Chris Wrena3446562014-06-03 18:11:47 -04002885 }
2886
John Spurlock7b414672014-07-18 13:02:39 -04002887 private static AudioAttributes audioAttributesForNotification(Notification n) {
Marco Nelissen1c066302014-11-18 10:48:12 -08002888 if (n.audioAttributes != null
2889 && !Notification.AUDIO_ATTRIBUTES_DEFAULT.equals(n.audioAttributes)) {
2890 // the audio attributes are set and different from the default, use them
John Spurlockbfa5dc42014-07-28 23:30:45 -04002891 return n.audioAttributes;
Jean-Michel Triviceb79bc2014-09-05 11:09:14 -07002892 } else if (n.audioStreamType >= 0 && n.audioStreamType < AudioSystem.getNumStreamTypes()) {
2893 // the stream type is valid, use it
2894 return new AudioAttributes.Builder()
2895 .setInternalLegacyStreamType(n.audioStreamType)
2896 .build();
2897 } else if (n.audioStreamType == AudioSystem.STREAM_DEFAULT) {
2898 return Notification.AUDIO_ATTRIBUTES_DEFAULT;
2899 } else {
2900 Log.w(TAG, String.format("Invalid stream type: %d", n.audioStreamType));
2901 return Notification.AUDIO_ATTRIBUTES_DEFAULT;
John Spurlockbfa5dc42014-07-28 23:30:45 -04002902 }
John Spurlock7b414672014-07-18 13:02:39 -04002903 }
2904
Adam Lesinski182f73f2013-12-05 16:48:06 -08002905 void showNextToastLocked() {
2906 ToastRecord record = mToastQueue.get(0);
2907 while (record != null) {
2908 if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback);
2909 try {
2910 record.callback.show();
2911 scheduleTimeoutLocked(record);
2912 return;
2913 } catch (RemoteException e) {
2914 Slog.w(TAG, "Object died trying to show notification " + record.callback
2915 + " in package " + record.pkg);
2916 // remove it from the list and let the process die
2917 int index = mToastQueue.indexOf(record);
2918 if (index >= 0) {
2919 mToastQueue.remove(index);
2920 }
2921 keepProcessAliveLocked(record.pid);
2922 if (mToastQueue.size() > 0) {
2923 record = mToastQueue.get(0);
2924 } else {
2925 record = null;
2926 }
2927 }
2928 }
2929 }
2930
2931 void cancelToastLocked(int index) {
2932 ToastRecord record = mToastQueue.get(index);
2933 try {
2934 record.callback.hide();
2935 } catch (RemoteException e) {
2936 Slog.w(TAG, "Object died trying to hide notification " + record.callback
2937 + " in package " + record.pkg);
2938 // don't worry about this, we're about to remove it from
2939 // the list anyway
2940 }
2941 mToastQueue.remove(index);
2942 keepProcessAliveLocked(record.pid);
2943 if (mToastQueue.size() > 0) {
2944 // Show the next one. If the callback fails, this will remove
2945 // it from the list, so don't assume that the list hasn't changed
2946 // after this point.
2947 showNextToastLocked();
2948 }
2949 }
2950
2951 private void scheduleTimeoutLocked(ToastRecord r)
2952 {
2953 mHandler.removeCallbacksAndMessages(r);
2954 Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r);
2955 long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY;
2956 mHandler.sendMessageDelayed(m, delay);
2957 }
2958
2959 private void handleTimeout(ToastRecord record)
2960 {
2961 if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback);
2962 synchronized (mToastQueue) {
2963 int index = indexOfToastLocked(record.pkg, record.callback);
2964 if (index >= 0) {
2965 cancelToastLocked(index);
2966 }
2967 }
2968 }
2969
2970 // lock on mToastQueue
2971 int indexOfToastLocked(String pkg, ITransientNotification callback)
2972 {
2973 IBinder cbak = callback.asBinder();
2974 ArrayList<ToastRecord> list = mToastQueue;
2975 int len = list.size();
2976 for (int i=0; i<len; i++) {
2977 ToastRecord r = list.get(i);
2978 if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) {
2979 return i;
2980 }
2981 }
2982 return -1;
2983 }
2984
2985 // lock on mToastQueue
2986 void keepProcessAliveLocked(int pid)
2987 {
2988 int toastCount = 0; // toasts from this pid
2989 ArrayList<ToastRecord> list = mToastQueue;
2990 int N = list.size();
2991 for (int i=0; i<N; i++) {
2992 ToastRecord r = list.get(i);
2993 if (r.pid == pid) {
2994 toastCount++;
2995 }
2996 }
2997 try {
2998 mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0);
2999 } catch (RemoteException e) {
3000 // Shouldn't happen.
3001 }
3002 }
3003
Chris Wrenf9536642014-04-17 10:01:54 -04003004 private void handleRankingReconsideration(Message message) {
Chris Wren470c1ac2014-05-21 15:28:10 -04003005 if (!(message.obj instanceof RankingReconsideration)) return;
3006 RankingReconsideration recon = (RankingReconsideration) message.obj;
3007 recon.run();
Chris Wren333a61c2014-05-28 16:40:57 -04003008 boolean changed;
Chris Wren470c1ac2014-05-21 15:28:10 -04003009 synchronized (mNotificationList) {
3010 final NotificationRecord record = mNotificationsByKey.get(recon.getKey());
3011 if (record == null) {
3012 return;
Chris Wrenf9536642014-04-17 10:01:54 -04003013 }
Chris Wren333a61c2014-05-28 16:40:57 -04003014 int indexBefore = findNotificationRecordIndexLocked(record);
3015 boolean interceptBefore = record.isIntercepted();
Chris Wren3ad4e3a2014-09-02 17:23:51 -04003016 int visibilityBefore = record.getPackageVisibilityOverride();
Chris Wren470c1ac2014-05-21 15:28:10 -04003017 recon.applyChangesLocked(record);
Chris Wren333a61c2014-05-28 16:40:57 -04003018 applyZenModeLocked(record);
Chris Wren54bbef42014-07-09 18:37:56 -04003019 mRankingHelper.sort(mNotificationList);
Chris Wren333a61c2014-05-28 16:40:57 -04003020 int indexAfter = findNotificationRecordIndexLocked(record);
3021 boolean interceptAfter = record.isIntercepted();
Chris Wren3ad4e3a2014-09-02 17:23:51 -04003022 int visibilityAfter = record.getPackageVisibilityOverride();
3023 changed = indexBefore != indexAfter || interceptBefore != interceptAfter
3024 || visibilityBefore != visibilityAfter;
Chris Wrena3446562014-06-03 18:11:47 -04003025 if (interceptBefore && !interceptAfter) {
3026 buzzBeepBlinkLocked(record);
3027 }
Chris Wrenf9536642014-04-17 10:01:54 -04003028 }
Chris Wren333a61c2014-05-28 16:40:57 -04003029 if (changed) {
Chris Wren470c1ac2014-05-21 15:28:10 -04003030 scheduleSendRankingUpdate();
3031 }
3032 }
3033
Chris Wren51017d02015-12-15 15:34:46 -05003034 private void handleRankingSort() {
Chris Wren54bbef42014-07-09 18:37:56 -04003035 synchronized (mNotificationList) {
3036 final int N = mNotificationList.size();
3037 ArrayList<String> orderBefore = new ArrayList<String>(N);
Julia Reynoldse46bb372016-03-17 11:05:58 -04003038 ArrayList<String> groupOverrideBefore = new ArrayList<>(N);
Chris Wren3ad4e3a2014-09-02 17:23:51 -04003039 int[] visibilities = new int[N];
Julia Reynoldse46bb372016-03-17 11:05:58 -04003040 int[] importances = new int[N];
Chris Wren54bbef42014-07-09 18:37:56 -04003041 for (int i = 0; i < N; i++) {
3042 final NotificationRecord r = mNotificationList.get(i);
3043 orderBefore.add(r.getKey());
Julia Reynoldse46bb372016-03-17 11:05:58 -04003044 groupOverrideBefore.add(r.sbn.getGroupKey());
Chris Wren3ad4e3a2014-09-02 17:23:51 -04003045 visibilities[i] = r.getPackageVisibilityOverride();
Julia Reynolds69766692016-02-01 15:35:08 -05003046 importances[i] = r.getImportance();
Chris Wren54bbef42014-07-09 18:37:56 -04003047 mRankingHelper.extractSignals(r);
3048 }
Chris Wren19a02b02015-12-22 10:34:22 -05003049 mRankingHelper.sort(mNotificationList);
Chris Wren54bbef42014-07-09 18:37:56 -04003050 for (int i = 0; i < N; i++) {
Chris Wren3ad4e3a2014-09-02 17:23:51 -04003051 final NotificationRecord r = mNotificationList.get(i);
3052 if (!orderBefore.get(i).equals(r.getKey())
Julia Reynolds69766692016-02-01 15:35:08 -05003053 || visibilities[i] != r.getPackageVisibilityOverride()
Julia Reynoldse46bb372016-03-17 11:05:58 -04003054 || importances[i] != r.getImportance()
3055 || !groupOverrideBefore.get(i).equals(r.sbn.getGroupKey())) {
Chris Wren54bbef42014-07-09 18:37:56 -04003056 scheduleSendRankingUpdate();
3057 return;
3058 }
3059 }
3060 }
3061 }
3062
Christoph Studerd5092bc2014-07-03 17:47:58 +02003063 // let zen mode evaluate this record
Chris Wren333a61c2014-05-28 16:40:57 -04003064 private void applyZenModeLocked(NotificationRecord record) {
Christoph Studerd5092bc2014-07-03 17:47:58 +02003065 record.setIntercepted(mZenModeHelper.shouldIntercept(record));
Julia Reynoldsf612869ae2015-11-05 16:48:55 -05003066 if (record.isIntercepted()) {
Julia Reynoldsd5607292016-02-05 15:25:58 -05003067 int suppressed = (mZenModeHelper.shouldSuppressWhenScreenOff()
3068 ? SUPPRESSED_EFFECT_SCREEN_OFF : 0)
3069 | (mZenModeHelper.shouldSuppressWhenScreenOn()
3070 ? SUPPRESSED_EFFECT_SCREEN_ON : 0);
Julia Reynoldsf612869ae2015-11-05 16:48:55 -05003071 record.setSuppressedVisualEffects(suppressed);
3072 }
Chris Wren333a61c2014-05-28 16:40:57 -04003073 }
3074
Chris Wren470c1ac2014-05-21 15:28:10 -04003075 // lock on mNotificationList
3076 private int findNotificationRecordIndexLocked(NotificationRecord target) {
Chris Wren54bbef42014-07-09 18:37:56 -04003077 return mRankingHelper.indexOf(mNotificationList, target);
Chris Wrenf9536642014-04-17 10:01:54 -04003078 }
3079
3080 private void scheduleSendRankingUpdate() {
Chris Wren52020492016-04-06 11:12:02 -04003081 if (!mHandler.hasMessages(MESSAGE_SEND_RANKING_UPDATE)) {
3082 Message m = Message.obtain(mHandler, MESSAGE_SEND_RANKING_UPDATE);
3083 mHandler.sendMessage(m);
3084 }
Chris Wrenf9536642014-04-17 10:01:54 -04003085 }
3086
3087 private void handleSendRankingUpdate() {
3088 synchronized (mNotificationList) {
Chris Wren333a61c2014-05-28 16:40:57 -04003089 mListeners.notifyRankingUpdateLocked();
Chris Wrenf9536642014-04-17 10:01:54 -04003090 }
3091 }
3092
John Spurlockd8afe3c2014-08-01 14:04:07 -04003093 private void scheduleListenerHintsChanged(int state) {
3094 mHandler.removeMessages(MESSAGE_LISTENER_HINTS_CHANGED);
3095 mHandler.obtainMessage(MESSAGE_LISTENER_HINTS_CHANGED, state, 0).sendToTarget();
John Spurlock1fa865f2014-07-21 14:56:39 -04003096 }
3097
Christoph Studer85a384b2014-08-27 20:16:15 +02003098 private void scheduleInterruptionFilterChanged(int listenerInterruptionFilter) {
3099 mHandler.removeMessages(MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED);
3100 mHandler.obtainMessage(
3101 MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED,
3102 listenerInterruptionFilter,
3103 0).sendToTarget();
3104 }
3105
John Spurlockd8afe3c2014-08-01 14:04:07 -04003106 private void handleListenerHintsChanged(int hints) {
John Spurlock1fa865f2014-07-21 14:56:39 -04003107 synchronized (mNotificationList) {
John Spurlockd8afe3c2014-08-01 14:04:07 -04003108 mListeners.notifyListenerHintsChangedLocked(hints);
John Spurlock1fa865f2014-07-21 14:56:39 -04003109 }
3110 }
3111
Christoph Studer85a384b2014-08-27 20:16:15 +02003112 private void handleListenerInterruptionFilterChanged(int interruptionFilter) {
3113 synchronized (mNotificationList) {
3114 mListeners.notifyInterruptionFilterChanged(interruptionFilter);
3115 }
3116 }
3117
Adam Lesinski182f73f2013-12-05 16:48:06 -08003118 private final class WorkerHandler extends Handler
3119 {
3120 @Override
3121 public void handleMessage(Message msg)
3122 {
3123 switch (msg.what)
3124 {
3125 case MESSAGE_TIMEOUT:
3126 handleTimeout((ToastRecord)msg.obj);
3127 break;
John Spurlock056c5192014-04-20 21:52:01 -04003128 case MESSAGE_SAVE_POLICY_FILE:
3129 handleSavePolicyFile();
3130 break;
Chris Wrenf9536642014-04-17 10:01:54 -04003131 case MESSAGE_SEND_RANKING_UPDATE:
3132 handleSendRankingUpdate();
3133 break;
John Spurlockd8afe3c2014-08-01 14:04:07 -04003134 case MESSAGE_LISTENER_HINTS_CHANGED:
3135 handleListenerHintsChanged(msg.arg1);
John Spurlock1fa865f2014-07-21 14:56:39 -04003136 break;
Christoph Studer85a384b2014-08-27 20:16:15 +02003137 case MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED:
3138 handleListenerInterruptionFilterChanged(msg.arg1);
3139 break;
Chris Wrenf9536642014-04-17 10:01:54 -04003140 }
3141 }
3142
3143 }
3144
Chris Wren51017d02015-12-15 15:34:46 -05003145 private final class RankingHandlerWorker extends Handler implements RankingHandler
Chris Wrenf9536642014-04-17 10:01:54 -04003146 {
Chris Wren51017d02015-12-15 15:34:46 -05003147 public RankingHandlerWorker(Looper looper) {
Chris Wrenf9536642014-04-17 10:01:54 -04003148 super(looper);
3149 }
3150
3151 @Override
3152 public void handleMessage(Message msg) {
3153 switch (msg.what) {
3154 case MESSAGE_RECONSIDER_RANKING:
3155 handleRankingReconsideration(msg);
3156 break;
Chris Wren51017d02015-12-15 15:34:46 -05003157 case MESSAGE_RANKING_SORT:
3158 handleRankingSort();
Chris Wren54bbef42014-07-09 18:37:56 -04003159 break;
Adam Lesinski182f73f2013-12-05 16:48:06 -08003160 }
3161 }
Chris Wren51017d02015-12-15 15:34:46 -05003162
3163 public void requestSort() {
3164 removeMessages(MESSAGE_RANKING_SORT);
3165 sendEmptyMessage(MESSAGE_RANKING_SORT);
3166 }
3167
3168 public void requestReconsideration(RankingReconsideration recon) {
3169 Message m = Message.obtain(this,
3170 NotificationManagerService.MESSAGE_RECONSIDER_RANKING, recon);
3171 long delay = recon.getDelay(TimeUnit.MILLISECONDS);
3172 sendMessageDelayed(m, delay);
3173 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08003174 }
3175
Adam Lesinski182f73f2013-12-05 16:48:06 -08003176 // Notifications
3177 // ============================================================================
3178 static int clamp(int x, int low, int high) {
3179 return (x < low) ? low : ((x > high) ? high : x);
3180 }
3181
3182 void sendAccessibilityEvent(Notification notification, CharSequence packageName) {
3183 AccessibilityManager manager = AccessibilityManager.getInstance(getContext());
svetoslavganov75986cf2009-05-14 22:28:01 -07003184 if (!manager.isEnabled()) {
3185 return;
3186 }
3187
3188 AccessibilityEvent event =
3189 AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
3190 event.setPackageName(packageName);
3191 event.setClassName(Notification.class.getName());
3192 event.setParcelableData(notification);
3193 CharSequence tickerText = notification.tickerText;
3194 if (!TextUtils.isEmpty(tickerText)) {
3195 event.getText().add(tickerText);
3196 }
3197
3198 manager.sendAccessibilityEvent(event);
3199 }
3200
Christoph Studer546bec82014-03-14 12:17:12 +01003201 private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason) {
Joe Onorato46439ce2010-11-19 13:56:21 -08003202 // tell the app
3203 if (sendDelete) {
Daniel Sandlerfde19b12013-01-17 00:21:05 -05003204 if (r.getNotification().deleteIntent != null) {
Joe Onorato46439ce2010-11-19 13:56:21 -08003205 try {
Daniel Sandlerfde19b12013-01-17 00:21:05 -05003206 r.getNotification().deleteIntent.send();
Joe Onorato46439ce2010-11-19 13:56:21 -08003207 } catch (PendingIntent.CanceledException ex) {
3208 // do nothing - there's no relevant way to recover, and
3209 // no reason to let this propagate
Daniel Sandler4f91efd2013-04-25 16:38:41 -04003210 Slog.w(TAG, "canceled PendingIntent for " + r.sbn.getPackageName(), ex);
Joe Onorato46439ce2010-11-19 13:56:21 -08003211 }
3212 }
3213 }
3214
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003215 // status bar
Dan Sandlerd63f9322015-05-06 15:18:49 -04003216 if (r.getNotification().getSmallIcon() != null) {
Christoph Studer71f18fd2014-05-20 17:02:04 +02003217 r.isCanceled = true;
Chris Wren333a61c2014-05-28 16:40:57 -04003218 mListeners.notifyRemovedLocked(r.sbn);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003219 }
3220
Chris Wren6054e612014-11-25 17:16:46 -05003221 final String canceledKey = r.getKey();
3222
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003223 // sound
Chris Wren6054e612014-11-25 17:16:46 -05003224 if (canceledKey.equals(mSoundNotificationKey)) {
3225 mSoundNotificationKey = null;
Jeff Sharkey098d5802012-04-26 17:30:34 -07003226 final long identity = Binder.clearCallingIdentity();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003227 try {
Adam Lesinski182f73f2013-12-05 16:48:06 -08003228 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
Jeff Sharkey098d5802012-04-26 17:30:34 -07003229 if (player != null) {
3230 player.stopAsync();
3231 }
3232 } catch (RemoteException e) {
3233 } finally {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003234 Binder.restoreCallingIdentity(identity);
3235 }
3236 }
3237
3238 // vibrate
Chris Wren6054e612014-11-25 17:16:46 -05003239 if (canceledKey.equals(mVibrateNotificationKey)) {
3240 mVibrateNotificationKey = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003241 long identity = Binder.clearCallingIdentity();
3242 try {
3243 mVibrator.cancel();
3244 }
3245 finally {
3246 Binder.restoreCallingIdentity(identity);
3247 }
3248 }
3249
3250 // light
Chris Wren6054e612014-11-25 17:16:46 -05003251 mLights.remove(canceledKey);
Daniel Sandler23d7c702013-03-07 16:32:06 -05003252
Christoph Studer546bec82014-03-14 12:17:12 +01003253 // Record usage stats
Julia Reynoldse46bb372016-03-17 11:05:58 -04003254 // TODO: add unbundling stats?
Christoph Studer546bec82014-03-14 12:17:12 +01003255 switch (reason) {
3256 case REASON_DELEGATE_CANCEL:
3257 case REASON_DELEGATE_CANCEL_ALL:
3258 case REASON_LISTENER_CANCEL:
3259 case REASON_LISTENER_CANCEL_ALL:
3260 mUsageStats.registerDismissedByUser(r);
3261 break;
Chris Wren9fa689f2015-11-20 16:44:53 -05003262 case REASON_APP_CANCEL:
3263 case REASON_APP_CANCEL_ALL:
Christoph Studer546bec82014-03-14 12:17:12 +01003264 mUsageStats.registerRemovedByApp(r);
3265 break;
Christoph Studer546bec82014-03-14 12:17:12 +01003266 }
3267
Christoph Studercef37cf2014-07-25 14:18:17 +02003268 mNotificationsByKey.remove(r.sbn.getKey());
Christoph Studer265c1052014-07-23 17:14:33 +02003269 String groupKey = r.getGroupKey();
3270 NotificationRecord groupSummary = mSummaryByGroupKey.get(groupKey);
3271 if (groupSummary != null && groupSummary.getKey().equals(r.getKey())) {
3272 mSummaryByGroupKey.remove(groupKey);
3273 }
Julia Reynoldseae43fb2016-05-09 12:42:58 -04003274 final ArrayMap<String, String> summaries = mAutobundledSummaries.get(r.sbn.getUserId());
3275 if (summaries != null && r.sbn.getKey().equals(summaries.get(r.sbn.getPackageName()))) {
3276 summaries.remove(r.sbn.getPackageName());
Julia Reynoldse46bb372016-03-17 11:05:58 -04003277 }
Christoph Studercef37cf2014-07-25 14:18:17 +02003278
Daniel Sandler23d7c702013-03-07 16:32:06 -05003279 // Save it for users of getHistoricalNotifications()
3280 mArchive.record(r.sbn);
Christoph Studer81e5b5f2014-10-22 17:19:56 +02003281
Chris Wren6650e572015-05-15 17:19:25 -04003282 final long now = System.currentTimeMillis();
Chris Wrene6ddb8a2015-05-27 15:21:00 -04003283 EventLogTags.writeNotificationCanceled(canceledKey, reason,
3284 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003285 }
3286
3287 /**
Dianne Hackbornd8a43f62009-08-17 23:33:56 -07003288 * Cancels a notification ONLY if it has all of the {@code mustHaveFlags}
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003289 * and none of the {@code mustNotHaveFlags}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003290 */
John Spurlocke6a7d932014-03-13 12:29:00 -04003291 void cancelNotification(final int callingUid, final int callingPid,
3292 final String pkg, final String tag, final int id,
Svetoslav Ganov835835e2013-08-04 20:17:52 -07003293 final int mustHaveFlags, final int mustNotHaveFlags, final boolean sendDelete,
John Spurlock7340fc82014-04-24 18:50:12 -04003294 final int userId, final int reason, final ManagedServiceInfo listener) {
Svetoslav Ganov835835e2013-08-04 20:17:52 -07003295 // In enqueueNotificationInternal notifications are added by scheduling the
3296 // work on the worker handler. Hence, we also schedule the cancel on this
3297 // handler to avoid a scenario where an add notification call followed by a
3298 // remove notification call ends up in not removing the notification.
3299 mHandler.post(new Runnable() {
3300 @Override
3301 public void run() {
Christoph Studere4ef156b2014-07-04 18:41:57 +02003302 String listenerName = listener == null ? null : listener.component.toShortString();
Chris Wrenbddb5bc2015-03-04 08:47:46 -08003303 if (DBG) EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, id, tag,
3304 userId, mustHaveFlags, mustNotHaveFlags, reason, listenerName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003305
Svetoslav Ganov835835e2013-08-04 20:17:52 -07003306 synchronized (mNotificationList) {
3307 int index = indexOfNotificationLocked(pkg, tag, id, userId);
3308 if (index >= 0) {
3309 NotificationRecord r = mNotificationList.get(index);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003310
Christoph Studer546bec82014-03-14 12:17:12 +01003311 // Ideally we'd do this in the caller of this method. However, that would
3312 // require the caller to also find the notification.
3313 if (reason == REASON_DELEGATE_CLICK) {
3314 mUsageStats.registerClickedByUser(r);
3315 }
3316
Svetoslav Ganov835835e2013-08-04 20:17:52 -07003317 if ((r.getNotification().flags & mustHaveFlags) != mustHaveFlags) {
3318 return;
3319 }
3320 if ((r.getNotification().flags & mustNotHaveFlags) != 0) {
3321 return;
3322 }
3323
3324 mNotificationList.remove(index);
3325
Selim Cinek3f19f602016-05-02 18:01:56 -07003326 cancelNotificationLocked(r, sendDelete, reason);
Christoph Studer265c1052014-07-23 17:14:33 +02003327 cancelGroupChildrenLocked(r, callingUid, callingPid, listenerName,
3328 REASON_GROUP_SUMMARY_CANCELED);
Svetoslav Ganov835835e2013-08-04 20:17:52 -07003329 updateLightsLocked();
3330 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003331 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003332 }
Svetoslav Ganov835835e2013-08-04 20:17:52 -07003333 });
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003334 }
3335
3336 /**
Daniel Sandler321e9c52012-10-12 10:59:26 -07003337 * Determine whether the userId applies to the notification in question, either because
3338 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard).
3339 */
3340 private boolean notificationMatchesUserId(NotificationRecord r, int userId) {
3341 return
3342 // looking for USER_ALL notifications? match everything
3343 userId == UserHandle.USER_ALL
3344 // a notification sent to USER_ALL matches any query
Daniel Sandlerfde19b12013-01-17 00:21:05 -05003345 || r.getUserId() == UserHandle.USER_ALL
Daniel Sandler321e9c52012-10-12 10:59:26 -07003346 // an exact user match
Daniel Sandlerfde19b12013-01-17 00:21:05 -05003347 || r.getUserId() == userId;
Daniel Sandler321e9c52012-10-12 10:59:26 -07003348 }
3349
3350 /**
Kenny Guy3a7c4a52014-03-03 18:24:03 +00003351 * Determine whether the userId applies to the notification in question, either because
3352 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard) or
Kenny Guy2a764942014-04-02 13:29:20 +01003353 * because it matches one of the users profiles.
Kenny Guy3a7c4a52014-03-03 18:24:03 +00003354 */
Kenny Guy2a764942014-04-02 13:29:20 +01003355 private boolean notificationMatchesCurrentProfiles(NotificationRecord r, int userId) {
Kenny Guya263e4e2014-03-03 18:24:03 +00003356 return notificationMatchesUserId(r, userId)
John Spurlockb408e8e2014-04-23 21:12:45 -04003357 || mUserProfiles.isCurrentProfile(r.getUserId());
Kenny Guy3a7c4a52014-03-03 18:24:03 +00003358 }
3359
3360 /**
Julia Reynoldsef37f282016-02-12 09:11:27 -05003361 * Cancels all notifications from a given package that have all of the
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003362 * {@code mustHaveFlags}.
3363 */
John Spurlocke6a7d932014-03-13 12:29:00 -04003364 boolean cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, int mustHaveFlags,
Julia Reynoldsef37f282016-02-12 09:11:27 -05003365 int mustNotHaveFlags, boolean doit, int userId, int reason,
John Spurlock7340fc82014-04-24 18:50:12 -04003366 ManagedServiceInfo listener) {
Christoph Studere4ef156b2014-07-04 18:41:57 +02003367 String listenerName = listener == null ? null : listener.component.toShortString();
John Spurlocke6a7d932014-03-13 12:29:00 -04003368 EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
3369 pkg, userId, mustHaveFlags, mustNotHaveFlags, reason,
Christoph Studere4ef156b2014-07-04 18:41:57 +02003370 listenerName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003371
3372 synchronized (mNotificationList) {
3373 final int N = mNotificationList.size();
Christoph Studere4ef156b2014-07-04 18:41:57 +02003374 ArrayList<NotificationRecord> canceledNotifications = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003375 for (int i = N-1; i >= 0; --i) {
3376 NotificationRecord r = mNotificationList.get(i);
Daniel Sandler321e9c52012-10-12 10:59:26 -07003377 if (!notificationMatchesUserId(r, userId)) {
Dianne Hackborn41203752012-08-31 14:05:51 -07003378 continue;
3379 }
Amith Yamasani5ec00e92012-11-07 16:58:30 -08003380 // Don't remove notifications to all, if there's no package name specified
Daniel Sandlerfde19b12013-01-17 00:21:05 -05003381 if (r.getUserId() == UserHandle.USER_ALL && pkg == null) {
Amith Yamasani5ec00e92012-11-07 16:58:30 -08003382 continue;
3383 }
Daniel Sandlerfde19b12013-01-17 00:21:05 -05003384 if ((r.getFlags() & mustHaveFlags) != mustHaveFlags) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003385 continue;
3386 }
Daniel Sandlerfde19b12013-01-17 00:21:05 -05003387 if ((r.getFlags() & mustNotHaveFlags) != 0) {
Dianne Hackbornd8a43f62009-08-17 23:33:56 -07003388 continue;
3389 }
Daniel Sandler4f91efd2013-04-25 16:38:41 -04003390 if (pkg != null && !r.sbn.getPackageName().equals(pkg)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003391 continue;
3392 }
Christoph Studere4ef156b2014-07-04 18:41:57 +02003393 if (canceledNotifications == null) {
3394 canceledNotifications = new ArrayList<>();
3395 }
3396 canceledNotifications.add(r);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08003397 if (!doit) {
3398 return true;
3399 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003400 mNotificationList.remove(i);
Christoph Studer546bec82014-03-14 12:17:12 +01003401 cancelNotificationLocked(r, false, reason);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003402 }
Christoph Studere4ef156b2014-07-04 18:41:57 +02003403 if (doit && canceledNotifications != null) {
3404 final int M = canceledNotifications.size();
3405 for (int i = 0; i < M; i++) {
3406 cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid,
Christoph Studer265c1052014-07-23 17:14:33 +02003407 listenerName, REASON_GROUP_SUMMARY_CANCELED);
Christoph Studere4ef156b2014-07-04 18:41:57 +02003408 }
3409 }
3410 if (canceledNotifications != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003411 updateLightsLocked();
3412 }
Christoph Studere4ef156b2014-07-04 18:41:57 +02003413 return canceledNotifications != null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003414 }
3415 }
3416
Adam Lesinski350159c2014-03-27 11:15:11 -07003417 void cancelAllLocked(int callingUid, int callingPid, int userId, int reason,
John Spurlock7340fc82014-04-24 18:50:12 -04003418 ManagedServiceInfo listener, boolean includeCurrentProfiles) {
Christoph Studere4ef156b2014-07-04 18:41:57 +02003419 String listenerName = listener == null ? null : listener.component.toShortString();
John Spurlocke6a7d932014-03-13 12:29:00 -04003420 EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
Christoph Studere4ef156b2014-07-04 18:41:57 +02003421 null, userId, 0, 0, reason, listenerName);
Christoph Studer546bec82014-03-14 12:17:12 +01003422
Christoph Studere4ef156b2014-07-04 18:41:57 +02003423 ArrayList<NotificationRecord> canceledNotifications = null;
Adam Lesinskie8240262014-03-26 16:01:00 -07003424 final int N = mNotificationList.size();
3425 for (int i=N-1; i>=0; i--) {
3426 NotificationRecord r = mNotificationList.get(i);
Kenny Guya263e4e2014-03-03 18:24:03 +00003427 if (includeCurrentProfiles) {
3428 if (!notificationMatchesCurrentProfiles(r, userId)) {
3429 continue;
3430 }
3431 } else {
3432 if (!notificationMatchesUserId(r, userId)) {
3433 continue;
3434 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003435 }
3436
Adam Lesinskie8240262014-03-26 16:01:00 -07003437 if ((r.getFlags() & (Notification.FLAG_ONGOING_EVENT
3438 | Notification.FLAG_NO_CLEAR)) == 0) {
3439 mNotificationList.remove(i);
Christoph Studer546bec82014-03-14 12:17:12 +01003440 cancelNotificationLocked(r, true, reason);
Christoph Studere4ef156b2014-07-04 18:41:57 +02003441 // Make a note so we can cancel children later.
3442 if (canceledNotifications == null) {
3443 canceledNotifications = new ArrayList<>();
3444 }
3445 canceledNotifications.add(r);
Adam Lesinskie8240262014-03-26 16:01:00 -07003446 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003447 }
Christoph Studere4ef156b2014-07-04 18:41:57 +02003448 int M = canceledNotifications != null ? canceledNotifications.size() : 0;
3449 for (int i = 0; i < M; i++) {
3450 cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid,
Christoph Studer265c1052014-07-23 17:14:33 +02003451 listenerName, REASON_GROUP_SUMMARY_CANCELED);
Christoph Studere4ef156b2014-07-04 18:41:57 +02003452 }
Adam Lesinskie8240262014-03-26 16:01:00 -07003453 updateLightsLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003454 }
3455
Christoph Studere4ef156b2014-07-04 18:41:57 +02003456 // Warning: The caller is responsible for invoking updateLightsLocked().
3457 private void cancelGroupChildrenLocked(NotificationRecord r, int callingUid, int callingPid,
Christoph Studer265c1052014-07-23 17:14:33 +02003458 String listenerName, int reason) {
Christoph Studere4ef156b2014-07-04 18:41:57 +02003459 Notification n = r.getNotification();
Christoph Studer3f31f5d2014-07-31 16:55:32 +02003460 if (!n.isGroupSummary()) {
Christoph Studere4ef156b2014-07-04 18:41:57 +02003461 return;
3462 }
3463
3464 String pkg = r.sbn.getPackageName();
3465 int userId = r.getUserId();
3466
3467 if (pkg == null) {
3468 if (DBG) Log.e(TAG, "No package for group summary: " + r.getKey());
3469 return;
3470 }
3471
3472 final int N = mNotificationList.size();
3473 for (int i = N - 1; i >= 0; i--) {
3474 NotificationRecord childR = mNotificationList.get(i);
Christoph Studere4ef156b2014-07-04 18:41:57 +02003475 StatusBarNotification childSbn = childR.sbn;
Julia Reynoldse46bb372016-03-17 11:05:58 -04003476 if ((childSbn.isGroup() && !childSbn.getNotification().isGroupSummary()) &&
Christoph Studerc44caa92014-08-22 19:16:00 +02003477 childR.getGroupKey().equals(r.getGroupKey())) {
Christoph Studer265c1052014-07-23 17:14:33 +02003478 EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, childSbn.getId(),
3479 childSbn.getTag(), userId, 0, 0, reason, listenerName);
Christoph Studere4ef156b2014-07-04 18:41:57 +02003480 mNotificationList.remove(i);
Christoph Studer265c1052014-07-23 17:14:33 +02003481 cancelNotificationLocked(childR, false, reason);
Christoph Studere4ef156b2014-07-04 18:41:57 +02003482 }
3483 }
3484 }
3485
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003486 // lock on mNotificationList
Adam Lesinski182f73f2013-12-05 16:48:06 -08003487 void updateLightsLocked()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003488 {
The Android Open Source Project10592532009-03-18 17:39:46 -07003489 // handle notification lights
Chris Wren6054e612014-11-25 17:16:46 -05003490 NotificationRecord ledNotification = null;
3491 while (ledNotification == null && !mLights.isEmpty()) {
3492 final String owner = mLights.get(mLights.size() - 1);
3493 ledNotification = mNotificationsByKey.get(owner);
3494 if (ledNotification == null) {
3495 Slog.wtfStack(TAG, "LED Notification does not exist: " + owner);
3496 mLights.remove(owner);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003497 }
3498 }
Mike Lockwoodc22404a2009-12-02 11:15:02 -05003499
Mike Lockwood63b5ad92011-08-30 09:55:30 -04003500 // Don't flash while we are in a call or screen is on
Chris Wren6054e612014-11-25 17:16:46 -05003501 if (ledNotification == null || mInCall || mScreenOn) {
Mike Lockwood3cb67a32009-11-27 14:25:58 -05003502 mNotificationLight.turnOff();
Wei Liu97e56662016-03-04 10:52:33 -08003503 if (mStatusBar != null) {
3504 mStatusBar.notificationLightOff();
3505 }
The Android Open Source Project10592532009-03-18 17:39:46 -07003506 } else {
Chris Wren6054e612014-11-25 17:16:46 -05003507 final Notification ledno = ledNotification.sbn.getNotification();
Daniel Sandlerfde19b12013-01-17 00:21:05 -05003508 int ledARGB = ledno.ledARGB;
3509 int ledOnMS = ledno.ledOnMS;
3510 int ledOffMS = ledno.ledOffMS;
3511 if ((ledno.defaults & Notification.DEFAULT_LIGHTS) != 0) {
Mike Lockwood670f9322010-01-20 12:13:36 -05003512 ledARGB = mDefaultNotificationColor;
3513 ledOnMS = mDefaultNotificationLedOn;
3514 ledOffMS = mDefaultNotificationLedOff;
3515 }
3516 if (mNotificationPulseEnabled) {
3517 // pulse repeatedly
Adam Lesinski182f73f2013-12-05 16:48:06 -08003518 mNotificationLight.setFlashing(ledARGB, Light.LIGHT_FLASH_TIMED,
Mike Lockwood670f9322010-01-20 12:13:36 -05003519 ledOnMS, ledOffMS);
Mike Lockwood670f9322010-01-20 12:13:36 -05003520 }
Wei Liu97e56662016-03-04 10:52:33 -08003521 if (mStatusBar != null) {
3522 // let SystemUI make an independent decision
3523 mStatusBar.notificationLightPulse(ledARGB, ledOnMS, ledOffMS);
3524 }
The Android Open Source Project10592532009-03-18 17:39:46 -07003525 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003526 }
3527
3528 // lock on mNotificationList
Adam Lesinski182f73f2013-12-05 16:48:06 -08003529 int indexOfNotificationLocked(String pkg, String tag, int id, int userId)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003530 {
3531 ArrayList<NotificationRecord> list = mNotificationList;
3532 final int len = list.size();
3533 for (int i=0; i<len; i++) {
3534 NotificationRecord r = list.get(i);
Vladimir Marko2526f332013-09-11 11:13:55 +01003535 if (notificationMatchesUserId(r, userId) && r.sbn.getId() == id &&
3536 TextUtils.equals(r.sbn.getTag(), tag) && r.sbn.getPackageName().equals(pkg)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003537 return i;
3538 }
3539 }
3540 return -1;
3541 }
3542
Christoph Studer71f18fd2014-05-20 17:02:04 +02003543 // lock on mNotificationList
3544 int indexOfNotificationLocked(String key) {
Christoph Studerc5115552014-06-12 20:22:31 +02003545 final int N = mNotificationList.size();
3546 for (int i = 0; i < N; i++) {
3547 if (key.equals(mNotificationList.get(i).getKey())) {
3548 return i;
3549 }
Christoph Studer71f18fd2014-05-20 17:02:04 +02003550 }
Christoph Studerc5115552014-06-12 20:22:31 +02003551 return -1;
Christoph Studer71f18fd2014-05-20 17:02:04 +02003552 }
3553
Mike Lockwoodc22404a2009-12-02 11:15:02 -05003554 private void updateNotificationPulse() {
3555 synchronized (mNotificationList) {
3556 updateLightsLocked();
3557 }
3558 }
John Spurlocke677d712014-02-13 12:52:19 -05003559
John Spurlock7340fc82014-04-24 18:50:12 -04003560 private static boolean isUidSystem(int uid) {
3561 final int appid = UserHandle.getAppId(uid);
3562 return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0);
3563 }
John Spurlockb408e8e2014-04-23 21:12:45 -04003564
John Spurlock7340fc82014-04-24 18:50:12 -04003565 private static boolean isCallerSystem() {
3566 return isUidSystem(Binder.getCallingUid());
3567 }
3568
3569 private static void checkCallerIsSystem() {
3570 if (isCallerSystem()) {
3571 return;
3572 }
3573 throw new SecurityException("Disallowed call for uid " + Binder.getCallingUid());
3574 }
3575
3576 private static void checkCallerIsSystemOrSameApp(String pkg) {
3577 if (isCallerSystem()) {
3578 return;
3579 }
3580 final int uid = Binder.getCallingUid();
3581 try {
3582 ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo(
3583 pkg, 0, UserHandle.getCallingUserId());
Dan Sandler09afc2e2014-07-18 14:29:20 -04003584 if (ai == null) {
3585 throw new SecurityException("Unknown package " + pkg);
3586 }
John Spurlock7340fc82014-04-24 18:50:12 -04003587 if (!UserHandle.isSameApp(ai.uid, uid)) {
3588 throw new SecurityException("Calling uid " + uid + " gave package"
3589 + pkg + " which is owned by uid " + ai.uid);
3590 }
3591 } catch (RemoteException re) {
3592 throw new SecurityException("Unknown package " + pkg + "\n" + re);
3593 }
3594 }
3595
John Spurlock32fe4c62014-10-02 12:16:02 -04003596 private static String callStateToString(int state) {
3597 switch (state) {
3598 case TelephonyManager.CALL_STATE_IDLE: return "CALL_STATE_IDLE";
3599 case TelephonyManager.CALL_STATE_RINGING: return "CALL_STATE_RINGING";
3600 case TelephonyManager.CALL_STATE_OFFHOOK: return "CALL_STATE_OFFHOOK";
3601 default: return "CALL_STATE_UNKNOWN_" + state;
3602 }
3603 }
3604
3605 private void listenForCallState() {
3606 TelephonyManager.from(getContext()).listen(new PhoneStateListener() {
3607 @Override
3608 public void onCallStateChanged(int state, String incomingNumber) {
3609 if (mCallState == state) return;
3610 if (DBG) Slog.d(TAG, "Call state changed: " + callStateToString(state));
3611 mCallState = state;
3612 }
3613 }, PhoneStateListener.LISTEN_CALL_STATE);
3614 }
3615
Christoph Studer05ad4822014-05-16 14:16:03 +02003616 /**
3617 * Generates a NotificationRankingUpdate from 'sbns', considering only
3618 * notifications visible to the given listener.
Chris Wren333a61c2014-05-28 16:40:57 -04003619 *
3620 * <p>Caller must hold a lock on mNotificationList.</p>
Christoph Studer05ad4822014-05-16 14:16:03 +02003621 */
Chris Wren333a61c2014-05-28 16:40:57 -04003622 private NotificationRankingUpdate makeRankingUpdateLocked(ManagedServiceInfo info) {
Chris Wren333a61c2014-05-28 16:40:57 -04003623 final int N = mNotificationList.size();
3624 ArrayList<String> keys = new ArrayList<String>(N);
Christoph Studer1d599da2014-06-12 15:25:59 +02003625 ArrayList<String> interceptedKeys = new ArrayList<String>(N);
Chris Wrenbdf33762015-12-04 15:50:51 -05003626 ArrayList<Integer> importance = new ArrayList<>(N);
Julia Reynoldse46bb372016-03-17 11:05:58 -04003627 Bundle overrideGroupKeys = new Bundle();
Chris Wren3ad4e3a2014-09-02 17:23:51 -04003628 Bundle visibilityOverrides = new Bundle();
Julia Reynoldsf612869ae2015-11-05 16:48:55 -05003629 Bundle suppressedVisualEffects = new Bundle();
Chris Wrenbdf33762015-12-04 15:50:51 -05003630 Bundle explanation = new Bundle();
Chris Wren333a61c2014-05-28 16:40:57 -04003631 for (int i = 0; i < N; i++) {
3632 NotificationRecord record = mNotificationList.get(i);
Christoph Studercef37cf2014-07-25 14:18:17 +02003633 if (!isVisibleToListener(record.sbn, info)) {
Christoph Studer05ad4822014-05-16 14:16:03 +02003634 continue;
3635 }
Chris Wrenbdf33762015-12-04 15:50:51 -05003636 final String key = record.sbn.getKey();
3637 keys.add(key);
3638 importance.add(record.getImportance());
3639 if (record.getImportanceExplanation() != null) {
3640 explanation.putCharSequence(key, record.getImportanceExplanation());
3641 }
Chris Wren333a61c2014-05-28 16:40:57 -04003642 if (record.isIntercepted()) {
Chris Wrenbdf33762015-12-04 15:50:51 -05003643 interceptedKeys.add(key);
Julia Reynoldsf612869ae2015-11-05 16:48:55 -05003644
Christoph Studer05ad4822014-05-16 14:16:03 +02003645 }
Chris Wrenbdf33762015-12-04 15:50:51 -05003646 suppressedVisualEffects.putInt(key, record.getSuppressedVisualEffects());
Chris Wren3ad4e3a2014-09-02 17:23:51 -04003647 if (record.getPackageVisibilityOverride()
3648 != NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE) {
Chris Wrenbdf33762015-12-04 15:50:51 -05003649 visibilityOverrides.putInt(key, record.getPackageVisibilityOverride());
Chris Wren3ad4e3a2014-09-02 17:23:51 -04003650 }
Julia Reynoldse46bb372016-03-17 11:05:58 -04003651 overrideGroupKeys.putString(key, record.sbn.getOverrideGroupKey());
Christoph Studer05ad4822014-05-16 14:16:03 +02003652 }
Chris Wrenbdf33762015-12-04 15:50:51 -05003653 final int M = keys.size();
3654 String[] keysAr = keys.toArray(new String[M]);
Christoph Studer1d599da2014-06-12 15:25:59 +02003655 String[] interceptedKeysAr = interceptedKeys.toArray(new String[interceptedKeys.size()]);
Chris Wrenbdf33762015-12-04 15:50:51 -05003656 int[] importanceAr = new int[M];
3657 for (int i = 0; i < M; i++) {
3658 importanceAr[i] = importance.get(i);
3659 }
Chris Wren3ad4e3a2014-09-02 17:23:51 -04003660 return new NotificationRankingUpdate(keysAr, interceptedKeysAr, visibilityOverrides,
Julia Reynoldse46bb372016-03-17 11:05:58 -04003661 suppressedVisualEffects, importanceAr, explanation, overrideGroupKeys);
Christoph Studer05ad4822014-05-16 14:16:03 +02003662 }
3663
Christoph Studercef37cf2014-07-25 14:18:17 +02003664 private boolean isVisibleToListener(StatusBarNotification sbn, ManagedServiceInfo listener) {
3665 if (!listener.enabledAndUserMatches(sbn.getUserId())) {
3666 return false;
3667 }
Justin Koh8d11a5a2014-08-04 18:29:49 -07003668 // TODO: remove this for older listeners.
Christoph Studercef37cf2014-07-25 14:18:17 +02003669 return true;
3670 }
3671
Andrei Stingaceanu355b2322016-02-12 16:43:51 +00003672 private boolean isPackageSuspendedForUser(String pkg, int uid) {
Andrei Stingaceanu0122f6512016-01-22 15:33:03 +00003673 int userId = UserHandle.getUserId(uid);
Andrei Stingaceanu0122f6512016-01-22 15:33:03 +00003674 try {
Andrei Stingaceanu355b2322016-02-12 16:43:51 +00003675 return AppGlobals.getPackageManager().isPackageSuspendedForUser(pkg, userId);
Andrei Stingaceanu0122f6512016-01-22 15:33:03 +00003676 } catch (RemoteException re) {
3677 throw new SecurityException("Could not talk to package manager service");
Andrei Stingaceanuefc4a342016-03-22 14:43:01 +00003678 } catch (IllegalArgumentException ex) {
3679 // Package not found.
3680 return false;
Andrei Stingaceanu0122f6512016-01-22 15:33:03 +00003681 }
Andrei Stingaceanu0122f6512016-01-22 15:33:03 +00003682 }
3683
Chris Wren47633422016-01-22 09:56:59 -05003684 private class TrimCache {
3685 StatusBarNotification heavy;
3686 StatusBarNotification sbnClone;
3687 StatusBarNotification sbnCloneLight;
3688
3689 TrimCache(StatusBarNotification sbn) {
3690 heavy = sbn;
3691 }
3692
3693 StatusBarNotification ForListener(ManagedServiceInfo info) {
3694 if (mListeners.getOnNotificationPostedTrim(info) == TRIM_LIGHT) {
3695 if (sbnCloneLight == null) {
3696 sbnCloneLight = heavy.cloneLight();
3697 }
3698 return sbnCloneLight;
3699 } else {
3700 if (sbnClone == null) {
3701 sbnClone = heavy.clone();
3702 }
3703 return sbnClone;
3704 }
3705 }
3706 }
3707
Chris Wren0efdb882016-03-01 17:17:47 -05003708 public class NotificationRankers extends ManagedServices {
Chris Wren51017d02015-12-15 15:34:46 -05003709
Chris Wren0efdb882016-03-01 17:17:47 -05003710 public NotificationRankers() {
Chris Wren51017d02015-12-15 15:34:46 -05003711 super(getContext(), mHandler, mNotificationList, mUserProfiles);
3712 }
3713
3714 @Override
3715 protected Config getConfig() {
3716 Config c = new Config();
Chris Wrene0ba7eb2016-03-04 17:30:43 -05003717 c.caption = "notification ranker service";
3718 c.serviceInterface = NotificationRankerService.SERVICE_INTERFACE;
3719 c.secureSettingName = null;
3720 c.bindPermission = Manifest.permission.BIND_NOTIFICATION_RANKER_SERVICE;
Chris Wren51017d02015-12-15 15:34:46 -05003721 c.settingsAction = Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS;
Chris Wrene0ba7eb2016-03-04 17:30:43 -05003722 c.clientLabel = R.string.notification_ranker_binding_label;
Chris Wren51017d02015-12-15 15:34:46 -05003723 return c;
3724 }
3725
3726 @Override
3727 protected IInterface asInterface(IBinder binder) {
3728 return INotificationListener.Stub.asInterface(binder);
3729 }
3730
3731 @Override
3732 protected boolean checkType(IInterface service) {
3733 return service instanceof INotificationListener;
3734 }
3735
3736 @Override
3737 protected void onServiceAdded(ManagedServiceInfo info) {
3738 mListeners.registerGuestService(info);
3739 }
3740
3741 @Override
3742 protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
3743 mListeners.unregisterService(removed.service, removed.userid);
3744 }
Chris Wren47633422016-01-22 09:56:59 -05003745
3746 public void onNotificationEnqueued(final NotificationRecord r) {
3747 final StatusBarNotification sbn = r.sbn;
3748 TrimCache trimCache = new TrimCache(sbn);
3749
Chris Wrene0ba7eb2016-03-04 17:30:43 -05003750 // mServices is the list inside ManagedServices of all the rankers,
Chris Wren47633422016-01-22 09:56:59 -05003751 // There should be only one, but it's a list, so while we enforce
3752 // singularity elsewhere, we keep it general here, to avoid surprises.
Chris Wren0efdb882016-03-01 17:17:47 -05003753 for (final ManagedServiceInfo info : NotificationRankers.this.mServices) {
Chris Wren47633422016-01-22 09:56:59 -05003754 boolean sbnVisible = isVisibleToListener(sbn, info);
3755 if (!sbnVisible) {
3756 continue;
3757 }
3758
3759 final int importance = r.getImportance();
3760 final boolean fromUser = r.isImportanceFromUser();
3761 final StatusBarNotification sbnToPost = trimCache.ForListener(info);
Chris Wrene0ba7eb2016-03-04 17:30:43 -05003762 mHandler.post(new Runnable() {
Chris Wren47633422016-01-22 09:56:59 -05003763 @Override
3764 public void run() {
3765 notifyEnqueued(info, sbnToPost, importance, fromUser);
3766 }
3767 });
3768 }
3769 }
3770
3771 private void notifyEnqueued(final ManagedServiceInfo info,
3772 final StatusBarNotification sbn, int importance, boolean fromUser) {
Chris Wrene0ba7eb2016-03-04 17:30:43 -05003773 final INotificationListener ranker = (INotificationListener) info.service;
Chris Wren47633422016-01-22 09:56:59 -05003774 StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
3775 try {
Chris Wrene0ba7eb2016-03-04 17:30:43 -05003776 ranker.onNotificationEnqueued(sbnHolder, importance, fromUser);
Chris Wren47633422016-01-22 09:56:59 -05003777 } catch (RemoteException ex) {
Chris Wrene0ba7eb2016-03-04 17:30:43 -05003778 Log.e(TAG, "unable to notify ranker (enqueued): " + ranker, ex);
Chris Wren47633422016-01-22 09:56:59 -05003779 }
3780 }
3781
3782 public boolean isEnabled() {
3783 return !mServices.isEmpty();
3784 }
Julia Reynolds1c9bd422016-03-15 09:25:56 -04003785
3786 @Override
3787 public void onUserSwitched(int user) {
3788 for (ManagedServiceInfo info : mServices) {
3789 unregisterService(info.service, info.userid);
3790 }
3791 registerRanker();
3792 }
3793
3794 @Override
3795 public void onPackagesChanged(boolean queryReplace, String[] pkgList) {
3796 if (DEBUG) Slog.d(TAG, "onPackagesChanged queryReplace=" + queryReplace
3797 + " pkgList=" + (pkgList == null ? null : Arrays.asList(pkgList)));
Chris Wren88d2f6d2016-03-17 15:47:09 -04003798 if (mRankerServicePackageName == null) {
3799 return;
3800 }
Julia Reynolds1c9bd422016-03-15 09:25:56 -04003801
3802 if (pkgList != null && (pkgList.length > 0)) {
3803 for (String pkgName : pkgList) {
3804 if (mRankerServicePackageName.equals(pkgName)) {
3805 registerRanker();
3806 }
3807 }
3808 }
3809 }
3810
3811 protected void registerRanker() {
3812 // Find the updatable ranker and register it.
Chris Wren88d2f6d2016-03-17 15:47:09 -04003813 if (mRankerServicePackageName == null) {
3814 Slog.w(TAG, "could not start ranker service: no package specified!");
3815 return;
3816 }
Julia Reynolds1c9bd422016-03-15 09:25:56 -04003817 Set<ComponentName> rankerComponents = queryPackageForServices(
Julia Reynoldsa3dc1fb2016-04-12 10:25:12 -04003818 mRankerServicePackageName, UserHandle.USER_SYSTEM);
Julia Reynolds1c9bd422016-03-15 09:25:56 -04003819 Iterator<ComponentName> iterator = rankerComponents.iterator();
3820 if (iterator.hasNext()) {
3821 ComponentName rankerComponent = iterator.next();
3822 if (iterator.hasNext()) {
3823 Slog.e(TAG, "found multiple ranker services:" + rankerComponents);
3824 } else {
3825 registerSystemService(rankerComponent, UserHandle.USER_SYSTEM);
3826 }
3827 } else {
3828 Slog.w(TAG, "could not start ranker service: none found");
3829 }
3830 }
Chris Wren51017d02015-12-15 15:34:46 -05003831 }
3832
John Spurlock7340fc82014-04-24 18:50:12 -04003833 public class NotificationListeners extends ManagedServices {
3834
Christoph Studerb82bc782014-08-20 14:29:43 +02003835 private final ArraySet<ManagedServiceInfo> mLightTrimListeners = new ArraySet<>();
3836
John Spurlock7340fc82014-04-24 18:50:12 -04003837 public NotificationListeners() {
3838 super(getContext(), mHandler, mNotificationList, mUserProfiles);
3839 }
3840
3841 @Override
3842 protected Config getConfig() {
3843 Config c = new Config();
3844 c.caption = "notification listener";
3845 c.serviceInterface = NotificationListenerService.SERVICE_INTERFACE;
3846 c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_LISTENERS;
3847 c.bindPermission = android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE;
3848 c.settingsAction = Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS;
3849 c.clientLabel = R.string.notification_listener_binding_label;
3850 return c;
3851 }
3852
3853 @Override
3854 protected IInterface asInterface(IBinder binder) {
3855 return INotificationListener.Stub.asInterface(binder);
3856 }
3857
3858 @Override
Chris Wren51017d02015-12-15 15:34:46 -05003859 protected boolean checkType(IInterface service) {
3860 return service instanceof INotificationListener;
3861 }
3862
3863 @Override
John Spurlock3b98b3f2014-05-01 09:08:48 -04003864 public void onServiceAdded(ManagedServiceInfo info) {
3865 final INotificationListener listener = (INotificationListener) info.service;
Chris Wren333a61c2014-05-28 16:40:57 -04003866 final NotificationRankingUpdate update;
Christoph Studer05ad4822014-05-16 14:16:03 +02003867 synchronized (mNotificationList) {
Chris Wren333a61c2014-05-28 16:40:57 -04003868 update = makeRankingUpdateLocked(info);
Christoph Studer05ad4822014-05-16 14:16:03 +02003869 }
John Spurlock7340fc82014-04-24 18:50:12 -04003870 try {
Chris Wren333a61c2014-05-28 16:40:57 -04003871 listener.onListenerConnected(update);
John Spurlock7340fc82014-04-24 18:50:12 -04003872 } catch (RemoteException e) {
3873 // we tried
3874 }
3875 }
3876
John Spurlock1fa865f2014-07-21 14:56:39 -04003877 @Override
3878 protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
Bryce Lee7219ada2016-04-08 10:54:23 -07003879 if (removeDisabledHints(removed)) {
John Spurlockd8afe3c2014-08-01 14:04:07 -04003880 updateListenerHintsLocked();
Christoph Studer0d6ef4b2014-12-02 15:00:48 +01003881 updateEffectsSuppressorLocked();
John Spurlock1fa865f2014-07-21 14:56:39 -04003882 }
Christoph Studerb82bc782014-08-20 14:29:43 +02003883 mLightTrimListeners.remove(removed);
3884 }
3885
3886 public void setOnNotificationPostedTrimLocked(ManagedServiceInfo info, int trim) {
3887 if (trim == TRIM_LIGHT) {
3888 mLightTrimListeners.add(info);
3889 } else {
3890 mLightTrimListeners.remove(info);
3891 }
3892 }
3893
3894 public int getOnNotificationPostedTrim(ManagedServiceInfo info) {
3895 return mLightTrimListeners.contains(info) ? TRIM_LIGHT : TRIM_FULL;
John Spurlock1fa865f2014-07-21 14:56:39 -04003896 }
3897
John Spurlock7340fc82014-04-24 18:50:12 -04003898 /**
3899 * asynchronously notify all listeners about a new notification
Christoph Studercef37cf2014-07-25 14:18:17 +02003900 *
3901 * <p>
3902 * Also takes care of removing a notification that has been visible to a listener before,
3903 * but isn't anymore.
John Spurlock7340fc82014-04-24 18:50:12 -04003904 */
Christoph Studercef37cf2014-07-25 14:18:17 +02003905 public void notifyPostedLocked(StatusBarNotification sbn, StatusBarNotification oldSbn) {
Christoph Studerb82bc782014-08-20 14:29:43 +02003906 // Lazily initialized snapshots of the notification.
Chris Wren47633422016-01-22 09:56:59 -05003907 TrimCache trimCache = new TrimCache(sbn);
Christoph Studerb82bc782014-08-20 14:29:43 +02003908
John Spurlock7340fc82014-04-24 18:50:12 -04003909 for (final ManagedServiceInfo info : mServices) {
Christoph Studercef37cf2014-07-25 14:18:17 +02003910 boolean sbnVisible = isVisibleToListener(sbn, info);
3911 boolean oldSbnVisible = oldSbn != null ? isVisibleToListener(oldSbn, info) : false;
3912 // This notification hasn't been and still isn't visible -> ignore.
3913 if (!oldSbnVisible && !sbnVisible) {
Christoph Studer05ad4822014-05-16 14:16:03 +02003914 continue;
Chris Wrenf9536642014-04-17 10:01:54 -04003915 }
Chris Wren333a61c2014-05-28 16:40:57 -04003916 final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
Christoph Studercef37cf2014-07-25 14:18:17 +02003917
3918 // This notification became invisible -> remove the old one.
3919 if (oldSbnVisible && !sbnVisible) {
3920 final StatusBarNotification oldSbnLightClone = oldSbn.cloneLight();
3921 mHandler.post(new Runnable() {
3922 @Override
3923 public void run() {
3924 notifyRemoved(info, oldSbnLightClone, update);
3925 }
3926 });
Christoph Studer05ad4822014-05-16 14:16:03 +02003927 continue;
3928 }
Christoph Studercef37cf2014-07-25 14:18:17 +02003929
Chris Wren47633422016-01-22 09:56:59 -05003930 final StatusBarNotification sbnToPost = trimCache.ForListener(info);
Christoph Studer05ad4822014-05-16 14:16:03 +02003931 mHandler.post(new Runnable() {
3932 @Override
3933 public void run() {
Christoph Studerb82bc782014-08-20 14:29:43 +02003934 notifyPosted(info, sbnToPost, update);
Christoph Studer05ad4822014-05-16 14:16:03 +02003935 }
3936 });
Kenny Guy3a7c4a52014-03-03 18:24:03 +00003937 }
3938 }
Kenny Guy3a7c4a52014-03-03 18:24:03 +00003939
John Spurlock7340fc82014-04-24 18:50:12 -04003940 /**
3941 * asynchronously notify all listeners about a removed notification
3942 */
Chris Wren333a61c2014-05-28 16:40:57 -04003943 public void notifyRemovedLocked(StatusBarNotification sbn) {
John Spurlock7340fc82014-04-24 18:50:12 -04003944 // make a copy in case changes are made to the underlying Notification object
3945 // NOTE: this copy is lightweight: it doesn't include heavyweight parts of the
3946 // notification
3947 final StatusBarNotification sbnLight = sbn.cloneLight();
Chris Wrenf9536642014-04-17 10:01:54 -04003948 for (final ManagedServiceInfo info : mServices) {
Christoph Studercef37cf2014-07-25 14:18:17 +02003949 if (!isVisibleToListener(sbn, info)) {
Christoph Studer05ad4822014-05-16 14:16:03 +02003950 continue;
Chris Wrenf9536642014-04-17 10:01:54 -04003951 }
Chris Wren333a61c2014-05-28 16:40:57 -04003952 final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
Christoph Studer05ad4822014-05-16 14:16:03 +02003953 mHandler.post(new Runnable() {
3954 @Override
3955 public void run() {
Christoph Studercef37cf2014-07-25 14:18:17 +02003956 notifyRemoved(info, sbnLight, update);
Christoph Studer05ad4822014-05-16 14:16:03 +02003957 }
3958 });
Chris Wrenf9536642014-04-17 10:01:54 -04003959 }
3960 }
3961
3962 /**
3963 * asynchronously notify all listeners about a reordering of notifications
Chris Wrenf9536642014-04-17 10:01:54 -04003964 */
Chris Wren333a61c2014-05-28 16:40:57 -04003965 public void notifyRankingUpdateLocked() {
Chris Wrenf9536642014-04-17 10:01:54 -04003966 for (final ManagedServiceInfo serviceInfo : mServices) {
Christoph Studer05ad4822014-05-16 14:16:03 +02003967 if (!serviceInfo.isEnabledForCurrentProfiles()) {
3968 continue;
3969 }
Christoph Studercef37cf2014-07-25 14:18:17 +02003970 final NotificationRankingUpdate update = makeRankingUpdateLocked(serviceInfo);
John Spurlock7340fc82014-04-24 18:50:12 -04003971 mHandler.post(new Runnable() {
3972 @Override
3973 public void run() {
Chris Wren333a61c2014-05-28 16:40:57 -04003974 notifyRankingUpdate(serviceInfo, update);
John Spurlock7340fc82014-04-24 18:50:12 -04003975 }
3976 });
Kenny Guya263e4e2014-03-03 18:24:03 +00003977 }
Kenny Guya263e4e2014-03-03 18:24:03 +00003978 }
Kenny Guya263e4e2014-03-03 18:24:03 +00003979
John Spurlockd8afe3c2014-08-01 14:04:07 -04003980 public void notifyListenerHintsChangedLocked(final int hints) {
John Spurlock1fa865f2014-07-21 14:56:39 -04003981 for (final ManagedServiceInfo serviceInfo : mServices) {
3982 if (!serviceInfo.isEnabledForCurrentProfiles()) {
3983 continue;
3984 }
3985 mHandler.post(new Runnable() {
3986 @Override
3987 public void run() {
John Spurlockd8afe3c2014-08-01 14:04:07 -04003988 notifyListenerHintsChanged(serviceInfo, hints);
John Spurlock1fa865f2014-07-21 14:56:39 -04003989 }
3990 });
3991 }
3992 }
3993
Christoph Studer85a384b2014-08-27 20:16:15 +02003994 public void notifyInterruptionFilterChanged(final int interruptionFilter) {
3995 for (final ManagedServiceInfo serviceInfo : mServices) {
3996 if (!serviceInfo.isEnabledForCurrentProfiles()) {
3997 continue;
3998 }
3999 mHandler.post(new Runnable() {
4000 @Override
4001 public void run() {
4002 notifyInterruptionFilterChanged(serviceInfo, interruptionFilter);
4003 }
4004 });
4005 }
4006 }
4007
Christoph Studercef37cf2014-07-25 14:18:17 +02004008 private void notifyPosted(final ManagedServiceInfo info,
Christoph Studer05ad4822014-05-16 14:16:03 +02004009 final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) {
John Spurlock7340fc82014-04-24 18:50:12 -04004010 final INotificationListener listener = (INotificationListener)info.service;
Griff Hazen84a00ea2014-09-02 17:10:47 -07004011 StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
John Spurlock7340fc82014-04-24 18:50:12 -04004012 try {
Griff Hazen84a00ea2014-09-02 17:10:47 -07004013 listener.onNotificationPosted(sbnHolder, rankingUpdate);
John Spurlock7340fc82014-04-24 18:50:12 -04004014 } catch (RemoteException ex) {
4015 Log.e(TAG, "unable to notify listener (posted): " + listener, ex);
4016 }
4017 }
4018
Christoph Studercef37cf2014-07-25 14:18:17 +02004019 private void notifyRemoved(ManagedServiceInfo info, StatusBarNotification sbn,
Christoph Studer05ad4822014-05-16 14:16:03 +02004020 NotificationRankingUpdate rankingUpdate) {
John Spurlock7340fc82014-04-24 18:50:12 -04004021 if (!info.enabledAndUserMatches(sbn.getUserId())) {
4022 return;
4023 }
Christoph Studer05ad4822014-05-16 14:16:03 +02004024 final INotificationListener listener = (INotificationListener) info.service;
Griff Hazen84a00ea2014-09-02 17:10:47 -07004025 StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
John Spurlock7340fc82014-04-24 18:50:12 -04004026 try {
Griff Hazen84a00ea2014-09-02 17:10:47 -07004027 listener.onNotificationRemoved(sbnHolder, rankingUpdate);
John Spurlock7340fc82014-04-24 18:50:12 -04004028 } catch (RemoteException ex) {
4029 Log.e(TAG, "unable to notify listener (removed): " + listener, ex);
John Spurlockb408e8e2014-04-23 21:12:45 -04004030 }
Kenny Guya263e4e2014-03-03 18:24:03 +00004031 }
Chris Wrenf9536642014-04-17 10:01:54 -04004032
Christoph Studer05ad4822014-05-16 14:16:03 +02004033 private void notifyRankingUpdate(ManagedServiceInfo info,
4034 NotificationRankingUpdate rankingUpdate) {
4035 final INotificationListener listener = (INotificationListener) info.service;
Chris Wrenf9536642014-04-17 10:01:54 -04004036 try {
Christoph Studer05ad4822014-05-16 14:16:03 +02004037 listener.onNotificationRankingUpdate(rankingUpdate);
Chris Wrenf9536642014-04-17 10:01:54 -04004038 } catch (RemoteException ex) {
4039 Log.e(TAG, "unable to notify listener (ranking update): " + listener, ex);
4040 }
4041 }
John Spurlock1fa865f2014-07-21 14:56:39 -04004042
John Spurlockd8afe3c2014-08-01 14:04:07 -04004043 private void notifyListenerHintsChanged(ManagedServiceInfo info, int hints) {
John Spurlock1fa865f2014-07-21 14:56:39 -04004044 final INotificationListener listener = (INotificationListener) info.service;
4045 try {
John Spurlockd8afe3c2014-08-01 14:04:07 -04004046 listener.onListenerHintsChanged(hints);
John Spurlock1fa865f2014-07-21 14:56:39 -04004047 } catch (RemoteException ex) {
John Spurlockd8afe3c2014-08-01 14:04:07 -04004048 Log.e(TAG, "unable to notify listener (listener hints): " + listener, ex);
John Spurlock1fa865f2014-07-21 14:56:39 -04004049 }
4050 }
Justin Koh38156c52014-06-04 13:57:49 -07004051
Christoph Studer85a384b2014-08-27 20:16:15 +02004052 private void notifyInterruptionFilterChanged(ManagedServiceInfo info,
4053 int interruptionFilter) {
4054 final INotificationListener listener = (INotificationListener) info.service;
4055 try {
4056 listener.onInterruptionFilterChanged(interruptionFilter);
4057 } catch (RemoteException ex) {
4058 Log.e(TAG, "unable to notify listener (interruption filter): " + listener, ex);
4059 }
4060 }
4061
Justin Koh38156c52014-06-04 13:57:49 -07004062 private boolean isListenerPackage(String packageName) {
4063 if (packageName == null) {
4064 return false;
4065 }
4066 // TODO: clean up locking object later
4067 synchronized (mNotificationList) {
4068 for (final ManagedServiceInfo serviceInfo : mServices) {
4069 if (packageName.equals(serviceInfo.component.getPackageName())) {
4070 return true;
4071 }
4072 }
4073 }
4074 return false;
4075 }
Kenny Guya263e4e2014-03-03 18:24:03 +00004076 }
John Spurlock25e2d242014-06-27 13:58:23 -04004077
4078 public static final class DumpFilter {
Dan Sandlera1770312015-07-10 13:59:29 -04004079 public boolean filtered = false;
John Spurlock25e2d242014-06-27 13:58:23 -04004080 public String pkgFilter;
John Spurlock50806fc2014-07-15 10:22:02 -04004081 public boolean zen;
Chris Wrene4b38802015-07-07 15:54:19 -04004082 public long since;
4083 public boolean stats;
Dan Sandlera1770312015-07-10 13:59:29 -04004084 public boolean redact = true;
John Spurlock25e2d242014-06-27 13:58:23 -04004085
4086 public static DumpFilter parseFromArguments(String[] args) {
Dan Sandlera1770312015-07-10 13:59:29 -04004087 final DumpFilter filter = new DumpFilter();
4088 for (int ai = 0; ai < args.length; ai++) {
4089 final String a = args[ai];
4090 if ("--noredact".equals(a) || "--reveal".equals(a)) {
4091 filter.redact = false;
4092 } else if ("p".equals(a) || "pkg".equals(a) || "--package".equals(a)) {
4093 if (ai < args.length-1) {
4094 ai++;
4095 filter.pkgFilter = args[ai].trim().toLowerCase();
4096 if (filter.pkgFilter.isEmpty()) {
4097 filter.pkgFilter = null;
4098 } else {
4099 filter.filtered = true;
4100 }
4101 }
4102 } else if ("--zen".equals(a) || "zen".equals(a)) {
4103 filter.filtered = true;
4104 filter.zen = true;
4105 } else if ("--stats".equals(a)) {
4106 filter.stats = true;
4107 if (ai < args.length-1) {
4108 ai++;
Tobias Thierer28532d02016-04-21 14:52:10 +01004109 filter.since = Long.parseLong(args[ai]);
Dan Sandlera1770312015-07-10 13:59:29 -04004110 } else {
4111 filter.since = 0;
4112 }
4113 }
John Spurlock25e2d242014-06-27 13:58:23 -04004114 }
Dan Sandlera1770312015-07-10 13:59:29 -04004115 return filter;
John Spurlock25e2d242014-06-27 13:58:23 -04004116 }
4117
4118 public boolean matches(StatusBarNotification sbn) {
Dan Sandlera1770312015-07-10 13:59:29 -04004119 if (!filtered) return true;
4120 return zen ? true : sbn != null
John Spurlock50806fc2014-07-15 10:22:02 -04004121 && (matches(sbn.getPackageName()) || matches(sbn.getOpPkg()));
John Spurlock25e2d242014-06-27 13:58:23 -04004122 }
4123
4124 public boolean matches(ComponentName component) {
Dan Sandlera1770312015-07-10 13:59:29 -04004125 if (!filtered) return true;
4126 return zen ? true : component != null && matches(component.getPackageName());
John Spurlock25e2d242014-06-27 13:58:23 -04004127 }
4128
4129 public boolean matches(String pkg) {
Dan Sandlera1770312015-07-10 13:59:29 -04004130 if (!filtered) return true;
4131 return zen ? true : pkg != null && pkg.toLowerCase().contains(pkgFilter);
John Spurlock50806fc2014-07-15 10:22:02 -04004132 }
4133
4134 @Override
4135 public String toString() {
Chris Wrene4b38802015-07-07 15:54:19 -04004136 return stats ? "stats" : zen ? "zen" : ('\'' + pkgFilter + '\'');
John Spurlock25e2d242014-06-27 13:58:23 -04004137 }
4138 }
Griff Hazen84a00ea2014-09-02 17:10:47 -07004139
4140 /**
4141 * Wrapper for a StatusBarNotification object that allows transfer across a oneway
4142 * binder without sending large amounts of data over a oneway transaction.
4143 */
4144 private static final class StatusBarNotificationHolder
4145 extends IStatusBarNotificationHolder.Stub {
Griff Hazene9aac5f2014-09-05 20:04:09 -07004146 private StatusBarNotification mValue;
Griff Hazen84a00ea2014-09-02 17:10:47 -07004147
4148 public StatusBarNotificationHolder(StatusBarNotification value) {
4149 mValue = value;
4150 }
4151
Griff Hazene9aac5f2014-09-05 20:04:09 -07004152 /** Get the held value and clear it. This function should only be called once per holder */
Griff Hazen84a00ea2014-09-02 17:10:47 -07004153 @Override
4154 public StatusBarNotification get() {
Griff Hazene9aac5f2014-09-05 20:04:09 -07004155 StatusBarNotification value = mValue;
4156 mValue = null;
4157 return value;
Griff Hazen84a00ea2014-09-02 17:10:47 -07004158 }
4159 }
John Spurlock7c74f782015-06-04 13:01:42 -04004160
4161 private final class PolicyAccess {
4162 private static final String SEPARATOR = ":";
4163 private final String[] PERM = {
4164 android.Manifest.permission.ACCESS_NOTIFICATION_POLICY
4165 };
4166
4167 public boolean isPackageGranted(String pkg) {
4168 return pkg != null && getGrantedPackages().contains(pkg);
4169 }
4170
4171 public void put(String pkg, boolean granted) {
4172 if (pkg == null) return;
4173 final ArraySet<String> pkgs = getGrantedPackages();
4174 boolean changed;
4175 if (granted) {
4176 changed = pkgs.add(pkg);
4177 } else {
4178 changed = pkgs.remove(pkg);
4179 }
4180 if (!changed) return;
4181 final String setting = TextUtils.join(SEPARATOR, pkgs);
4182 final int currentUser = ActivityManager.getCurrentUser();
4183 Settings.Secure.putStringForUser(getContext().getContentResolver(),
4184 Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES,
4185 setting,
4186 currentUser);
4187 getContext().sendBroadcastAsUser(new Intent(NotificationManager
4188 .ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED)
4189 .setPackage(pkg)
4190 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), new UserHandle(currentUser), null);
4191 }
4192
4193 public ArraySet<String> getGrantedPackages() {
4194 final ArraySet<String> pkgs = new ArraySet<>();
Julia Reynoldsea6c4482015-08-13 09:01:33 -04004195
4196 long identity = Binder.clearCallingIdentity();
4197 try {
4198 final String setting = Settings.Secure.getStringForUser(
4199 getContext().getContentResolver(),
4200 Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES,
4201 ActivityManager.getCurrentUser());
4202 if (setting != null) {
4203 final String[] tokens = setting.split(SEPARATOR);
4204 for (int i = 0; i < tokens.length; i++) {
4205 String token = tokens[i];
4206 if (token != null) {
Andreas Gampe1ed71f32015-12-11 15:49:07 -08004207 token = token.trim();
Julia Reynoldsea6c4482015-08-13 09:01:33 -04004208 }
4209 if (TextUtils.isEmpty(token)) {
4210 continue;
4211 }
4212 pkgs.add(token);
John Spurlock7c74f782015-06-04 13:01:42 -04004213 }
John Spurlock7c74f782015-06-04 13:01:42 -04004214 }
Julia Reynoldsea6c4482015-08-13 09:01:33 -04004215 } finally {
4216 Binder.restoreCallingIdentity(identity);
John Spurlock7c74f782015-06-04 13:01:42 -04004217 }
4218 return pkgs;
4219 }
4220
4221 public String[] getRequestingPackages() throws RemoteException {
4222 final ParceledListSlice list = AppGlobals.getPackageManager()
4223 .getPackagesHoldingPermissions(PERM, 0 /*flags*/,
4224 ActivityManager.getCurrentUser());
4225 final List<PackageInfo> pkgs = list.getList();
4226 if (pkgs == null || pkgs.isEmpty()) return new String[0];
4227 final int N = pkgs.size();
4228 final String[] rt = new String[N];
4229 for (int i = 0; i < N; i++) {
4230 rt[i] = pkgs.get(i).packageName;
4231 }
4232 return rt;
4233 }
4234 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004235}