blob: 038e47e4dada3fc499918f6c272458e02a78ead9 [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
Julia Reynolds7c96b582017-05-25 12:35:36 -040019import static android.app.NotificationManager.IMPORTANCE_MIN;
Julia Reynolds85769912016-10-25 09:08:57 -040020import static android.app.NotificationManager.IMPORTANCE_NONE;
Julia Reynolds5f20e9f2017-01-30 08:54:53 -050021import static android.content.pm.PackageManager.FEATURE_LEANBACK;
22import static android.content.pm.PackageManager.FEATURE_TELEVISION;
Julia Reynolds73ed76b2017-04-04 17:04:38 -040023import static android.service.notification.NotificationListenerService
24 .NOTIFICATION_CHANNEL_OR_GROUP_ADDED;
25import static android.service.notification.NotificationListenerService
26 .NOTIFICATION_CHANNEL_OR_GROUP_DELETED;
27import static android.service.notification.NotificationListenerService
28 .NOTIFICATION_CHANNEL_OR_GROUP_UPDATED;
Julia Reynolds3aa5f1e2016-11-09 15:43:49 -050029import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
30import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL_ALL;
31import static android.service.notification.NotificationListenerService.REASON_CHANNEL_BANNED;
Julia Reynoldsf619bc52017-03-17 08:32:23 -040032import static android.service.notification.NotificationListenerService.REASON_CANCEL;
33import static android.service.notification.NotificationListenerService.REASON_CANCEL_ALL;
34import static android.service.notification.NotificationListenerService.REASON_CLICK;
35import static android.service.notification.NotificationListenerService.REASON_ERROR;
Julia Reynolds3aa5f1e2016-11-09 15:43:49 -050036import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED;
37import static android.service.notification.NotificationListenerService.REASON_LISTENER_CANCEL;
38import static android.service.notification.NotificationListenerService.REASON_LISTENER_CANCEL_ALL;
39import static android.service.notification.NotificationListenerService.REASON_PACKAGE_BANNED;
40import static android.service.notification.NotificationListenerService.REASON_PACKAGE_CHANGED;
41import static android.service.notification.NotificationListenerService.REASON_PACKAGE_SUSPENDED;
42import static android.service.notification.NotificationListenerService.REASON_PROFILE_TURNED_OFF;
43import static android.service.notification.NotificationListenerService.REASON_SNOOZED;
Julia Reynolds2a128742016-11-28 14:29:25 -050044import static android.service.notification.NotificationListenerService.REASON_TIMEOUT;
Julia Reynolds3aa5f1e2016-11-09 15:43:49 -050045import static android.service.notification.NotificationListenerService.REASON_UNAUTOBUNDLED;
46import static android.service.notification.NotificationListenerService.REASON_USER_STOPPED;
Jason Monk63506742015-12-16 12:06:51 -050047import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_EFFECTS;
Bryce Lee7219ada2016-04-08 10:54:23 -070048import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_NOTIFICATION_EFFECTS;
49import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS;
Julia Reynoldsd5607292016-02-05 15:25:58 -050050import static android.service.notification.NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_OFF;
Julia Reynolds61721582016-01-05 08:35:25 -050051import static android.service.notification.NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_ON;
Christoph Studerb82bc782014-08-20 14:29:43 +020052import static android.service.notification.NotificationListenerService.TRIM_FULL;
53import static android.service.notification.NotificationListenerService.TRIM_LIGHT;
Felipe Lemea1b79bf2016-05-24 13:06:54 -070054
Wale Ogunwaleac2561e2016-11-01 15:43:46 -070055import static android.view.Display.DEFAULT_DISPLAY;
56import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
Jeff Sharkey098d5802012-04-26 17:30:34 -070057import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
svetoslavganov75986cf2009-05-14 22:28:01 -070058
Chris Wren51017d02015-12-15 15:34:46 -050059import android.Manifest;
Julia Reynoldsa78cdff2017-04-26 10:19:25 -040060import android.annotation.NonNull;
Wei Liu97e56662016-03-04 10:52:33 -080061import android.annotation.Nullable;
Dianne Hackborn41203752012-08-31 14:05:51 -070062import android.app.ActivityManager;
Felipe Lemea1b79bf2016-05-24 13:06:54 -070063import android.app.ActivityManagerInternal;
Julia Reynolds2a128742016-11-28 14:29:25 -050064import android.app.AlarmManager;
John Spurlock7340fc82014-04-24 18:50:12 -040065import android.app.AppGlobals;
Daniel Sandler4a900ac2013-01-30 14:04:10 -050066import android.app.AppOpsManager;
Julia Reynoldsa47a27f2015-08-24 08:31:47 -040067import android.app.AutomaticZenRule;
Julia Reynolds59e152e2017-01-25 17:42:53 -050068import android.app.NotificationChannelGroup;
Geoffrey Pitsche75a66e2016-11-22 11:12:11 -050069import android.app.backup.BackupManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080070import android.app.IActivityManager;
71import android.app.INotificationManager;
72import android.app.ITransientNotification;
73import android.app.Notification;
Julia Reynoldsb5e44b72016-08-16 15:00:25 -040074import android.app.NotificationChannel;
John Spurlock1fc476d2015-04-14 16:05:20 -040075import android.app.NotificationManager.Policy;
Geoffrey Pitsche75a66e2016-11-22 11:12:11 -050076import android.app.NotificationManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080077import android.app.PendingIntent;
78import android.app.StatusBarManager;
Amith Yamasanif47e51e2015-04-17 10:02:15 -070079import android.app.usage.UsageEvents;
Amith Yamasanif47e51e2015-04-17 10:02:15 -070080import android.app.usage.UsageStatsManagerInternal;
Julia Reynolds73ed76b2017-04-04 17:04:38 -040081import android.companion.ICompanionDeviceManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080082import android.content.BroadcastReceiver;
Daniel Sandler5feceeb2013-03-22 18:29:23 -070083import android.content.ComponentName;
Dianne Hackborn1dac2772009-06-26 18:16:48 -070084import android.content.ContentResolver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080085import android.content.Context;
86import android.content.Intent;
87import android.content.IntentFilter;
John Spurlock7340fc82014-04-24 18:50:12 -040088import android.content.pm.ApplicationInfo;
Kenny Guy70058402014-10-28 20:45:06 +000089import android.content.pm.IPackageManager;
Daniel Sandler4a900ac2013-01-30 14:04:10 -050090import android.content.pm.PackageInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080091import android.content.pm.PackageManager;
92import android.content.pm.PackageManager.NameNotFoundException;
Christoph Studercee44ba2014-05-20 18:36:43 +020093import android.content.pm.ParceledListSlice;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080094import android.content.res.Resources;
Dianne Hackborn1dac2772009-06-26 18:16:48 -070095import android.database.ContentObserver;
svetoslavganov75986cf2009-05-14 22:28:01 -070096import android.media.AudioManager;
John Spurlockcdb57ae2015-02-11 19:04:11 -050097import android.media.AudioManagerInternal;
Jeff Sharkey098d5802012-04-26 17:30:34 -070098import android.media.IRingtonePlayer;
Marta Białka39c992f2011-03-10 10:27:24 +010099import android.media.ToneGenerator;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800100import android.net.Uri;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800101import android.os.Binder;
Geoffrey Pitsch86c11e602017-04-17 15:28:40 -0400102import android.os.Build;
John Spurlock2b122f42014-08-27 16:29:47 -0400103import android.os.Bundle;
John Spurlock056c5192014-04-20 21:52:01 -0400104import android.os.Environment;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800105import android.os.Handler;
Chris Wrenf9536642014-04-17 10:01:54 -0400106import android.os.HandlerThread;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800107import android.os.IBinder;
John Spurlock7340fc82014-04-24 18:50:12 -0400108import android.os.IInterface;
Chris Wrenf9536642014-04-17 10:01:54 -0400109import android.os.Looper;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800110import android.os.Message;
Dianne Hackbornd8a43f62009-08-17 23:33:56 -0700111import android.os.Process;
svetoslavganov75986cf2009-05-14 22:28:01 -0700112import android.os.RemoteException;
Julia Reynolds73ed76b2017-04-04 17:04:38 -0400113import android.os.ServiceManager;
Chris Wrenc8673a82016-05-17 17:11:29 -0400114import android.os.SystemClock;
Selim Cinekb5605e52015-02-20 18:21:41 +0100115import android.os.SystemProperties;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700116import android.os.UserHandle;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800117import android.os.Vibrator;
Michael Wright71216972017-01-31 18:33:54 +0000118import android.os.VibrationEffect;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800119import android.provider.Settings;
Julia Reynoldse46bb372016-03-17 11:05:58 -0400120import android.service.notification.Adjustment;
Chris Wren333a61c2014-05-28 16:40:57 -0400121import android.service.notification.Condition;
John Spurlock7340fc82014-04-24 18:50:12 -0400122import android.service.notification.IConditionProvider;
Chris Wren333a61c2014-05-28 16:40:57 -0400123import android.service.notification.INotificationListener;
Griff Hazen84a00ea2014-09-02 17:10:47 -0700124import android.service.notification.IStatusBarNotificationHolder;
Julia Reynolds77b2cc92016-11-08 14:41:09 -0500125import android.service.notification.NotificationAssistantService;
John Spurlock7340fc82014-04-24 18:50:12 -0400126import android.service.notification.NotificationListenerService;
Christoph Studer05ad4822014-05-16 14:16:03 +0200127import android.service.notification.NotificationRankingUpdate;
Julia Reynoldsc9842c12017-02-07 12:46:41 -0500128import android.service.notification.NotificationRecordProto;
129import android.service.notification.NotificationServiceDumpProto;
130import android.service.notification.NotificationServiceProto;
Julia Reynolds22f02b32016-12-01 15:05:13 -0500131import android.service.notification.SnoozeCriterion;
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700132import android.service.notification.StatusBarNotification;
John Spurlock056c5192014-04-20 21:52:01 -0400133import android.service.notification.ZenModeConfig;
Julia Reynolds520df6e2017-02-13 09:05:10 -0500134import android.service.notification.ZenModeProto;
John Spurlock32fe4c62014-10-02 12:16:02 -0400135import android.telephony.PhoneStateListener;
Daniel Sandlere96ffb12010-03-11 13:38:06 -0500136import android.telephony.TelephonyManager;
svetoslavganov75986cf2009-05-14 22:28:01 -0700137import android.text.TextUtils;
John Spurlocka4294292014-03-24 18:02:32 -0400138import android.util.ArrayMap;
John Spurlock1fa865f2014-07-21 14:56:39 -0400139import android.util.ArraySet;
Dianne Hackborn39606a02012-07-31 17:54:35 -0700140import android.util.AtomicFile;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800141import android.util.Log;
Andy Stadler110988c2010-12-03 14:29:16 -0800142import android.util.Slog;
Bryce Lee7219ada2016-04-08 10:54:23 -0700143import android.util.SparseArray;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400144import android.util.Xml;
Julia Reynoldsc9842c12017-02-07 12:46:41 -0500145import android.util.proto.ProtoOutputStream;
Svetoslav Ganovaa076532016-08-01 19:16:43 -0700146import android.view.WindowManagerInternal;
svetoslavganov75986cf2009-05-14 22:28:01 -0700147import android.view.accessibility.AccessibilityEvent;
148import android.view.accessibility.AccessibilityManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800149import android.widget.Toast;
Rubin Xu7eadc1b2016-02-01 16:13:45 +0000150
Scott Greenwald9a05b312013-06-28 00:37:54 -0400151import com.android.internal.R;
Julia Reynolds88860ce2017-06-01 16:55:49 -0400152import com.android.internal.annotations.GuardedBy;
Chris Wren93bb8b82016-03-29 14:35:05 -0400153import com.android.internal.annotations.VisibleForTesting;
Chris Wren9eb5e102017-01-26 13:15:06 -0500154import com.android.internal.logging.MetricsLogger;
Julia Reynolds520df6e2017-02-13 09:05:10 -0500155import com.android.internal.logging.nano.MetricsProto;
Chris Wren9eb5e102017-01-26 13:15:06 -0500156import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
Chris Wrend1dbc922015-06-19 17:51:16 -0400157import com.android.internal.statusbar.NotificationVisibility;
Julia Reynolds73ed76b2017-04-04 17:04:38 -0400158import com.android.internal.util.ArrayUtils;
Jeff Sharkeyfe9a53b2017-03-31 14:08:23 -0600159import com.android.internal.util.DumpUtils;
John Spurlock056c5192014-04-20 21:52:01 -0400160import com.android.internal.util.FastXmlSerializer;
Julia Reynoldsa47a27f2015-08-24 08:31:47 -0400161import com.android.internal.util.Preconditions;
Felipe Lemea1b79bf2016-05-24 13:06:54 -0700162import com.android.server.DeviceIdleController;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800163import com.android.server.EventLogTags;
Amith Yamasanif47e51e2015-04-17 10:02:15 -0700164import com.android.server.LocalServices;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800165import com.android.server.SystemService;
166import com.android.server.lights.Light;
167import com.android.server.lights.LightsManager;
John Spurlock7340fc82014-04-24 18:50:12 -0400168import com.android.server.notification.ManagedServices.ManagedServiceInfo;
Svetoslav Ganovaa076532016-08-01 19:16:43 -0700169import com.android.server.policy.PhoneWindowManager;
John Spurlockb408e8e2014-04-23 21:12:45 -0400170import com.android.server.statusbar.StatusBarManagerInternal;
Ruben Brunke24b9a62016-02-16 21:38:24 -0800171import com.android.server.notification.ManagedServices.UserProfiles;
Ruben Brunkdd18a0b2015-12-04 16:16:31 -0800172
John Spurlockb408e8e2014-04-23 21:12:45 -0400173import libcore.io.IoUtils;
Rubin Xu7eadc1b2016-02-01 16:13:45 +0000174
Chris Wrene4b38802015-07-07 15:54:19 -0400175import org.json.JSONException;
176import org.json.JSONObject;
Jeff Sharkey098d5802012-04-26 17:30:34 -0700177import org.xmlpull.v1.XmlPullParser;
178import org.xmlpull.v1.XmlPullParserException;
John Spurlock056c5192014-04-20 21:52:01 -0400179import org.xmlpull.v1.XmlSerializer;
Jeff Sharkey098d5802012-04-26 17:30:34 -0700180
John Spurlock35ef0a62015-05-28 11:24:10 -0400181import java.io.ByteArrayInputStream;
182import java.io.ByteArrayOutputStream;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400183import java.io.File;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800184import java.io.FileDescriptor;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400185import java.io.FileInputStream;
186import java.io.FileNotFoundException;
John Spurlock056c5192014-04-20 21:52:01 -0400187import java.io.FileOutputStream;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400188import java.io.IOException;
John Spurlock35ef0a62015-05-28 11:24:10 -0400189import java.io.InputStream;
190import java.io.OutputStream;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800191import java.io.PrintWriter;
Wojciech Staszkiewicz9e9e2e72015-05-08 14:58:46 +0100192import java.nio.charset.StandardCharsets;
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500193import java.util.ArrayDeque;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800194import java.util.ArrayList;
Michael Wrightbc4d0d92017-03-23 18:57:57 +0000195import java.util.Arrays;
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500196import java.util.Iterator;
John Spurlock7c74f782015-06-04 13:01:42 -0400197import java.util.List;
Chris Wrenacf424a2016-03-15 12:48:55 -0400198import java.util.Map;
Christoph Studer265c1052014-07-23 17:14:33 +0200199import java.util.Map.Entry;
Julia Reynolds4b82f6d2017-01-04 10:47:41 -0500200import java.util.Objects;
Chris Wren51017d02015-12-15 15:34:46 -0500201import java.util.concurrent.TimeUnit;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400202
Daniel Sandlerd0a2f862010-08-03 15:29:31 -0400203/** {@hide} */
Adam Lesinski182f73f2013-12-05 16:48:06 -0800204public class NotificationManagerService extends SystemService {
205 static final String TAG = "NotificationService";
Christoph Studer1f32c652014-11-26 15:32:20 +0100206 static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
Selim Cinek40412492015-12-08 18:03:22 -0800207 public static final boolean ENABLE_CHILD_NOTIFICATIONS
208 = SystemProperties.getBoolean("debug.child_notifs", true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800209
Adam Lesinski182f73f2013-12-05 16:48:06 -0800210 static final int MAX_PACKAGE_NOTIFICATIONS = 50;
Chris Wrena61f1792016-08-04 11:24:42 -0400211 static final float DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE = 10f;
Joe Onoratobd73d012010-06-04 11:44:54 -0700212
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800213 // message codes
Adam Lesinski182f73f2013-12-05 16:48:06 -0800214 static final int MESSAGE_TIMEOUT = 2;
John Spurlock056c5192014-04-20 21:52:01 -0400215 static final int MESSAGE_SAVE_POLICY_FILE = 3;
Chris Wren51017d02015-12-15 15:34:46 -0500216 static final int MESSAGE_SEND_RANKING_UPDATE = 4;
217 static final int MESSAGE_LISTENER_HINTS_CHANGED = 5;
218 static final int MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED = 6;
219
220 // ranking thread messages
221 private static final int MESSAGE_RECONSIDER_RANKING = 1000;
222 private static final int MESSAGE_RANKING_SORT = 1001;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800223
Svetoslav Ganovaa076532016-08-01 19:16:43 -0700224 static final int LONG_DELAY = PhoneWindowManager.TOAST_WINDOW_TIMEOUT;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800225 static final int SHORT_DELAY = 2000; // 2 seconds
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800226
Adam Lesinski182f73f2013-12-05 16:48:06 -0800227 static final long[] DEFAULT_VIBRATE_PATTERN = {0, 250, 250, 250};
Christoph Studer265c1052014-07-23 17:14:33 +0200228
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -0500229 static final long SNOOZE_UNTIL_UNSPECIFIED = -1;
230
Adam Lesinski182f73f2013-12-05 16:48:06 -0800231 static final int VIBRATE_PATTERN_MAXLEN = 8 * 2 + 1; // up to eight bumps
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800232
Adam Lesinski182f73f2013-12-05 16:48:06 -0800233 static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_NOTIFICATION;
Daniel Sandler526fa0e2012-12-04 14:51:50 -0500234
Adam Lesinski182f73f2013-12-05 16:48:06 -0800235 static final boolean ENABLE_BLOCKED_TOASTS = true;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400236
Christoph Studer12aeda82014-09-23 19:08:56 +0200237 // When #matchesCallFilter is called from the ringer, wait at most
238 // 3s to resolve the contacts. This timeout is required since
239 // ContactsProvider might take a long time to start up.
240 //
241 // Return STARRED_CONTACT when the timeout is hit in order to avoid
242 // missed calls in ZEN mode "Important".
243 static final int MATCHES_CALL_FILTER_CONTACTS_TIMEOUT_MS = 3000;
244 static final float MATCHES_CALL_FILTER_TIMEOUT_AFFINITY =
245 ValidateNotificationPeople.STARRED_CONTACT;
246
Christoph Studer265c1052014-07-23 17:14:33 +0200247 /** notification_enqueue status value for a newly enqueued notification. */
248 private static final int EVENTLOG_ENQUEUE_STATUS_NEW = 0;
249
250 /** notification_enqueue status value for an existing notification. */
251 private static final int EVENTLOG_ENQUEUE_STATUS_UPDATE = 1;
252
253 /** notification_enqueue status value for an ignored notification. */
254 private static final int EVENTLOG_ENQUEUE_STATUS_IGNORED = 2;
Chris Wrenc8673a82016-05-17 17:11:29 -0400255 private static final long MIN_PACKAGE_OVERRATE_LOG_INTERVAL = 5000; // milliseconds
Christoph Studer265c1052014-07-23 17:14:33 +0200256
Julia Reynolds4b82f6d2017-01-04 10:47:41 -0500257 private static final long DELAY_FOR_ASSISTANT_TIME = 100;
258
Julia Reynolds2a128742016-11-28 14:29:25 -0500259 private static final String ACTION_NOTIFICATION_TIMEOUT =
260 NotificationManagerService.class.getSimpleName() + ".TIMEOUT";
261 private static final int REQUEST_CODE_TIMEOUT = 1;
262 private static final String SCHEME_TIMEOUT = "timeout";
263 private static final String EXTRA_KEY = "key";
264
Adam Lesinski182f73f2013-12-05 16:48:06 -0800265 private IActivityManager mAm;
Geoffrey Pitsche75a66e2016-11-22 11:12:11 -0500266 private IPackageManager mPackageManager;
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -0500267 private PackageManager mPackageManagerClient;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800268 AudioManager mAudioManager;
John Spurlockcdb57ae2015-02-11 19:04:11 -0500269 AudioManagerInternal mAudioManagerInternal;
Wei Liu97e56662016-03-04 10:52:33 -0800270 @Nullable StatusBarManagerInternal mStatusBar;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800271 Vibrator mVibrator;
Svetoslav Ganovaa076532016-08-01 19:16:43 -0700272 private WindowManagerInternal mWindowManagerInternal;
Julia Reynolds2a128742016-11-28 14:29:25 -0500273 private AlarmManager mAlarmManager;
Julia Reynolds73ed76b2017-04-04 17:04:38 -0400274 private ICompanionDeviceManager mCompanionManager;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800275
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800276 final IBinder mForegroundToken = new Binder();
Chris Wren93bb8b82016-03-29 14:35:05 -0400277 private Handler mHandler;
Chris Wrenf9536642014-04-17 10:01:54 -0400278 private final HandlerThread mRankingThread = new HandlerThread("ranker",
279 Process.THREAD_PRIORITY_BACKGROUND);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800280
Adam Lesinski182f73f2013-12-05 16:48:06 -0800281 private Light mNotificationLight;
282 Light mAttentionLight;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800283
Daniel Sandleredbb3802012-11-13 20:49:47 -0800284 private long[] mFallbackVibrationPattern;
Chris Wren5116a822014-06-04 15:59:50 -0400285 private boolean mUseAttentionLight;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800286 boolean mSystemReady;
Daniel Sandleredbb3802012-11-13 20:49:47 -0800287
John Spurlockd8afe3c2014-08-01 14:04:07 -0400288 private boolean mDisableNotificationEffects;
John Spurlock32fe4c62014-10-02 12:16:02 -0400289 private int mCallState;
Chris Wren6054e612014-11-25 17:16:46 -0500290 private String mSoundNotificationKey;
291 private String mVibrateNotificationKey;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800292
Bryce Lee7219ada2016-04-08 10:54:23 -0700293 private final SparseArray<ArraySet<ManagedServiceInfo>> mListenersDisablingEffects =
294 new SparseArray<ArraySet<ManagedServiceInfo>>();
295 private List<ComponentName> mEffectsSuppressors = new ArrayList<ComponentName>();
John Spurlockd8afe3c2014-08-01 14:04:07 -0400296 private int mListenerHints; // right now, all hints are global
John Spurlock83104102015-02-12 23:25:12 -0500297 private int mInterruptionFilter = NotificationListenerService.INTERRUPTION_FILTER_UNKNOWN;
John Spurlock1fa865f2014-07-21 14:56:39 -0400298
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500299 // for enabling and disabling notification pulse behavior
Mike Lockwood63b5ad92011-08-30 09:55:30 -0400300 private boolean mScreenOn = true;
Daniel Sandlere96ffb12010-03-11 13:38:06 -0500301 private boolean mInCall = false;
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500302 private boolean mNotificationPulseEnabled;
303
Marta Białka39c992f2011-03-10 10:27:24 +0100304 // for generating notification tones in-call
305 private ToneGenerator mInCallToneGenerator;
306 private final Object mInCallToneGeneratorLock = new Object();
307
Daniel Sandler09a247e2013-02-14 10:24:17 -0500308 // used as a mutex for access to all active notifications & listeners
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -0500309 final Object mNotificationLock = new Object();
Julia Reynolds0839c022017-06-15 15:24:01 -0400310 @GuardedBy("mNotificationLock")
Adam Lesinski182f73f2013-12-05 16:48:06 -0800311 final ArrayList<NotificationRecord> mNotificationList =
Fred Quintana6ecaff12009-09-25 14:23:13 -0700312 new ArrayList<NotificationRecord>();
Julia Reynolds0839c022017-06-15 15:24:01 -0400313 @GuardedBy("mNotificationLock")
John Spurlocka4294292014-03-24 18:02:32 -0400314 final ArrayMap<String, NotificationRecord> mNotificationsByKey =
315 new ArrayMap<String, NotificationRecord>();
Julia Reynolds0839c022017-06-15 15:24:01 -0400316 @GuardedBy("mNotificationLock")
Chris Wren6676dab2016-12-21 18:26:27 -0500317 final ArrayList<NotificationRecord> mEnqueuedNotifications = new ArrayList<>();
Julia Reynolds0839c022017-06-15 15:24:01 -0400318 @GuardedBy("mNotificationLock")
Julia Reynoldseae43fb2016-05-09 12:42:58 -0400319 final ArrayMap<Integer, ArrayMap<String, String>> mAutobundledSummaries = new ArrayMap<>();
Adam Lesinski182f73f2013-12-05 16:48:06 -0800320 final ArrayList<ToastRecord> mToastQueue = new ArrayList<ToastRecord>();
Christoph Studer265c1052014-07-23 17:14:33 +0200321 final ArrayMap<String, NotificationRecord> mSummaryByGroupKey = new ArrayMap<>();
John Spurlock7c74f782015-06-04 13:01:42 -0400322 final PolicyAccess mPolicyAccess = new PolicyAccess();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800323
Chris Wren6054e612014-11-25 17:16:46 -0500324 // The last key in this list owns the hardware.
325 ArrayList<String> mLights = new ArrayList<>();
svetoslavganov75986cf2009-05-14 22:28:01 -0700326
Adam Lesinski182f73f2013-12-05 16:48:06 -0800327 private AppOpsManager mAppOps;
Amith Yamasanif47e51e2015-04-17 10:02:15 -0700328 private UsageStatsManagerInternal mAppUsageStats;
Daniel Sandler4a900ac2013-01-30 14:04:10 -0500329
Griff Hazen9f637d12014-06-10 11:13:51 -0700330 private Archive mArchive;
331
John Spurlock21258a32015-05-27 18:22:55 -0400332 // Persistent storage for notification policy
Daniel Sandler0da673f2012-04-11 12:33:16 -0400333 private AtomicFile mPolicyFile;
John Spurlock21258a32015-05-27 18:22:55 -0400334
Daniel Sandler0da673f2012-04-11 12:33:16 -0400335 private static final int DB_VERSION = 1;
336
John Spurlock21258a32015-05-27 18:22:55 -0400337 private static final String TAG_NOTIFICATION_POLICY = "notification-policy";
Daniel Sandler0da673f2012-04-11 12:33:16 -0400338 private static final String ATTR_VERSION = "version";
339
Chris Wren54bbef42014-07-09 18:37:56 -0400340 private RankingHelper mRankingHelper;
Scott Greenwald9a05b312013-06-28 00:37:54 -0400341
John Spurlockb408e8e2014-04-23 21:12:45 -0400342 private final UserProfiles mUserProfiles = new UserProfiles();
John Spurlock7340fc82014-04-24 18:50:12 -0400343 private NotificationListeners mListeners;
Julia Reynolds77b2cc92016-11-08 14:41:09 -0500344 private NotificationAssistants mNotificationAssistants;
John Spurlock7340fc82014-04-24 18:50:12 -0400345 private ConditionProviders mConditionProviders;
Christoph Studer1c3f81f2014-04-16 15:05:56 +0200346 private NotificationUsageStats mUsageStats;
Christoph Studer546bec82014-03-14 12:17:12 +0100347
John Spurlocke6a7d932014-03-13 12:29:00 -0400348 private static final int MY_UID = Process.myUid();
349 private static final int MY_PID = Process.myPid();
Dianne Hackborn98305522017-05-05 17:53:53 -0700350 private static final IBinder WHITELIST_TOKEN = new Binder();
Chris Wren51017d02015-12-15 15:34:46 -0500351 private RankingHandler mRankingHandler;
Chris Wrenc8673a82016-05-17 17:11:29 -0400352 private long mLastOverRateLogTime;
Chris Wren763a9bb2016-05-31 17:14:12 -0400353 private float mMaxPackageEnqueueRate = DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE;
John Spurlocke6a7d932014-03-13 12:29:00 -0400354
Julia Reynolds72f1cbb2016-09-19 14:57:31 -0400355 private SnoozeHelper mSnoozeHelper;
Julia Reynolds8f488d32016-10-14 10:59:01 -0400356 private GroupHelper mGroupHelper;
Julia Reynolds5f20e9f2017-01-30 08:54:53 -0500357 private boolean mIsTelevision;
Julia Reynolds72f1cbb2016-09-19 14:57:31 -0400358
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500359 private static class Archive {
Griff Hazen9f637d12014-06-10 11:13:51 -0700360 final int mBufferSize;
361 final ArrayDeque<StatusBarNotification> mBuffer;
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500362
Griff Hazen9f637d12014-06-10 11:13:51 -0700363 public Archive(int size) {
364 mBufferSize = size;
365 mBuffer = new ArrayDeque<StatusBarNotification>(mBufferSize);
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500366 }
Jeff Sharkey0c1baf92013-04-03 13:08:52 -0700367
Daniel Sandler5e62e3a2013-04-15 20:57:02 -0400368 public String toString() {
369 final StringBuilder sb = new StringBuilder();
370 final int N = mBuffer.size();
371 sb.append("Archive (");
372 sb.append(N);
373 sb.append(" notification");
374 sb.append((N==1)?")":"s)");
375 return sb.toString();
376 }
377
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500378 public void record(StatusBarNotification nr) {
Griff Hazen9f637d12014-06-10 11:13:51 -0700379 if (mBuffer.size() == mBufferSize) {
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500380 mBuffer.removeFirst();
381 }
Daniel Sandler26b81d52013-05-20 20:56:43 -0400382
383 // We don't want to store the heavy bits of the notification in the archive,
384 // but other clients in the system process might be using the object, so we
385 // store a (lightened) copy.
386 mBuffer.addLast(nr.cloneLight());
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500387 }
388
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500389 public Iterator<StatusBarNotification> descendingIterator() {
390 return mBuffer.descendingIterator();
391 }
Daniel Sandler78d0d252013-02-12 08:14:52 -0500392
393 public StatusBarNotification[] getArray(int count) {
Griff Hazen9f637d12014-06-10 11:13:51 -0700394 if (count == 0) count = mBufferSize;
Daniel Sandler78d0d252013-02-12 08:14:52 -0500395 final StatusBarNotification[] a
396 = new StatusBarNotification[Math.min(count, mBuffer.size())];
397 Iterator<StatusBarNotification> iter = descendingIterator();
398 int i=0;
399 while (iter.hasNext() && i < count) {
400 a[i++] = iter.next();
401 }
402 return a;
403 }
404
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500405 }
406
John Spurlock35ef0a62015-05-28 11:24:10 -0400407 private void readPolicyXml(InputStream stream, boolean forRestore)
408 throws XmlPullParserException, NumberFormatException, IOException {
409 final XmlPullParser parser = Xml.newPullParser();
410 parser.setInput(stream, StandardCharsets.UTF_8.name());
411
Chris Wrenacf424a2016-03-15 12:48:55 -0400412 while (parser.next() != END_DOCUMENT) {
John Spurlock35ef0a62015-05-28 11:24:10 -0400413 mZenModeHelper.readXml(parser, forRestore);
414 mRankingHelper.readXml(parser, forRestore);
415 }
416 }
417
John Spurlock056c5192014-04-20 21:52:01 -0400418 private void loadPolicyFile() {
John Spurlock21258a32015-05-27 18:22:55 -0400419 if (DBG) Slog.d(TAG, "loadPolicyFile");
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -0500420 synchronized (mPolicyFile) {
Daniel Sandler0da673f2012-04-11 12:33:16 -0400421
John Spurlock056c5192014-04-20 21:52:01 -0400422 FileInputStream infile = null;
423 try {
424 infile = mPolicyFile.openRead();
John Spurlock35ef0a62015-05-28 11:24:10 -0400425 readPolicyXml(infile, false /*forRestore*/);
John Spurlock056c5192014-04-20 21:52:01 -0400426 } catch (FileNotFoundException e) {
427 // No data yet
428 } catch (IOException e) {
429 Log.wtf(TAG, "Unable to read notification policy", e);
430 } catch (NumberFormatException e) {
431 Log.wtf(TAG, "Unable to parse notification policy", e);
432 } catch (XmlPullParserException e) {
433 Log.wtf(TAG, "Unable to parse notification policy", e);
434 } finally {
435 IoUtils.closeQuietly(infile);
436 }
437 }
438 }
439
440 public void savePolicyFile() {
441 mHandler.removeMessages(MESSAGE_SAVE_POLICY_FILE);
442 mHandler.sendEmptyMessage(MESSAGE_SAVE_POLICY_FILE);
443 }
444
445 private void handleSavePolicyFile() {
John Spurlock21258a32015-05-27 18:22:55 -0400446 if (DBG) Slog.d(TAG, "handleSavePolicyFile");
John Spurlock056c5192014-04-20 21:52:01 -0400447 synchronized (mPolicyFile) {
448 final FileOutputStream stream;
449 try {
450 stream = mPolicyFile.startWrite();
451 } catch (IOException e) {
452 Slog.w(TAG, "Failed to save policy file", e);
453 return;
454 }
455
456 try {
John Spurlock35ef0a62015-05-28 11:24:10 -0400457 writePolicyXml(stream, false /*forBackup*/);
John Spurlock056c5192014-04-20 21:52:01 -0400458 mPolicyFile.finishWrite(stream);
459 } catch (IOException e) {
460 Slog.w(TAG, "Failed to save policy file, restoring backup", e);
461 mPolicyFile.failWrite(stream);
Daniel Sandler0da673f2012-04-11 12:33:16 -0400462 }
463 }
John Spurlock35ef0a62015-05-28 11:24:10 -0400464 BackupManager.dataChanged(getContext().getPackageName());
465 }
466
467 private void writePolicyXml(OutputStream stream, boolean forBackup) throws IOException {
468 final XmlSerializer out = new FastXmlSerializer();
469 out.setOutput(stream, StandardCharsets.UTF_8.name());
470 out.startDocument(null, true);
471 out.startTag(null, TAG_NOTIFICATION_POLICY);
472 out.attribute(null, ATTR_VERSION, Integer.toString(DB_VERSION));
473 mZenModeHelper.writeXml(out, forBackup);
474 mRankingHelper.writeXml(out, forBackup);
475 out.endTag(null, TAG_NOTIFICATION_POLICY);
476 out.endDocument();
Daniel Sandler0da673f2012-04-11 12:33:16 -0400477 }
478
Chris Wren66189fc2015-06-25 14:04:33 -0400479 /** Use this to check if a package can post a notification or toast. */
480 private boolean checkNotificationOp(String pkg, int uid) {
481 return mAppOps.checkOp(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
Andrei Stingaceanu355b2322016-02-12 16:43:51 +0000482 == AppOpsManager.MODE_ALLOWED && !isPackageSuspendedForUser(pkg, uid);
Chris Wren66189fc2015-06-25 14:04:33 -0400483 }
484
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800485 private static final class ToastRecord
486 {
487 final int pid;
488 final String pkg;
489 final ITransientNotification callback;
490 int duration;
Svetoslav Ganovaa076532016-08-01 19:16:43 -0700491 Binder token;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800492
Svetoslav Ganovaa076532016-08-01 19:16:43 -0700493 ToastRecord(int pid, String pkg, ITransientNotification callback, int duration,
494 Binder token) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800495 this.pid = pid;
496 this.pkg = pkg;
497 this.callback = callback;
498 this.duration = duration;
Svetoslav Ganovaa076532016-08-01 19:16:43 -0700499 this.token = token;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800500 }
501
502 void update(int duration) {
503 this.duration = duration;
504 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800505
John Spurlock25e2d242014-06-27 13:58:23 -0400506 void dump(PrintWriter pw, String prefix, DumpFilter filter) {
507 if (filter != null && !filter.matches(pkg)) return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800508 pw.println(prefix + this);
509 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800510
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800511 @Override
512 public final String toString()
513 {
514 return "ToastRecord{"
515 + Integer.toHexString(System.identityHashCode(this))
516 + " pkg=" + pkg
517 + " callback=" + callback
518 + " duration=" + duration;
519 }
520 }
521
Adam Lesinski182f73f2013-12-05 16:48:06 -0800522 private final NotificationDelegate mNotificationDelegate = new NotificationDelegate() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800523
Adam Lesinski182f73f2013-12-05 16:48:06 -0800524 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800525 public void onSetDisabled(int status) {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -0500526 synchronized (mNotificationLock) {
John Spurlockd8afe3c2014-08-01 14:04:07 -0400527 mDisableNotificationEffects =
528 (status & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0;
John Spurlock32fe4c62014-10-02 12:16:02 -0400529 if (disableNotificationEffects(null) != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800530 // cancel whatever's going on
531 long identity = Binder.clearCallingIdentity();
532 try {
Adam Lesinski182f73f2013-12-05 16:48:06 -0800533 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
Jeff Sharkey098d5802012-04-26 17:30:34 -0700534 if (player != null) {
535 player.stopAsync();
536 }
537 } catch (RemoteException e) {
538 } finally {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800539 Binder.restoreCallingIdentity(identity);
540 }
541
542 identity = Binder.clearCallingIdentity();
543 try {
544 mVibrator.cancel();
Jeff Sharkey098d5802012-04-26 17:30:34 -0700545 } finally {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800546 Binder.restoreCallingIdentity(identity);
547 }
548 }
549 }
550 }
551
Adam Lesinski182f73f2013-12-05 16:48:06 -0800552 @Override
John Spurlocke6a7d932014-03-13 12:29:00 -0400553 public void onClearAll(int callingUid, int callingPid, int userId) {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -0500554 synchronized (mNotificationLock) {
Julia Reynoldsf619bc52017-03-17 08:32:23 -0400555 cancelAllLocked(callingUid, callingPid, userId, REASON_CANCEL_ALL, null,
Kenny Guya263e4e2014-03-03 18:24:03 +0000556 /*includeCurrentProfiles*/ true);
Adam Lesinskie8240262014-03-26 16:01:00 -0700557 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800558 }
559
Adam Lesinski182f73f2013-12-05 16:48:06 -0800560 @Override
Christoph Studer03b87a22014-04-30 17:33:27 +0200561 public void onNotificationClick(int callingUid, int callingPid, String key) {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -0500562 synchronized (mNotificationLock) {
Christoph Studer03b87a22014-04-30 17:33:27 +0200563 NotificationRecord r = mNotificationsByKey.get(key);
564 if (r == null) {
565 Log.w(TAG, "No notification with key: " + key);
566 return;
567 }
Chris Wrene6ddb8a2015-05-27 15:21:00 -0400568 final long now = System.currentTimeMillis();
Chris Wren9eb5e102017-01-26 13:15:06 -0500569 MetricsLogger.action(r.getLogMaker(now)
570 .setCategory(MetricsEvent.NOTIFICATION_ITEM)
571 .setType(MetricsEvent.TYPE_ACTION));
Chris Wrene6ddb8a2015-05-27 15:21:00 -0400572 EventLogTags.writeNotificationClicked(key,
573 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
574
Christoph Studer03b87a22014-04-30 17:33:27 +0200575 StatusBarNotification sbn = r.sbn;
576 cancelNotification(callingUid, callingPid, sbn.getPackageName(), sbn.getTag(),
577 sbn.getId(), Notification.FLAG_AUTO_CANCEL,
578 Notification.FLAG_FOREGROUND_SERVICE, false, r.getUserId(),
Julia Reynoldsf619bc52017-03-17 08:32:23 -0400579 REASON_CLICK, null);
Christoph Studer03b87a22014-04-30 17:33:27 +0200580 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800581 }
582
Adam Lesinski182f73f2013-12-05 16:48:06 -0800583 @Override
Christoph Studer4da84cd2014-10-21 17:24:20 +0200584 public void onNotificationActionClick(int callingUid, int callingPid, String key,
585 int actionIndex) {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -0500586 synchronized (mNotificationLock) {
Christoph Studer4da84cd2014-10-21 17:24:20 +0200587 NotificationRecord r = mNotificationsByKey.get(key);
588 if (r == null) {
589 Log.w(TAG, "No notification with key: " + key);
590 return;
591 }
Chris Wrene6ddb8a2015-05-27 15:21:00 -0400592 final long now = System.currentTimeMillis();
Chris Wren9eb5e102017-01-26 13:15:06 -0500593 MetricsLogger.action(r.getLogMaker(now)
594 .setCategory(MetricsEvent.NOTIFICATION_ITEM_ACTION)
595 .setType(MetricsEvent.TYPE_ACTION)
596 .setSubtype(actionIndex));
Chris Wrene6ddb8a2015-05-27 15:21:00 -0400597 EventLogTags.writeNotificationActionClicked(key, actionIndex,
598 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
Christoph Studer4da84cd2014-10-21 17:24:20 +0200599 // TODO: Log action click via UsageStats.
600 }
601 }
602
603 @Override
John Spurlocke6a7d932014-03-13 12:29:00 -0400604 public void onNotificationClear(int callingUid, int callingPid,
605 String pkg, String tag, int id, int userId) {
606 cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
Kenny Guy3a7c4a52014-03-03 18:24:03 +0000607 Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
Julia Reynoldsf619bc52017-03-17 08:32:23 -0400608 true, userId, REASON_CANCEL, null);
Daniel Sandler0f0b11c2010-08-04 15:54:58 -0400609 }
610
Adam Lesinski182f73f2013-12-05 16:48:06 -0800611 @Override
Chris Wrenb659c4f2015-06-25 17:12:27 -0400612 public void onPanelRevealed(boolean clearEffects, int items) {
Chris Wren9eb5e102017-01-26 13:15:06 -0500613 MetricsLogger.visible(getContext(), MetricsEvent.NOTIFICATION_PANEL);
Chris Wren621933f2017-06-14 15:59:03 -0400614 MetricsLogger.histogram(getContext(), "note_load", items);
Chris Wrenb659c4f2015-06-25 17:12:27 -0400615 EventLogTags.writeNotificationPanelRevealed(items);
Christoph Studer1f32c652014-11-26 15:32:20 +0100616 if (clearEffects) {
617 clearEffects();
618 }
619 }
620
621 @Override
622 public void onPanelHidden() {
Chris Wren9eb5e102017-01-26 13:15:06 -0500623 MetricsLogger.hidden(getContext(), MetricsEvent.NOTIFICATION_PANEL);
Christoph Studer1f32c652014-11-26 15:32:20 +0100624 EventLogTags.writeNotificationPanelHidden();
625 }
626
627 @Override
628 public void clearEffects() {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -0500629 synchronized (mNotificationLock) {
Christoph Studer1f32c652014-11-26 15:32:20 +0100630 if (DBG) Slog.d(TAG, "clearEffects");
Chris Wren93bb8b82016-03-29 14:35:05 -0400631 clearSoundLocked();
632 clearVibrateLocked();
633 clearLightsLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800634 }
635 }
Joe Onorato005847b2010-06-04 16:08:02 -0400636
Adam Lesinski182f73f2013-12-05 16:48:06 -0800637 @Override
John Spurlocke6a7d932014-03-13 12:29:00 -0400638 public void onNotificationError(int callingUid, int callingPid, String pkg, String tag, int id,
Kenny Guy3a7c4a52014-03-03 18:24:03 +0000639 int uid, int initialPid, String message, int userId) {
Daniel Sandlerd0a2f862010-08-03 15:29:31 -0400640 Slog.d(TAG, "onNotification error pkg=" + pkg + " tag=" + tag + " id=" + id
641 + "; will crashApplication(uid=" + uid + ", pid=" + initialPid + ")");
John Spurlocke6a7d932014-03-13 12:29:00 -0400642 cancelNotification(callingUid, callingPid, pkg, tag, id, 0, 0, false, userId,
Julia Reynoldsf619bc52017-03-17 08:32:23 -0400643 REASON_ERROR, null);
Dianne Hackborn9d39d0c2010-06-24 15:57:42 -0700644 long ident = Binder.clearCallingIdentity();
645 try {
Christopher Tate8aa8fe12017-01-20 17:50:32 -0800646 ActivityManager.getService().crashApplication(uid, initialPid, pkg, -1,
Dianne Hackborn9d39d0c2010-06-24 15:57:42 -0700647 "Bad notification posted from package " + pkg
648 + ": " + message);
649 } catch (RemoteException e) {
650 }
651 Binder.restoreCallingIdentity(ident);
Joe Onorato005847b2010-06-04 16:08:02 -0400652 }
John Spurlocke677d712014-02-13 12:52:19 -0500653
654 @Override
Chris Wrend1dbc922015-06-19 17:51:16 -0400655 public void onNotificationVisibilityChanged(NotificationVisibility[] newlyVisibleKeys,
656 NotificationVisibility[] noLongerVisibleKeys) {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -0500657 synchronized (mNotificationLock) {
Chris Wrend1dbc922015-06-19 17:51:16 -0400658 for (NotificationVisibility nv : newlyVisibleKeys) {
659 NotificationRecord r = mNotificationsByKey.get(nv.key);
Christoph Studerffeb0c32014-05-07 22:23:56 +0200660 if (r == null) continue;
Chris Wrend1dbc922015-06-19 17:51:16 -0400661 r.setVisibility(true, nv.rank);
662 nv.recycle();
Christoph Studerffeb0c32014-05-07 22:23:56 +0200663 }
664 // Note that we might receive this event after notifications
665 // have already left the system, e.g. after dismissing from the
666 // shade. Hence not finding notifications in
667 // mNotificationsByKey is not an exceptional condition.
Chris Wrend1dbc922015-06-19 17:51:16 -0400668 for (NotificationVisibility nv : noLongerVisibleKeys) {
669 NotificationRecord r = mNotificationsByKey.get(nv.key);
Christoph Studerffeb0c32014-05-07 22:23:56 +0200670 if (r == null) continue;
Chris Wrend1dbc922015-06-19 17:51:16 -0400671 r.setVisibility(false, nv.rank);
672 nv.recycle();
Christoph Studerffeb0c32014-05-07 22:23:56 +0200673 }
674 }
Christoph Studer92b389d2014-04-01 18:44:40 +0200675 }
Chris Wren78403d72014-07-28 10:23:24 +0100676
677 @Override
678 public void onNotificationExpansionChanged(String key,
679 boolean userAction, boolean expanded) {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -0500680 synchronized (mNotificationLock) {
Chris Wren78403d72014-07-28 10:23:24 +0100681 NotificationRecord r = mNotificationsByKey.get(key);
682 if (r != null) {
683 r.stats.onExpansionChanged(userAction, expanded);
Chris Wrene6ddb8a2015-05-27 15:21:00 -0400684 final long now = System.currentTimeMillis();
Chris Wren9eb5e102017-01-26 13:15:06 -0500685 MetricsLogger.action(r.getLogMaker(now)
686 .setCategory(MetricsEvent.NOTIFICATION_ITEM)
687 .setType(MetricsEvent.TYPE_DETAIL));
Chris Wrene6ddb8a2015-05-27 15:21:00 -0400688 EventLogTags.writeNotificationExpansion(key,
689 userAction ? 1 : 0, expanded ? 1 : 0,
690 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
Chris Wren78403d72014-07-28 10:23:24 +0100691 }
692 }
693 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800694 };
695
Julia Reynolds88860ce2017-06-01 16:55:49 -0400696 @GuardedBy("mNotificationLock")
Chris Wren93bb8b82016-03-29 14:35:05 -0400697 private void clearSoundLocked() {
698 mSoundNotificationKey = null;
699 long identity = Binder.clearCallingIdentity();
700 try {
701 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
702 if (player != null) {
703 player.stopAsync();
704 }
705 } catch (RemoteException e) {
706 } finally {
707 Binder.restoreCallingIdentity(identity);
708 }
709 }
710
Julia Reynolds88860ce2017-06-01 16:55:49 -0400711 @GuardedBy("mNotificationLock")
Chris Wren93bb8b82016-03-29 14:35:05 -0400712 private void clearVibrateLocked() {
713 mVibrateNotificationKey = null;
714 long identity = Binder.clearCallingIdentity();
715 try {
716 mVibrator.cancel();
717 } finally {
718 Binder.restoreCallingIdentity(identity);
719 }
720 }
721
Julia Reynolds88860ce2017-06-01 16:55:49 -0400722 @GuardedBy("mNotificationLock")
Chris Wren93bb8b82016-03-29 14:35:05 -0400723 private void clearLightsLocked() {
724 // light
725 mLights.clear();
726 updateLightsLocked();
727 }
728
Julia Reynolds2a128742016-11-28 14:29:25 -0500729 private final BroadcastReceiver mNotificationTimeoutReceiver = new BroadcastReceiver() {
730 @Override
731 public void onReceive(Context context, Intent intent) {
732 String action = intent.getAction();
733 if (action == null) {
734 return;
735 }
736 if (ACTION_NOTIFICATION_TIMEOUT.equals(action)) {
737 final NotificationRecord record;
738 synchronized (mNotificationLock) {
739 record = findNotificationByKeyLocked(intent.getStringExtra(EXTRA_KEY));
740 }
741 if (record != null) {
742 cancelNotification(record.sbn.getUid(), record.sbn.getInitialPid(),
743 record.sbn.getPackageName(), record.sbn.getTag(),
744 record.sbn.getId(), 0,
745 Notification.FLAG_FOREGROUND_SERVICE, true, record.getUserId(),
746 REASON_TIMEOUT, null);
747 }
748 }
749 }
750 };
751
Kenny Guy70058402014-10-28 20:45:06 +0000752 private final BroadcastReceiver mPackageIntentReceiver = new BroadcastReceiver() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800753 @Override
754 public void onReceive(Context context, Intent intent) {
755 String action = intent.getAction();
Dianne Hackborn29cd7f12015-01-08 10:37:05 -0800756 if (action == null) {
757 return;
758 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800759
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800760 boolean queryRestart = false;
Chris Wrenae9bb572013-05-15 14:50:28 -0400761 boolean queryRemove = false;
Daniel Sandler26ece572012-06-01 15:38:46 -0400762 boolean packageChanged = false;
John Spurlock79f78922013-05-16 09:10:05 -0400763 boolean cancelNotifications = true;
Andrei Stingaceanu0122f6512016-01-22 15:33:03 +0000764 int reason = REASON_PACKAGE_CHANGED;
Chris Wrenf9536642014-04-17 10:01:54 -0400765
Chris Wren3da73022013-05-10 14:41:21 -0400766 if (action.equals(Intent.ACTION_PACKAGE_ADDED)
Chris Wrenae9bb572013-05-15 14:50:28 -0400767 || (queryRemove=action.equals(Intent.ACTION_PACKAGE_REMOVED))
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800768 || action.equals(Intent.ACTION_PACKAGE_RESTARTED)
Daniel Sandler26ece572012-06-01 15:38:46 -0400769 || (packageChanged=action.equals(Intent.ACTION_PACKAGE_CHANGED))
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800770 || (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART))
Andrei Stingaceanu0122f6512016-01-22 15:33:03 +0000771 || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)
772 || action.equals(Intent.ACTION_PACKAGES_SUSPENDED)) {
Kenny Guy70058402014-10-28 20:45:06 +0000773 int changeUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
774 UserHandle.USER_ALL);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800775 String pkgList[] = null;
Julia Reynolds4036e8d2017-01-13 09:50:05 -0500776 int uidList[] = null;
Julia Reynolds6434eb22016-08-08 17:19:26 -0400777 boolean removingPackage = queryRemove &&
778 !intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
779 if (DBG) Slog.i(TAG, "action=" + action + " removing=" + removingPackage);
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800780 if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800781 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
Julia Reynolds4036e8d2017-01-13 09:50:05 -0500782 uidList = intent.getIntArrayExtra(Intent.EXTRA_CHANGED_UID_LIST);
Andrei Stingaceanu0122f6512016-01-22 15:33:03 +0000783 } else if (action.equals(Intent.ACTION_PACKAGES_SUSPENDED)) {
784 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
785 reason = REASON_PACKAGE_SUSPENDED;
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800786 } else if (queryRestart) {
787 pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
Julia Reynolds4036e8d2017-01-13 09:50:05 -0500788 uidList = new int[] {intent.getIntExtra(Intent.EXTRA_UID, -1)};
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800789 } else {
790 Uri uri = intent.getData();
791 if (uri == null) {
792 return;
793 }
794 String pkgName = uri.getSchemeSpecificPart();
795 if (pkgName == null) {
796 return;
797 }
Daniel Sandler26ece572012-06-01 15:38:46 -0400798 if (packageChanged) {
799 // We cancel notifications for packages which have just been disabled
Christopher Tate06e5fed2013-10-09 14:39:15 -0700800 try {
Geoffrey Pitsche75a66e2016-11-22 11:12:11 -0500801 final int enabled = mPackageManager.getApplicationEnabledSetting(
802 pkgName,
Kenny Guy70058402014-10-28 20:45:06 +0000803 changeUserId != UserHandle.USER_ALL ? changeUserId :
Geoffrey Pitsche75a66e2016-11-22 11:12:11 -0500804 UserHandle.USER_SYSTEM);
Christopher Tate06e5fed2013-10-09 14:39:15 -0700805 if (enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED
806 || enabled == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
807 cancelNotifications = false;
808 }
809 } catch (IllegalArgumentException e) {
810 // Package doesn't exist; probably racing with uninstall.
811 // cancelNotifications is already true, so nothing to do here.
812 if (DBG) {
813 Slog.i(TAG, "Exception trying to look up app enabled setting", e);
814 }
Kenny Guy70058402014-10-28 20:45:06 +0000815 } catch (RemoteException e) {
816 // Failed to talk to PackageManagerService Should never happen!
Daniel Sandler26ece572012-06-01 15:38:46 -0400817 }
818 }
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800819 pkgList = new String[]{pkgName};
Julia Reynolds4036e8d2017-01-13 09:50:05 -0500820 uidList = new int[] {intent.getIntExtra(Intent.EXTRA_UID, -1)};
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800821 }
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800822 if (pkgList != null && (pkgList.length > 0)) {
823 for (String pkgName : pkgList) {
John Spurlock79f78922013-05-16 09:10:05 -0400824 if (cancelNotifications) {
Julia Reynoldsb5e44b72016-08-16 15:00:25 -0400825 cancelAllNotificationsInt(MY_UID, MY_PID, pkgName, null, 0, 0,
826 !queryRestart, changeUserId, reason, null);
John Spurlock79f78922013-05-16 09:10:05 -0400827 }
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800828 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800829 }
Julia Reynolds6434eb22016-08-08 17:19:26 -0400830 mListeners.onPackagesChanged(removingPackage, pkgList);
Julia Reynolds77b2cc92016-11-08 14:41:09 -0500831 mNotificationAssistants.onPackagesChanged(removingPackage, pkgList);
Julia Reynolds6434eb22016-08-08 17:19:26 -0400832 mConditionProviders.onPackagesChanged(removingPackage, pkgList);
Julia Reynolds4036e8d2017-01-13 09:50:05 -0500833 mRankingHelper.onPackagesChanged(removingPackage, changeUserId, pkgList, uidList);
834 savePolicyFile();
Kenny Guy70058402014-10-28 20:45:06 +0000835 }
836 }
837 };
838
839 private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
840 @Override
841 public void onReceive(Context context, Intent intent) {
842 String action = intent.getAction();
843
844 if (action.equals(Intent.ACTION_SCREEN_ON)) {
Mike Lockwood63b5ad92011-08-30 09:55:30 -0400845 // Keep track of screen on/off state, but do not turn off the notification light
846 // until user passes through the lock screen or views the notification.
847 mScreenOn = true;
Christoph Studer1f32c652014-11-26 15:32:20 +0100848 updateNotificationPulse();
Mike Lockwood63b5ad92011-08-30 09:55:30 -0400849 } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
850 mScreenOn = false;
Christoph Studer1f32c652014-11-26 15:32:20 +0100851 updateNotificationPulse();
Daniel Sandlere96ffb12010-03-11 13:38:06 -0500852 } else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) {
John Spurlock5d2eeb12014-01-16 10:46:36 -0500853 mInCall = TelephonyManager.EXTRA_STATE_OFFHOOK
854 .equals(intent.getStringExtra(TelephonyManager.EXTRA_STATE));
Daniel Sandlere96ffb12010-03-11 13:38:06 -0500855 updateNotificationPulse();
Marta Białka39c992f2011-03-10 10:27:24 +0100856 synchronized (mInCallToneGeneratorLock) {
857 if (mInCall) {
858 if (mInCallToneGenerator == null) {
859 int relativeToneVolume = getContext().getResources().getInteger(
860 R.integer.config_inCallNotificationVolumeRelative);
861 if (relativeToneVolume < ToneGenerator.MIN_VOLUME
862 || relativeToneVolume > ToneGenerator.MAX_VOLUME) {
863 relativeToneVolume = ToneGenerator.MAX_VOLUME;
864 }
865 try {
866 mInCallToneGenerator = new ToneGenerator(
867 AudioManager.STREAM_VOICE_CALL, relativeToneVolume);
868 } catch (RuntimeException e) {
869 Log.e(TAG, "Error creating local tone generator: " + e);
870 mInCallToneGenerator = null;
871 }
872 }
873 } else {
874 if (mInCallToneGenerator != null) {
875 mInCallToneGenerator.release();
876 mInCallToneGenerator = null;
877 }
878 }
879 }
Dianne Hackborn80a4af22012-08-27 19:18:31 -0700880 } else if (action.equals(Intent.ACTION_USER_STOPPED)) {
881 int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
882 if (userHandle >= 0) {
Julia Reynoldsb5e44b72016-08-16 15:00:25 -0400883 cancelAllNotificationsInt(MY_UID, MY_PID, null, null, 0, 0, true, userHandle,
Julia Reynoldsef37f282016-02-12 09:11:27 -0500884 REASON_USER_STOPPED, null);
Dianne Hackborn80a4af22012-08-27 19:18:31 -0700885 }
Rubin Xue95057a2016-04-01 16:49:25 +0100886 } else if (action.equals(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)) {
Rubin Xu7eadc1b2016-02-01 16:13:45 +0000887 int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
Rubin Xue95057a2016-04-01 16:49:25 +0100888 if (userHandle >= 0) {
Julia Reynoldsb5e44b72016-08-16 15:00:25 -0400889 cancelAllNotificationsInt(MY_UID, MY_PID, null, null, 0, 0, true, userHandle,
Julia Reynoldsef37f282016-02-12 09:11:27 -0500890 REASON_PROFILE_TURNED_OFF, null);
Rubin Xu7eadc1b2016-02-01 16:13:45 +0000891 }
Mike Lockwood63b5ad92011-08-30 09:55:30 -0400892 } else if (action.equals(Intent.ACTION_USER_PRESENT)) {
893 // turn off LED when user passes through lock screen
894 mNotificationLight.turnOff();
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400895 } else if (action.equals(Intent.ACTION_USER_SWITCHED)) {
John Spurlock1b8b22b2015-05-20 09:47:13 -0400896 final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400897 // reload per-user settings
898 mSettingsObserver.update(null);
John Spurlockb408e8e2014-04-23 21:12:45 -0400899 mUserProfiles.updateCache(context);
Christoph Studerb53dfd42014-09-12 14:45:59 +0200900 // Refresh managed services
John Spurlock1b8b22b2015-05-20 09:47:13 -0400901 mConditionProviders.onUserSwitched(user);
902 mListeners.onUserSwitched(user);
Julia Reynolds77b2cc92016-11-08 14:41:09 -0500903 mNotificationAssistants.onUserSwitched(user);
John Spurlock21258a32015-05-27 18:22:55 -0400904 mZenModeHelper.onUserSwitched(user);
Kenny Guy3a7c4a52014-03-03 18:24:03 +0000905 } else if (action.equals(Intent.ACTION_USER_ADDED)) {
John Spurlockb408e8e2014-04-23 21:12:45 -0400906 mUserProfiles.updateCache(context);
John Spurlock21258a32015-05-27 18:22:55 -0400907 } else if (action.equals(Intent.ACTION_USER_REMOVED)) {
908 final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
909 mZenModeHelper.onUserRemoved(user);
Julia Reynolds2e9bf5f2017-05-03 13:23:30 -0400910 mRankingHelper.onUserRemoved(user);
911 savePolicyFile();
Julia Reynoldsa3dcaff2016-02-03 15:04:05 -0500912 } else if (action.equals(Intent.ACTION_USER_UNLOCKED)) {
913 final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
914 mConditionProviders.onUserUnlocked(user);
915 mListeners.onUserUnlocked(user);
Julia Reynolds77b2cc92016-11-08 14:41:09 -0500916 mNotificationAssistants.onUserUnlocked(user);
Julia Reynoldsa3dcaff2016-02-03 15:04:05 -0500917 mZenModeHelper.onUserUnlocked(user);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800918 }
919 }
920 };
921
John Spurlock7c74f782015-06-04 13:01:42 -0400922 private final class SettingsObserver extends ContentObserver {
Chris Wren89aa2262017-05-05 18:05:56 -0400923 private final Uri NOTIFICATION_BADGING_URI
924 = Settings.Secure.getUriFor(Settings.Secure.NOTIFICATION_BADGING);
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400925 private final Uri NOTIFICATION_LIGHT_PULSE_URI
926 = Settings.System.getUriFor(Settings.System.NOTIFICATION_LIGHT_PULSE);
Chris Wren763a9bb2016-05-31 17:14:12 -0400927 private final Uri NOTIFICATION_RATE_LIMIT_URI
928 = Settings.Global.getUriFor(Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE);
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400929
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700930 SettingsObserver(Handler handler) {
931 super(handler);
932 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800933
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700934 void observe() {
Adam Lesinski182f73f2013-12-05 16:48:06 -0800935 ContentResolver resolver = getContext().getContentResolver();
Chris Wren89aa2262017-05-05 18:05:56 -0400936 resolver.registerContentObserver(NOTIFICATION_BADGING_URI,
937 false, this, UserHandle.USER_ALL);
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400938 resolver.registerContentObserver(NOTIFICATION_LIGHT_PULSE_URI,
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700939 false, this, UserHandle.USER_ALL);
Chris Wren763a9bb2016-05-31 17:14:12 -0400940 resolver.registerContentObserver(NOTIFICATION_RATE_LIMIT_URI,
941 false, this, UserHandle.USER_ALL);
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400942 update(null);
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700943 }
944
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400945 @Override public void onChange(boolean selfChange, Uri uri) {
946 update(uri);
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700947 }
948
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400949 public void update(Uri uri) {
Adam Lesinski182f73f2013-12-05 16:48:06 -0800950 ContentResolver resolver = getContext().getContentResolver();
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400951 if (uri == null || NOTIFICATION_LIGHT_PULSE_URI.equals(uri)) {
zhoulei7e376972017-05-17 18:41:25 +0800952 boolean pulseEnabled = Settings.System.getIntForUser(resolver,
953 Settings.System.NOTIFICATION_LIGHT_PULSE, 0, UserHandle.USER_CURRENT) != 0;
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400954 if (mNotificationPulseEnabled != pulseEnabled) {
955 mNotificationPulseEnabled = pulseEnabled;
956 updateNotificationPulse();
957 }
958 }
Chris Wren763a9bb2016-05-31 17:14:12 -0400959 if (uri == null || NOTIFICATION_RATE_LIMIT_URI.equals(uri)) {
960 mMaxPackageEnqueueRate = Settings.Global.getFloat(resolver,
961 Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE, mMaxPackageEnqueueRate);
962 }
Chris Wren89aa2262017-05-05 18:05:56 -0400963 if (uri == null || NOTIFICATION_BADGING_URI.equals(uri)) {
964 mRankingHelper.updateBadgingEnabled();
965 }
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700966 }
967 }
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500968
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400969 private SettingsObserver mSettingsObserver;
John Spurlock056c5192014-04-20 21:52:01 -0400970 private ZenModeHelper mZenModeHelper;
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400971
Daniel Sandleredbb3802012-11-13 20:49:47 -0800972 static long[] getLongArray(Resources r, int resid, int maxlen, long[] def) {
973 int[] ar = r.getIntArray(resid);
974 if (ar == null) {
975 return def;
976 }
977 final int len = ar.length > maxlen ? maxlen : ar.length;
978 long[] out = new long[len];
979 for (int i=0; i<len; i++) {
980 out[i] = ar[i];
981 }
982 return out;
983 }
984
Jeff Brownb880d882014-02-10 19:47:07 -0800985 public NotificationManagerService(Context context) {
986 super(context);
Dianne Hackborn98305522017-05-05 17:53:53 -0700987 Notification.processWhitelistToken = WHITELIST_TOKEN;
Jeff Brownb880d882014-02-10 19:47:07 -0800988 }
989
Geoffrey Pitsche75a66e2016-11-22 11:12:11 -0500990 // TODO - replace these methods with a single VisibleForTesting constructor
Chris Wren93bb8b82016-03-29 14:35:05 -0400991 @VisibleForTesting
992 void setAudioManager(AudioManager audioMananger) {
993 mAudioManager = audioMananger;
994 }
995
996 @VisibleForTesting
997 void setVibrator(Vibrator vibrator) {
998 mVibrator = vibrator;
999 }
1000
1001 @VisibleForTesting
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001002 void setLights(Light light) {
1003 mNotificationLight = light;
1004 mAttentionLight = light;
Julia Reynolds033a4122017-01-31 16:50:38 -05001005 mNotificationPulseEnabled = true;
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001006 }
1007
1008 @VisibleForTesting
1009 void setScreenOn(boolean on) {
1010 mScreenOn = on;
1011 }
1012
1013 @VisibleForTesting
1014 void addNotification(NotificationRecord r) {
1015 mNotificationList.add(r);
1016 mNotificationsByKey.put(r.sbn.getKey(), r);
Julia Reynoldsa78cdff2017-04-26 10:19:25 -04001017 if (r.sbn.isGroup()) {
1018 mSummaryByGroupKey.put(r.getGroupKey(), r);
1019 }
1020 }
1021
1022 @VisibleForTesting
1023 void addEnqueuedNotification(NotificationRecord r) {
1024 mEnqueuedNotifications.add(r);
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001025 }
1026
1027 @VisibleForTesting
Chris Wren93bb8b82016-03-29 14:35:05 -04001028 void setSystemReady(boolean systemReady) {
1029 mSystemReady = systemReady;
1030 }
1031
1032 @VisibleForTesting
1033 void setHandler(Handler handler) {
1034 mHandler = handler;
1035 }
1036
Chris Wrend4054312016-06-24 17:07:40 -04001037 @VisibleForTesting
Julia Reynolds0c299d42016-11-15 14:37:04 -05001038 void setFallbackVibrationPattern(long[] vibrationPattern) {
1039 mFallbackVibrationPattern = vibrationPattern;
Chris Wrend4054312016-06-24 17:07:40 -04001040 }
1041
Geoffrey Pitsche75a66e2016-11-22 11:12:11 -05001042 @VisibleForTesting
1043 void setPackageManager(IPackageManager packageManager) {
1044 mPackageManager = packageManager;
1045 }
1046
Julia Reynolds5f20e9f2017-01-30 08:54:53 -05001047 @VisibleForTesting
1048 void setRankingHelper(RankingHelper rankingHelper) {
1049 mRankingHelper = rankingHelper;
1050 }
1051
1052 @VisibleForTesting
1053 void setIsTelevision(boolean isTelevision) {
1054 mIsTelevision = isTelevision;
1055 }
1056
Geoffrey Pitsch03533712017-01-05 10:30:07 -05001057 // TODO: Tests should call onStart instead once the methods above are removed.
Geoffrey Pitsche75a66e2016-11-22 11:12:11 -05001058 @VisibleForTesting
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05001059 void init(Looper looper, IPackageManager packageManager, PackageManager packageManagerClient,
Julia Reynolds73ed76b2017-04-04 17:04:38 -04001060 LightsManager lightsManager, NotificationListeners notificationListeners,
Geoffrey Pitschd5bcf212017-06-01 15:45:35 -04001061 ICompanionDeviceManager companionManager, SnoozeHelper snoozeHelper,
1062 NotificationUsageStats usageStats) {
Chris Wren54bbef42014-07-09 18:37:56 -04001063 Resources resources = getContext().getResources();
Chris Wren763a9bb2016-05-31 17:14:12 -04001064 mMaxPackageEnqueueRate = Settings.Global.getFloat(getContext().getContentResolver(),
1065 Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE,
1066 DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE);
1067
Sudheer Shankadc589ac2016-11-10 15:30:17 -08001068 mAm = ActivityManager.getService();
Geoffrey Pitsch03533712017-01-05 10:30:07 -05001069 mPackageManager = packageManager;
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05001070 mPackageManagerClient = packageManagerClient;
Adam Lesinski182f73f2013-12-05 16:48:06 -08001071 mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
1072 mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
Amith Yamasanif47e51e2015-04-17 10:02:15 -07001073 mAppUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
Julia Reynolds2a128742016-11-28 14:29:25 -05001074 mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
Julia Reynolds73ed76b2017-04-04 17:04:38 -04001075 mCompanionManager = companionManager;
San Mehat3ee13172010-02-04 20:54:43 -08001076
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05001077 mHandler = new WorkerHandler(looper);
Chris Wrenf9536642014-04-17 10:01:54 -04001078 mRankingThread.start();
Chris Wren54bbef42014-07-09 18:37:56 -04001079 String[] extractorNames;
1080 try {
1081 extractorNames = resources.getStringArray(R.array.config_notificationSignalExtractors);
1082 } catch (Resources.NotFoundException e) {
1083 extractorNames = new String[0];
1084 }
Geoffrey Pitschd5bcf212017-06-01 15:45:35 -04001085 mUsageStats = usageStats;
Chris Wren51017d02015-12-15 15:34:46 -05001086 mRankingHandler = new RankingHandlerWorker(mRankingThread.getLooper());
Chris Wren54bbef42014-07-09 18:37:56 -04001087 mRankingHelper = new RankingHelper(getContext(),
Julia Reynolds85769912016-10-25 09:08:57 -04001088 getContext().getPackageManager(),
Chris Wren51017d02015-12-15 15:34:46 -05001089 mRankingHandler,
Chris Wren5eab2b72015-06-16 13:56:22 -04001090 mUsageStats,
Chris Wren54bbef42014-07-09 18:37:56 -04001091 extractorNames);
John Spurlockb2278d62015-04-07 12:47:12 -04001092 mConditionProviders = new ConditionProviders(getContext(), mHandler, mUserProfiles);
1093 mZenModeHelper = new ZenModeHelper(getContext(), mHandler.getLooper(), mConditionProviders);
John Spurlock1c923a32014-04-27 16:42:29 -04001094 mZenModeHelper.addCallback(new ZenModeHelper.Callback() {
John Spurlock056c5192014-04-20 21:52:01 -04001095 @Override
1096 public void onConfigChanged() {
1097 savePolicyFile();
1098 }
John Spurlockd8afe3c2014-08-01 14:04:07 -04001099
1100 @Override
1101 void onZenModeChanged() {
John Spurlock80774932015-05-07 17:38:50 -04001102 sendRegisteredOnlyBroadcast(NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED);
Jason Monka9927322015-12-13 16:22:37 -05001103 getContext().sendBroadcastAsUser(
Jason Monk63506742015-12-16 12:06:51 -05001104 new Intent(NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED_INTERNAL)
1105 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT),
Jason Monka9927322015-12-13 16:22:37 -05001106 UserHandle.ALL, android.Manifest.permission.MANAGE_NOTIFICATIONS);
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05001107 synchronized (mNotificationLock) {
Christoph Studer85a384b2014-08-27 20:16:15 +02001108 updateInterruptionFilterLocked();
John Spurlockd8afe3c2014-08-01 14:04:07 -04001109 }
1110 }
John Spurlock1fc476d2015-04-14 16:05:20 -04001111
1112 @Override
1113 void onPolicyChanged() {
John Spurlock80774932015-05-07 17:38:50 -04001114 sendRegisteredOnlyBroadcast(NotificationManager.ACTION_NOTIFICATION_POLICY_CHANGED);
1115 }
John Spurlock056c5192014-04-20 21:52:01 -04001116 });
Julia Reynoldsa78cdff2017-04-26 10:19:25 -04001117 mSnoozeHelper = snoozeHelper;
Julia Reynolds8f488d32016-10-14 10:59:01 -04001118 mGroupHelper = new GroupHelper(new GroupHelper.Callback() {
1119 @Override
1120 public void addAutoGroup(String key) {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05001121 synchronized (mNotificationLock) {
Julia Reynolds8f488d32016-10-14 10:59:01 -04001122 addAutogroupKeyLocked(key);
1123 }
Julia Reynolds22f02b32016-12-01 15:05:13 -05001124 mRankingHandler.requestSort(false);
Julia Reynolds8f488d32016-10-14 10:59:01 -04001125 }
1126
1127 @Override
1128 public void removeAutoGroup(String key) {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05001129 synchronized (mNotificationLock) {
Julia Reynolds8f488d32016-10-14 10:59:01 -04001130 removeAutogroupKeyLocked(key);
1131 }
Julia Reynolds22f02b32016-12-01 15:05:13 -05001132 mRankingHandler.requestSort(false);
Julia Reynolds8f488d32016-10-14 10:59:01 -04001133 }
1134
1135 @Override
1136 public void addAutoGroupSummary(int userId, String pkg, String triggeringKey) {
1137 createAutoGroupSummary(userId, pkg, triggeringKey);
1138 }
1139
1140 @Override
1141 public void removeAutoGroupSummary(int userId, String pkg) {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05001142 synchronized (mNotificationLock) {
Julia Reynolds8f488d32016-10-14 10:59:01 -04001143 clearAutogroupSummaryLocked(userId, pkg);
1144 }
1145 }
1146 });
Julia Reynolds72f1cbb2016-09-19 14:57:31 -04001147
John Spurlock056c5192014-04-20 21:52:01 -04001148 final File systemDir = new File(Environment.getDataDirectory(), "system");
1149 mPolicyFile = new AtomicFile(new File(systemDir, "notification_policy.xml"));
Daniel Sandler4a900ac2013-01-30 14:04:10 -05001150
Julia Reynolds5fe2eae2017-05-22 08:45:27 -04001151 loadPolicyFile();
Daniel Sandler0da673f2012-04-11 12:33:16 -04001152
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05001153 // This is a ManagedServices object that keeps track of the listeners.
1154 mListeners = notificationListeners;
Chris Wren0efdb882016-03-01 17:17:47 -05001155
Julia Reynolds77b2cc92016-11-08 14:41:09 -05001156 // This is a MangedServices object that keeps track of the assistant.
1157 mNotificationAssistants = new NotificationAssistants();
Chris Wren0efdb882016-03-01 17:17:47 -05001158
Adam Lesinski182f73f2013-12-05 16:48:06 -08001159 mStatusBar = getLocalService(StatusBarManagerInternal.class);
Wei Liu97e56662016-03-04 10:52:33 -08001160 if (mStatusBar != null) {
1161 mStatusBar.setNotificationDelegate(mNotificationDelegate);
1162 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001163
Geoffrey Pitsch03533712017-01-05 10:30:07 -05001164 mNotificationLight = lightsManager.getLight(LightsManager.LIGHT_ID_NOTIFICATIONS);
1165 mAttentionLight = lightsManager.getLight(LightsManager.LIGHT_ID_ATTENTION);
Mike Lockwood3cb67a32009-11-27 14:25:58 -05001166
Daniel Sandleredbb3802012-11-13 20:49:47 -08001167 mFallbackVibrationPattern = getLongArray(resources,
Scott Greenwald9a05b312013-06-28 00:37:54 -04001168 R.array.config_notificationFallbackVibePattern,
Daniel Sandleredbb3802012-11-13 20:49:47 -08001169 VIBRATE_PATTERN_MAXLEN,
1170 DEFAULT_VIBRATE_PATTERN);
1171
Chris Wren5116a822014-06-04 15:59:50 -04001172 mUseAttentionLight = resources.getBoolean(R.bool.config_useAttentionLight);
1173
Joe Onorato39f5b6a2009-07-23 12:29:19 -04001174 // Don't start allowing notifications until the setup wizard has run once.
1175 // After that, including subsequent boots, init with notifications turned on.
1176 // This works on the first boot because the setup wizard will toggle this
1177 // flag at least once and we'll go back to 0 after that.
Adam Lesinski182f73f2013-12-05 16:48:06 -08001178 if (0 == Settings.Global.getInt(getContext().getContentResolver(),
Jeff Brownbf6f6f92012-09-25 15:03:20 -07001179 Settings.Global.DEVICE_PROVISIONED, 0)) {
John Spurlockd8afe3c2014-08-01 14:04:07 -04001180 mDisableNotificationEffects = true;
Joe Onorato39f5b6a2009-07-23 12:29:19 -04001181 }
John Spurlockb2278d62015-04-07 12:47:12 -04001182 mZenModeHelper.initZenMode();
John Spurlockf3701772015-02-12 13:29:37 -05001183 mInterruptionFilter = mZenModeHelper.getZenModeListenerInterruptionFilter();
Joe Onorato39f5b6a2009-07-23 12:29:19 -04001184
John Spurlockb408e8e2014-04-23 21:12:45 -04001185 mUserProfiles.updateCache(getContext());
John Spurlock32fe4c62014-10-02 12:16:02 -04001186 listenForCallState();
Kenny Guya263e4e2014-03-03 18:24:03 +00001187
Mike Lockwood35e16bf2010-11-30 19:53:36 -05001188 // register for various Intents
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001189 IntentFilter filter = new IntentFilter();
Mike Lockwoodc22404a2009-12-02 11:15:02 -05001190 filter.addAction(Intent.ACTION_SCREEN_ON);
1191 filter.addAction(Intent.ACTION_SCREEN_OFF);
Daniel Sandlere96ffb12010-03-11 13:38:06 -05001192 filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
Mike Lockwood63b5ad92011-08-30 09:55:30 -04001193 filter.addAction(Intent.ACTION_USER_PRESENT);
Dianne Hackborn80a4af22012-08-27 19:18:31 -07001194 filter.addAction(Intent.ACTION_USER_STOPPED);
Daniel Sandler4b749ef2013-03-18 21:53:04 -04001195 filter.addAction(Intent.ACTION_USER_SWITCHED);
Kenny Guy3a7c4a52014-03-03 18:24:03 +00001196 filter.addAction(Intent.ACTION_USER_ADDED);
John Spurlock21258a32015-05-27 18:22:55 -04001197 filter.addAction(Intent.ACTION_USER_REMOVED);
Julia Reynoldsa3dcaff2016-02-03 15:04:05 -05001198 filter.addAction(Intent.ACTION_USER_UNLOCKED);
Rubin Xue95057a2016-04-01 16:49:25 +01001199 filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001200 getContext().registerReceiver(mIntentReceiver, filter);
Kenny Guy70058402014-10-28 20:45:06 +00001201
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08001202 IntentFilter pkgFilter = new IntentFilter();
Chris Wren3da73022013-05-10 14:41:21 -04001203 pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08001204 pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
Daniel Sandleraac0eb02011-08-06 22:51:56 -04001205 pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08001206 pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
1207 pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
1208 pkgFilter.addDataScheme("package");
Kenny Guy70058402014-10-28 20:45:06 +00001209 getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, pkgFilter, null,
1210 null);
1211
Andrei Stingaceanu0122f6512016-01-22 15:33:03 +00001212 IntentFilter suspendedPkgFilter = new IntentFilter();
1213 suspendedPkgFilter.addAction(Intent.ACTION_PACKAGES_SUSPENDED);
1214 getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL,
1215 suspendedPkgFilter, null, null);
1216
Suchi Amalapurapub56ae202010-02-04 22:51:07 -08001217 IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
Kenny Guy70058402014-10-28 20:45:06 +00001218 getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, sdFilter, null,
1219 null);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001220
Julia Reynolds2a128742016-11-28 14:29:25 -05001221 IntentFilter timeoutFilter = new IntentFilter(ACTION_NOTIFICATION_TIMEOUT);
1222 timeoutFilter.addDataScheme(SCHEME_TIMEOUT);
1223 getContext().registerReceiver(mNotificationTimeoutReceiver, timeoutFilter);
1224
Daniel Sandler4b749ef2013-03-18 21:53:04 -04001225 mSettingsObserver = new SettingsObserver(mHandler);
Scott Greenwald9a05b312013-06-28 00:37:54 -04001226
Griff Hazen9f637d12014-06-10 11:13:51 -07001227 mArchive = new Archive(resources.getInteger(
1228 R.integer.config_notificationServiceArchiveSize));
Julia Reynolds5f20e9f2017-01-30 08:54:53 -05001229
1230 mIsTelevision = mPackageManagerClient.hasSystemFeature(FEATURE_LEANBACK)
1231 || mPackageManagerClient.hasSystemFeature(FEATURE_TELEVISION);
Geoffrey Pitsch03533712017-01-05 10:30:07 -05001232 }
Griff Hazen9f637d12014-06-10 11:13:51 -07001233
Geoffrey Pitsch03533712017-01-05 10:30:07 -05001234 @Override
1235 public void onStart() {
Julia Reynoldsa78cdff2017-04-26 10:19:25 -04001236 SnoozeHelper snoozeHelper = new SnoozeHelper(getContext(), new SnoozeHelper.Callback() {
1237 @Override
1238 public void repost(int userId, NotificationRecord r) {
1239 try {
1240 if (DBG) {
1241 Slog.d(TAG, "Reposting " + r.getKey());
1242 }
1243 enqueueNotificationInternal(r.sbn.getPackageName(), r.sbn.getOpPkg(),
1244 r.sbn.getUid(), r.sbn.getInitialPid(), r.sbn.getTag(), r.sbn.getId(),
1245 r.sbn.getNotification(), userId);
1246 } catch (Exception e) {
1247 Slog.e(TAG, "Cannot un-snooze notification", e);
1248 }
1249 }
1250 }, mUserProfiles);
1251
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05001252 init(Looper.myLooper(), AppGlobals.getPackageManager(), getContext().getPackageManager(),
Julia Reynolds73ed76b2017-04-04 17:04:38 -04001253 getLocalService(LightsManager.class), new NotificationListeners(),
Geoffrey Pitschd5bcf212017-06-01 15:45:35 -04001254 null, snoozeHelper, new NotificationUsageStats(getContext()));
Adam Lesinski182f73f2013-12-05 16:48:06 -08001255 publishBinderService(Context.NOTIFICATION_SERVICE, mService);
1256 publishLocalService(NotificationManagerInternal.class, mInternalService);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001257 }
1258
John Spurlocke7a835b2015-05-13 10:47:05 -04001259 private void sendRegisteredOnlyBroadcast(String action) {
1260 getContext().sendBroadcastAsUser(new Intent(action)
1261 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), UserHandle.ALL, null);
1262 }
1263
Adam Lesinski182f73f2013-12-05 16:48:06 -08001264 @Override
1265 public void onBootPhase(int phase) {
1266 if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
1267 // no beeping until we're basically done booting
1268 mSystemReady = true;
Jeff Sharkey098d5802012-04-26 17:30:34 -07001269
Adam Lesinski182f73f2013-12-05 16:48:06 -08001270 // Grab our optional AudioService
1271 mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
John Spurlockcdb57ae2015-02-11 19:04:11 -05001272 mAudioManagerInternal = getLocalService(AudioManagerInternal.class);
Svetoslav Ganovaa076532016-08-01 19:16:43 -07001273 mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
John Spurlock661f2cf2014-11-17 10:29:10 -05001274 mZenModeHelper.onSystemReady();
Adam Lesinskia6db4ab2014-03-24 12:31:45 -07001275 } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
1276 // This observer will force an update when observe is called, causing us to
1277 // bind to listener services.
1278 mSettingsObserver.observe();
John Spurlockb408e8e2014-04-23 21:12:45 -04001279 mListeners.onBootPhaseAppsCanStart();
Julia Reynolds77b2cc92016-11-08 14:41:09 -05001280 mNotificationAssistants.onBootPhaseAppsCanStart();
John Spurlock7340fc82014-04-24 18:50:12 -04001281 mConditionProviders.onBootPhaseAppsCanStart();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001282 }
1283 }
1284
Julia Reynolds88860ce2017-06-01 16:55:49 -04001285 @GuardedBy("mNotificationLock")
John Spurlockd8afe3c2014-08-01 14:04:07 -04001286 private void updateListenerHintsLocked() {
Bryce Lee7219ada2016-04-08 10:54:23 -07001287 final int hints = calculateHints();
John Spurlockd8afe3c2014-08-01 14:04:07 -04001288 if (hints == mListenerHints) return;
Bryce Lee7219ada2016-04-08 10:54:23 -07001289 ZenLog.traceListenerHintsChanged(mListenerHints, hints, mEffectsSuppressors.size());
John Spurlockd8afe3c2014-08-01 14:04:07 -04001290 mListenerHints = hints;
1291 scheduleListenerHintsChanged(hints);
John Spurlock1fa865f2014-07-21 14:56:39 -04001292 }
1293
Julia Reynolds88860ce2017-06-01 16:55:49 -04001294 @GuardedBy("mNotificationLock")
John Spurlockb4782522014-08-22 14:54:46 -04001295 private void updateEffectsSuppressorLocked() {
Bryce Lee7219ada2016-04-08 10:54:23 -07001296 final long updatedSuppressedEffects = calculateSuppressedEffects();
1297 if (updatedSuppressedEffects == mZenModeHelper.getSuppressedEffects()) return;
1298 final List<ComponentName> suppressors = getSuppressors();
1299 ZenLog.traceEffectsSuppressorChanged(mEffectsSuppressors, suppressors, updatedSuppressedEffects);
1300 mEffectsSuppressors = suppressors;
1301 mZenModeHelper.setSuppressedEffects(updatedSuppressedEffects);
John Spurlocke7a835b2015-05-13 10:47:05 -04001302 sendRegisteredOnlyBroadcast(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED);
John Spurlockb4782522014-08-22 14:54:46 -04001303 }
1304
Julia Reynolds73ed76b2017-04-04 17:04:38 -04001305 private void updateNotificationChannelInt(String pkg, int uid, NotificationChannel channel,
1306 boolean fromListener) {
Julia Reynolds924eed12017-01-19 09:52:07 -05001307 if (channel.getImportance() == NotificationManager.IMPORTANCE_NONE) {
1308 // cancel
1309 cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channel.getId(), 0, 0, true,
1310 UserHandle.getUserId(Binder.getCallingUid()), REASON_CHANNEL_BANNED,
1311 null);
1312 }
Julia Reynolds60315332017-04-04 14:29:07 -04001313 mRankingHelper.updateNotificationChannel(pkg, uid, channel);
Julia Reynolds924eed12017-01-19 09:52:07 -05001314
Julia Reynolds73ed76b2017-04-04 17:04:38 -04001315 final NotificationChannel modifiedChannel =
1316 mRankingHelper.getNotificationChannel(pkg, uid, channel.getId(), false);
1317
1318 if (!fromListener) {
1319 mListeners.notifyNotificationChannelChanged(
Julia Reynoldsf27d6b22017-04-13 15:48:16 -04001320 pkg, UserHandle.getUserHandleForUid(uid),
1321 modifiedChannel, NOTIFICATION_CHANNEL_OR_GROUP_UPDATED);
Julia Reynolds73ed76b2017-04-04 17:04:38 -04001322 }
1323
Julia Reynoldsa917a112017-03-21 11:09:14 -04001324 synchronized (mNotificationLock) {
Julia Reynolds924eed12017-01-19 09:52:07 -05001325 final int N = mNotificationList.size();
1326 for (int i = N - 1; i >= 0; --i) {
1327 NotificationRecord r = mNotificationList.get(i);
Julia Reynoldsa917a112017-03-21 11:09:14 -04001328 if (r.sbn.getPackageName().equals(pkg)
1329 && r.sbn.getUid() == uid
1330 && channel.getId() != null
1331 && channel.getId().equals(r.getChannel().getId())) {
Julia Reynolds73ed76b2017-04-04 17:04:38 -04001332 r.updateNotificationChannel(modifiedChannel);
Julia Reynolds924eed12017-01-19 09:52:07 -05001333 }
1334 }
1335 }
1336 mRankingHandler.requestSort(true);
1337 savePolicyFile();
1338 }
1339
Bryce Lee7219ada2016-04-08 10:54:23 -07001340 private ArrayList<ComponentName> getSuppressors() {
1341 ArrayList<ComponentName> names = new ArrayList<ComponentName>();
1342 for (int i = mListenersDisablingEffects.size() - 1; i >= 0; --i) {
1343 ArraySet<ManagedServiceInfo> serviceInfoList = mListenersDisablingEffects.valueAt(i);
1344
1345 for (ManagedServiceInfo info : serviceInfoList) {
1346 names.add(info.component);
1347 }
1348 }
1349
1350 return names;
1351 }
1352
1353 private boolean removeDisabledHints(ManagedServiceInfo info) {
1354 return removeDisabledHints(info, 0);
1355 }
1356
1357 private boolean removeDisabledHints(ManagedServiceInfo info, int hints) {
1358 boolean removed = false;
1359
1360 for (int i = mListenersDisablingEffects.size() - 1; i >= 0; --i) {
1361 final int hint = mListenersDisablingEffects.keyAt(i);
1362 final ArraySet<ManagedServiceInfo> listeners =
1363 mListenersDisablingEffects.valueAt(i);
1364
1365 if (hints == 0 || (hint & hints) == hint) {
1366 removed = removed || listeners.remove(info);
1367 }
1368 }
1369
1370 return removed;
1371 }
1372
1373 private void addDisabledHints(ManagedServiceInfo info, int hints) {
1374 if ((hints & HINT_HOST_DISABLE_EFFECTS) != 0) {
1375 addDisabledHint(info, HINT_HOST_DISABLE_EFFECTS);
1376 }
1377
1378 if ((hints & HINT_HOST_DISABLE_NOTIFICATION_EFFECTS) != 0) {
1379 addDisabledHint(info, HINT_HOST_DISABLE_NOTIFICATION_EFFECTS);
1380 }
1381
1382 if ((hints & HINT_HOST_DISABLE_CALL_EFFECTS) != 0) {
1383 addDisabledHint(info, HINT_HOST_DISABLE_CALL_EFFECTS);
1384 }
1385 }
1386
1387 private void addDisabledHint(ManagedServiceInfo info, int hint) {
1388 if (mListenersDisablingEffects.indexOfKey(hint) < 0) {
1389 mListenersDisablingEffects.put(hint, new ArraySet<ManagedServiceInfo>());
1390 }
1391
1392 ArraySet<ManagedServiceInfo> hintListeners = mListenersDisablingEffects.get(hint);
1393 hintListeners.add(info);
1394 }
1395
1396 private int calculateHints() {
1397 int hints = 0;
1398 for (int i = mListenersDisablingEffects.size() - 1; i >= 0; --i) {
1399 int hint = mListenersDisablingEffects.keyAt(i);
1400 ArraySet<ManagedServiceInfo> serviceInfoList = mListenersDisablingEffects.valueAt(i);
1401
1402 if (!serviceInfoList.isEmpty()) {
1403 hints |= hint;
1404 }
1405 }
1406
1407 return hints;
1408 }
1409
1410 private long calculateSuppressedEffects() {
1411 int hints = calculateHints();
1412 long suppressedEffects = 0;
1413
1414 if ((hints & HINT_HOST_DISABLE_EFFECTS) != 0) {
1415 suppressedEffects |= ZenModeHelper.SUPPRESSED_EFFECT_ALL;
1416 }
1417
1418 if ((hints & HINT_HOST_DISABLE_NOTIFICATION_EFFECTS) != 0) {
1419 suppressedEffects |= ZenModeHelper.SUPPRESSED_EFFECT_NOTIFICATIONS;
1420 }
1421
1422 if ((hints & HINT_HOST_DISABLE_CALL_EFFECTS) != 0) {
1423 suppressedEffects |= ZenModeHelper.SUPPRESSED_EFFECT_CALLS;
1424 }
1425
1426 return suppressedEffects;
1427 }
1428
Julia Reynolds88860ce2017-06-01 16:55:49 -04001429 @GuardedBy("mNotificationLock")
Christoph Studer85a384b2014-08-27 20:16:15 +02001430 private void updateInterruptionFilterLocked() {
1431 int interruptionFilter = mZenModeHelper.getZenModeListenerInterruptionFilter();
1432 if (interruptionFilter == mInterruptionFilter) return;
1433 mInterruptionFilter = interruptionFilter;
1434 scheduleInterruptionFilterChanged(interruptionFilter);
1435 }
1436
Geoffrey Pitsche75a66e2016-11-22 11:12:11 -05001437 @VisibleForTesting
1438 INotificationManager getBinderService() {
1439 return INotificationManager.Stub.asInterface(mService);
1440 }
1441
Geoffrey Pitsch415e4542017-04-10 13:12:58 -04001442 @VisibleForTesting
1443 NotificationManagerInternal getInternalService() {
1444 return mInternalService;
1445 }
1446
Adam Lesinski182f73f2013-12-05 16:48:06 -08001447 private final IBinder mService = new INotificationManager.Stub() {
1448 // Toasts
1449 // ============================================================================
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001450
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001451 @Override
Adam Lesinski182f73f2013-12-05 16:48:06 -08001452 public void enqueueToast(String pkg, ITransientNotification callback, int duration)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001453 {
Adam Lesinski182f73f2013-12-05 16:48:06 -08001454 if (DBG) {
1455 Slog.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback
1456 + " duration=" + duration);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001457 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001458
1459 if (pkg == null || callback == null) {
1460 Slog.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback);
1461 return ;
1462 }
1463
Geoffrey Pitsch27684152017-05-02 11:41:31 -04001464 final boolean isSystemToast = isCallerSystemOrPhone() || ("android".equals(pkg));
Andrei Stingaceanu355b2322016-02-12 16:43:51 +00001465 final boolean isPackageSuspended =
1466 isPackageSuspendedForUser(pkg, Binder.getCallingUid());
Adam Lesinski182f73f2013-12-05 16:48:06 -08001467
Geoffrey Pitsch2486f892017-05-22 10:53:44 -04001468 if (ENABLE_BLOCKED_TOASTS && !isSystemToast &&
Julia Reynolds5fe2eae2017-05-22 08:45:27 -04001469 (!areNotificationsEnabledForPackage(pkg, Binder.getCallingUid())
1470 || isPackageSuspended)) {
Geoffrey Pitsch2486f892017-05-22 10:53:44 -04001471 Slog.e(TAG, "Suppressing toast from package " + pkg
1472 + (isPackageSuspended
1473 ? " due to package suspended by administrator."
1474 : " by user request."));
1475 return;
Adam Lesinski182f73f2013-12-05 16:48:06 -08001476 }
1477
1478 synchronized (mToastQueue) {
1479 int callingPid = Binder.getCallingPid();
1480 long callingId = Binder.clearCallingIdentity();
1481 try {
1482 ToastRecord record;
1483 int index = indexOfToastLocked(pkg, callback);
1484 // If it's already in the queue, we update it in place, we don't
1485 // move it to the end of the queue.
1486 if (index >= 0) {
1487 record = mToastQueue.get(index);
1488 record.update(duration);
1489 } else {
1490 // Limit the number of toasts that any given package except the android
1491 // package can enqueue. Prevents DOS attacks and deals with leaks.
1492 if (!isSystemToast) {
1493 int count = 0;
1494 final int N = mToastQueue.size();
1495 for (int i=0; i<N; i++) {
1496 final ToastRecord r = mToastQueue.get(i);
1497 if (r.pkg.equals(pkg)) {
1498 count++;
1499 if (count >= MAX_PACKAGE_NOTIFICATIONS) {
1500 Slog.e(TAG, "Package has already posted " + count
1501 + " toasts. Not showing more. Package=" + pkg);
1502 return;
1503 }
1504 }
1505 }
1506 }
1507
Svetoslav Ganovaa076532016-08-01 19:16:43 -07001508 Binder token = new Binder();
Wale Ogunwaleac2561e2016-11-01 15:43:46 -07001509 mWindowManagerInternal.addWindowToken(token, TYPE_TOAST, DEFAULT_DISPLAY);
Svetoslav Ganovaa076532016-08-01 19:16:43 -07001510 record = new ToastRecord(callingPid, pkg, callback, duration, token);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001511 mToastQueue.add(record);
1512 index = mToastQueue.size() - 1;
Svetoslav Ganovaa076532016-08-01 19:16:43 -07001513 keepProcessAliveIfNeededLocked(callingPid);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001514 }
1515 // If it's at index 0, it's the current toast. It doesn't matter if it's
1516 // new or just been updated. Call back and tell it to show itself.
1517 // If the callback fails, this will remove it from the list, so don't
1518 // assume that it's valid after this.
1519 if (index == 0) {
1520 showNextToastLocked();
1521 }
1522 } finally {
1523 Binder.restoreCallingIdentity(callingId);
1524 }
1525 }
1526 }
1527
1528 @Override
1529 public void cancelToast(String pkg, ITransientNotification callback) {
1530 Slog.i(TAG, "cancelToast pkg=" + pkg + " callback=" + callback);
1531
1532 if (pkg == null || callback == null) {
1533 Slog.e(TAG, "Not cancelling notification. pkg=" + pkg + " callback=" + callback);
1534 return ;
1535 }
1536
1537 synchronized (mToastQueue) {
1538 long callingId = Binder.clearCallingIdentity();
1539 try {
1540 int index = indexOfToastLocked(pkg, callback);
1541 if (index >= 0) {
1542 cancelToastLocked(index);
1543 } else {
1544 Slog.w(TAG, "Toast already cancelled. pkg=" + pkg
1545 + " callback=" + callback);
1546 }
1547 } finally {
1548 Binder.restoreCallingIdentity(callingId);
1549 }
1550 }
1551 }
1552
1553 @Override
Christoph Studer8fd7f1e2014-04-11 17:35:05 -04001554 public void enqueueNotificationWithTag(String pkg, String opPkg, String tag, int id,
Julia Reynoldsfea6f7b2017-04-19 13:50:12 -04001555 Notification notification, int userId) throws RemoteException {
Christoph Studer8fd7f1e2014-04-11 17:35:05 -04001556 enqueueNotificationInternal(pkg, opPkg, Binder.getCallingUid(),
Julia Reynoldsfea6f7b2017-04-19 13:50:12 -04001557 Binder.getCallingPid(), tag, id, notification, userId);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001558 }
1559
1560 @Override
1561 public void cancelNotificationWithTag(String pkg, String tag, int id, int userId) {
John Spurlock7340fc82014-04-24 18:50:12 -04001562 checkCallerIsSystemOrSameApp(pkg);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001563 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1564 Binder.getCallingUid(), userId, true, false, "cancelNotificationWithTag", pkg);
Julia Reynoldse46bb372016-03-17 11:05:58 -04001565 // Don't allow client applications to cancel foreground service notis or autobundled
1566 // summaries.
Geoffrey Pitsch27684152017-05-02 11:41:31 -04001567 final int mustNotHaveFlags = isCallingUidSystem() ? 0 :
1568 (Notification.FLAG_FOREGROUND_SERVICE | Notification.FLAG_AUTOGROUP_SUMMARY);
John Spurlocke6a7d932014-03-13 12:29:00 -04001569 cancelNotification(Binder.getCallingUid(), Binder.getCallingPid(), pkg, tag, id, 0,
Geoffrey Pitsch27684152017-05-02 11:41:31 -04001570 mustNotHaveFlags, false, userId, REASON_APP_CANCEL, null);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001571 }
1572
1573 @Override
1574 public void cancelAllNotifications(String pkg, int userId) {
John Spurlock7340fc82014-04-24 18:50:12 -04001575 checkCallerIsSystemOrSameApp(pkg);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001576
1577 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1578 Binder.getCallingUid(), userId, true, false, "cancelAllNotifications", pkg);
1579
1580 // Calling from user space, don't allow the canceling of actively
1581 // running foreground services.
John Spurlocke6a7d932014-03-13 12:29:00 -04001582 cancelAllNotificationsInt(Binder.getCallingUid(), Binder.getCallingPid(),
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001583 pkg, null, 0, Notification.FLAG_FOREGROUND_SERVICE, true, userId,
Julia Reynoldsef37f282016-02-12 09:11:27 -05001584 REASON_APP_CANCEL_ALL, null);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001585 }
1586
1587 @Override
1588 public void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) {
John Spurlock7340fc82014-04-24 18:50:12 -04001589 checkCallerIsSystem();
Adam Lesinski182f73f2013-12-05 16:48:06 -08001590
Chris Wrenacf424a2016-03-15 12:48:55 -04001591 mRankingHelper.setEnabled(pkg, uid, enabled);
Julia Reynolds5fe2eae2017-05-22 08:45:27 -04001592 // Now, cancel any outstanding notifications that are part of a just-disabled app
Julia Reynolds4da79702017-06-01 11:06:10 -04001593 if (!enabled) {
Julia Reynolds5fe2eae2017-05-22 08:45:27 -04001594 cancelAllNotificationsInt(MY_UID, MY_PID, pkg, null, 0, 0, true,
1595 UserHandle.getUserId(uid), REASON_PACKAGE_BANNED, null);
1596 }
Chris Wrenacf424a2016-03-15 12:48:55 -04001597 savePolicyFile();
Adam Lesinski182f73f2013-12-05 16:48:06 -08001598 }
1599
1600 /**
1601 * Use this when you just want to know if notifications are OK for this package.
1602 */
1603 @Override
Julia Reynolds81afbcd2016-02-09 14:54:08 -05001604 public boolean areNotificationsEnabled(String pkg) {
1605 return areNotificationsEnabledForPackage(pkg, Binder.getCallingUid());
1606 }
1607
1608 /**
1609 * Use this when you just want to know if notifications are OK for this package.
1610 */
1611 @Override
Adam Lesinski182f73f2013-12-05 16:48:06 -08001612 public boolean areNotificationsEnabledForPackage(String pkg, int uid) {
Julia Reynolds81afbcd2016-02-09 14:54:08 -05001613 checkCallerIsSystemOrSameApp(pkg);
Julia Reynolds5fe2eae2017-05-22 08:45:27 -04001614
1615 return mRankingHelper.getImportance(pkg, uid) != IMPORTANCE_NONE;
Adam Lesinski182f73f2013-12-05 16:48:06 -08001616 }
1617
Chris Wren54bbef42014-07-09 18:37:56 -04001618 @Override
Julia Reynoldsef37f282016-02-12 09:11:27 -05001619 public int getPackageImportance(String pkg) {
Julia Reynolds81afbcd2016-02-09 14:54:08 -05001620 checkCallerIsSystemOrSameApp(pkg);
Julia Reynoldsef37f282016-02-12 09:11:27 -05001621 return mRankingHelper.getImportance(pkg, Binder.getCallingUid());
Julia Reynolds81afbcd2016-02-09 14:54:08 -05001622 }
1623
1624 @Override
Julia Reynolds924eed12017-01-19 09:52:07 -05001625 public boolean canShowBadge(String pkg, int uid) {
1626 checkCallerIsSystem();
1627 return mRankingHelper.canShowBadge(pkg, uid);
1628 }
1629
1630 @Override
1631 public void setShowBadge(String pkg, int uid, boolean showBadge) {
1632 checkCallerIsSystem();
1633 mRankingHelper.setShowBadge(pkg, uid, showBadge);
1634 savePolicyFile();
1635 }
1636
1637 @Override
Julia Reynolds59e152e2017-01-25 17:42:53 -05001638 public void createNotificationChannelGroups(String pkg,
1639 ParceledListSlice channelGroupList) throws RemoteException {
1640 checkCallerIsSystemOrSameApp(pkg);
1641 List<NotificationChannelGroup> groups = channelGroupList.getList();
1642 final int groupSize = groups.size();
1643 for (int i = 0; i < groupSize; i++) {
1644 final NotificationChannelGroup group = groups.get(i);
1645 Preconditions.checkNotNull(group, "group in list is null");
1646 mRankingHelper.createNotificationChannelGroup(pkg, Binder.getCallingUid(), group,
1647 true /* fromTargetApp */);
Julia Reynoldsf27d6b22017-04-13 15:48:16 -04001648 mListeners.notifyNotificationChannelGroupChanged(pkg,
1649 UserHandle.of(UserHandle.getCallingUserId()), group,
Julia Reynolds73ed76b2017-04-04 17:04:38 -04001650 NOTIFICATION_CHANNEL_OR_GROUP_ADDED);
Julia Reynolds59e152e2017-01-25 17:42:53 -05001651 }
1652 savePolicyFile();
1653 }
1654
Geoffrey Pitsch3560f842017-03-22 16:42:43 -04001655 private void createNotificationChannelsImpl(String pkg, int uid,
1656 ParceledListSlice channelsList) {
Geoffrey Pitsch03533712017-01-05 10:30:07 -05001657 List<NotificationChannel> channels = channelsList.getList();
1658 final int channelsSize = channels.size();
1659 for (int i = 0; i < channelsSize; i++) {
1660 final NotificationChannel channel = channels.get(i);
1661 Preconditions.checkNotNull(channel, "channel in list is null");
Geoffrey Pitsch3560f842017-03-22 16:42:43 -04001662 mRankingHelper.createNotificationChannel(pkg, uid, channel,
Geoffrey Pitsch03533712017-01-05 10:30:07 -05001663 true /* fromTargetApp */);
Julia Reynolds73ed76b2017-04-04 17:04:38 -04001664 mListeners.notifyNotificationChannelChanged(pkg,
Julia Reynoldsf27d6b22017-04-13 15:48:16 -04001665 UserHandle.getUserHandleForUid(uid),
Julia Reynolds73ed76b2017-04-04 17:04:38 -04001666 mRankingHelper.getNotificationChannel(pkg, uid, channel.getId(), false),
1667 NOTIFICATION_CHANNEL_OR_GROUP_ADDED);
Geoffrey Pitsch03533712017-01-05 10:30:07 -05001668 }
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001669 savePolicyFile();
1670 }
1671
1672 @Override
Geoffrey Pitsch3560f842017-03-22 16:42:43 -04001673 public void createNotificationChannels(String pkg,
1674 ParceledListSlice channelsList) throws RemoteException {
1675 checkCallerIsSystemOrSameApp(pkg);
1676 createNotificationChannelsImpl(pkg, Binder.getCallingUid(), channelsList);
1677 }
1678
1679 @Override
1680 public void createNotificationChannelsForPackage(String pkg, int uid,
1681 ParceledListSlice channelsList) throws RemoteException {
1682 checkCallerIsSystem();
1683 createNotificationChannelsImpl(pkg, uid, channelsList);
1684 }
1685
1686 @Override
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001687 public NotificationChannel getNotificationChannel(String pkg, String channelId) {
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001688 checkCallerIsSystemOrSameApp(pkg);
Julia Reynolds4036e8d2017-01-13 09:50:05 -05001689 return mRankingHelper.getNotificationChannel(
1690 pkg, Binder.getCallingUid(), channelId, false /* includeDeleted */);
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001691 }
1692
1693 @Override
1694 public NotificationChannel getNotificationChannelForPackage(String pkg, int uid,
Julia Reynolds4036e8d2017-01-13 09:50:05 -05001695 String channelId, boolean includeDeleted) {
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001696 checkCallerIsSystem();
Julia Reynolds9bfba592017-03-15 14:03:55 -04001697 return mRankingHelper.getNotificationChannel(pkg, uid, channelId, includeDeleted);
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001698 }
1699
1700 @Override
1701 public void deleteNotificationChannel(String pkg, String channelId) {
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001702 checkCallerIsSystemOrSameApp(pkg);
Julia Reynolds73ed76b2017-04-04 17:04:38 -04001703 final int callingUid = Binder.getCallingUid();
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001704 if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(channelId)) {
1705 throw new IllegalArgumentException("Cannot delete default channel");
1706 }
1707 cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channelId, 0, 0, true,
Julia Reynolds73ed76b2017-04-04 17:04:38 -04001708 UserHandle.getUserId(callingUid), REASON_CHANNEL_BANNED, null);
1709 mRankingHelper.deleteNotificationChannel(pkg, callingUid, channelId);
1710 mListeners.notifyNotificationChannelChanged(pkg,
Julia Reynoldsf27d6b22017-04-13 15:48:16 -04001711 UserHandle.getUserHandleForUid(callingUid),
Julia Reynolds73ed76b2017-04-04 17:04:38 -04001712 mRankingHelper.getNotificationChannel(pkg, callingUid, channelId, true),
1713 NOTIFICATION_CHANNEL_OR_GROUP_DELETED);
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001714 savePolicyFile();
1715 }
1716
1717 @Override
Julia Reynolds9bfba592017-03-15 14:03:55 -04001718 public ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroups(
1719 String pkg) {
1720 checkCallerIsSystemOrSameApp(pkg);
1721 return new ParceledListSlice<>(new ArrayList(
1722 mRankingHelper.getNotificationChannelGroups(pkg, Binder.getCallingUid())));
1723 }
1724
1725 @Override
Julia Reynolds73ed76b2017-04-04 17:04:38 -04001726 public void deleteNotificationChannelGroup(String pkg, String groupId) {
Julia Reynolds9bfba592017-03-15 14:03:55 -04001727 checkCallerIsSystemOrSameApp(pkg);
1728
Julia Reynolds73ed76b2017-04-04 17:04:38 -04001729 final int callingUid = Binder.getCallingUid();
1730 NotificationChannelGroup groupToDelete =
1731 mRankingHelper.getNotificationChannelGroup(groupId, pkg, callingUid);
1732 if (groupToDelete != null) {
1733 List<NotificationChannel> deletedChannels =
1734 mRankingHelper.deleteNotificationChannelGroup(pkg, callingUid, groupId);
1735 for (int i = 0; i < deletedChannels.size(); i++) {
1736 final NotificationChannel deletedChannel = deletedChannels.get(i);
1737 cancelAllNotificationsInt(MY_UID, MY_PID, pkg, deletedChannel.getId(), 0, 0,
1738 true,
1739 UserHandle.getUserId(Binder.getCallingUid()), REASON_CHANNEL_BANNED,
1740 null);
Julia Reynoldsf27d6b22017-04-13 15:48:16 -04001741 mListeners.notifyNotificationChannelChanged(pkg,
1742 UserHandle.getUserHandleForUid(callingUid),
1743 deletedChannel,
Julia Reynolds73ed76b2017-04-04 17:04:38 -04001744 NOTIFICATION_CHANNEL_OR_GROUP_DELETED);
1745 }
1746 mListeners.notifyNotificationChannelGroupChanged(
Julia Reynoldsf27d6b22017-04-13 15:48:16 -04001747 pkg, UserHandle.getUserHandleForUid(callingUid), groupToDelete,
1748 NOTIFICATION_CHANNEL_OR_GROUP_DELETED);
Julia Reynolds73ed76b2017-04-04 17:04:38 -04001749 savePolicyFile();
Julia Reynolds9bfba592017-03-15 14:03:55 -04001750 }
Julia Reynolds9bfba592017-03-15 14:03:55 -04001751 }
1752
1753 @Override
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001754 public void updateNotificationChannelForPackage(String pkg, int uid,
1755 NotificationChannel channel) {
Geoffrey Pitsch4dd50062016-12-06 16:41:22 -05001756 enforceSystemOrSystemUI("Caller not system or systemui");
Julia Reynolds924eed12017-01-19 09:52:07 -05001757 Preconditions.checkNotNull(channel);
Julia Reynolds73ed76b2017-04-04 17:04:38 -04001758 updateNotificationChannelInt(pkg, uid, channel, false);
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001759 }
1760
1761 @Override
1762 public ParceledListSlice<NotificationChannel> getNotificationChannelsForPackage(String pkg,
Julia Reynolds4036e8d2017-01-13 09:50:05 -05001763 int uid, boolean includeDeleted) {
Geoffrey Pitschdf44b602017-02-03 13:31:50 -05001764 enforceSystemOrSystemUI("getNotificationChannelsForPackage");
Julia Reynolds4036e8d2017-01-13 09:50:05 -05001765 return mRankingHelper.getNotificationChannels(pkg, uid, includeDeleted);
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001766 }
1767
1768 @Override
Geoffrey Pitschdf44b602017-02-03 13:31:50 -05001769 public int getNumNotificationChannelsForPackage(String pkg, int uid,
1770 boolean includeDeleted) {
1771 enforceSystemOrSystemUI("getNumNotificationChannelsForPackage");
1772 return mRankingHelper.getNotificationChannels(pkg, uid, includeDeleted)
1773 .getList().size();
1774 }
1775
1776 @Override
Julia Reynolds17717f52017-05-09 11:46:06 -04001777 public boolean onlyHasDefaultChannel(String pkg, int uid) {
1778 enforceSystemOrSystemUI("onlyHasDefaultChannel");
1779 return mRankingHelper.onlyHasDefaultChannel(pkg, uid);
1780 }
1781
1782 @Override
Julia Reynolds41103f42017-03-15 11:36:35 -04001783 public int getDeletedChannelCount(String pkg, int uid) {
1784 enforceSystemOrSystemUI("getDeletedChannelCount");
1785 return mRankingHelper.getDeletedChannelCount(pkg, uid);
1786 }
1787
1788 @Override
Julia Reynolds59e152e2017-01-25 17:42:53 -05001789 public ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroupsForPackage(
1790 String pkg, int uid, boolean includeDeleted) {
1791 checkCallerIsSystem();
1792 return mRankingHelper.getNotificationChannelGroups(pkg, uid, includeDeleted);
1793 }
1794
1795 @Override
Geoffrey Pitschdf44b602017-02-03 13:31:50 -05001796 public NotificationChannelGroup getNotificationChannelGroupForPackage(
1797 String groupId, String pkg, int uid) {
1798 enforceSystemOrSystemUI("getNotificationChannelGroupForPackage");
1799 return mRankingHelper.getNotificationChannelGroup(groupId, pkg, uid);
1800 }
1801
1802 @Override
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001803 public ParceledListSlice<NotificationChannel> getNotificationChannels(String pkg) {
1804 checkCallerIsSystemOrSameApp(pkg);
Julia Reynolds4036e8d2017-01-13 09:50:05 -05001805 return mRankingHelper.getNotificationChannels(
1806 pkg, Binder.getCallingUid(), false /* includeDeleted */);
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001807 }
1808
Julia Reynolds4036e8d2017-01-13 09:50:05 -05001809 @Override
Julia Reynolds5355e852017-02-07 14:54:13 -05001810 public void clearData(String packageName, int uid, boolean fromApp) throws RemoteException {
Julia Reynolds4036e8d2017-01-13 09:50:05 -05001811 checkCallerIsSystem();
1812
1813 // Cancel posted notifications
1814 cancelAllNotificationsInt(MY_UID, MY_PID, packageName, null, 0, 0, true,
1815 UserHandle.getUserId(Binder.getCallingUid()), REASON_CHANNEL_BANNED, null);
1816
1817 // Listener & assistant
1818 mListeners.onPackagesChanged(true, new String[] {packageName});
1819 mNotificationAssistants.onPackagesChanged(true, new String[] {packageName});
1820
1821 // Zen
1822 mConditionProviders.onPackagesChanged(true, new String[] {packageName});
1823
1824 // Reset notification preferences
Julia Reynolds5355e852017-02-07 14:54:13 -05001825 if (!fromApp) {
1826 mRankingHelper.onPackagesChanged(true, UserHandle.getCallingUserId(),
1827 new String[]{packageName}, new int[]{uid});
1828 }
Julia Reynolds4036e8d2017-01-13 09:50:05 -05001829
1830 savePolicyFile();
1831 }
1832
1833
Adam Lesinski182f73f2013-12-05 16:48:06 -08001834 /**
1835 * System-only API for getting a list of current (i.e. not cleared) notifications.
1836 *
1837 * Requires ACCESS_NOTIFICATIONS which is signature|system.
Chris Wrenf9536642014-04-17 10:01:54 -04001838 * @returns A list of all the notifications, in natural order.
Adam Lesinski182f73f2013-12-05 16:48:06 -08001839 */
1840 @Override
1841 public StatusBarNotification[] getActiveNotifications(String callingPkg) {
1842 // enforce() will ensure the calling uid has the correct permission
1843 getContext().enforceCallingOrSelfPermission(
1844 android.Manifest.permission.ACCESS_NOTIFICATIONS,
1845 "NotificationManagerService.getActiveNotifications");
1846
1847 StatusBarNotification[] tmp = null;
1848 int uid = Binder.getCallingUid();
1849
1850 // noteOp will check to make sure the callingPkg matches the uid
1851 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
1852 == AppOpsManager.MODE_ALLOWED) {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05001853 synchronized (mNotificationLock) {
Adam Lesinski182f73f2013-12-05 16:48:06 -08001854 tmp = new StatusBarNotification[mNotificationList.size()];
1855 final int N = mNotificationList.size();
1856 for (int i=0; i<N; i++) {
1857 tmp[i] = mNotificationList.get(i).sbn;
1858 }
1859 }
1860 }
1861 return tmp;
1862 }
1863
1864 /**
Dan Sandler994349c2015-04-15 11:02:54 -04001865 * Public API for getting a list of current notifications for the calling package/uid.
1866 *
Julia Reynolds573c6532017-01-24 17:44:38 -05001867 * Note that since notification posting is done asynchronously, this will not return
1868 * notifications that are in the process of being posted.
1869 *
Dan Sandler994349c2015-04-15 11:02:54 -04001870 * @returns A list of all the package's notifications, in natural order.
1871 */
1872 @Override
1873 public ParceledListSlice<StatusBarNotification> getAppActiveNotifications(String pkg,
1874 int incomingUserId) {
1875 checkCallerIsSystemOrSameApp(pkg);
1876 int userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1877 Binder.getCallingUid(), incomingUserId, true, false,
1878 "getAppActiveNotifications", pkg);
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05001879 synchronized (mNotificationLock) {
Julia Reynoldsfeb73412017-04-18 09:28:22 -04001880 final ArrayMap<String, StatusBarNotification> map
1881 = new ArrayMap<>(mNotificationList.size() + mEnqueuedNotifications.size());
Erik Wolsheimer2242b4d2015-11-24 13:22:04 -08001882 final int N = mNotificationList.size();
Dan Sandler994349c2015-04-15 11:02:54 -04001883 for (int i = 0; i < N; i++) {
Chris Wren6676dab2016-12-21 18:26:27 -05001884 StatusBarNotification sbn = sanitizeSbn(pkg, userId,
1885 mNotificationList.get(i).sbn);
1886 if (sbn != null) {
1887 map.put(sbn.getKey(), sbn);
1888 }
1889 }
1890 for(NotificationRecord snoozed: mSnoozeHelper.getSnoozed(userId, pkg)) {
1891 StatusBarNotification sbn = sanitizeSbn(pkg, userId, snoozed.sbn);
1892 if (sbn != null) {
1893 map.put(sbn.getKey(), sbn);
1894 }
1895 }
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05001896 final int M = mEnqueuedNotifications.size();
1897 for (int i = 0; i < M; i++) {
Chris Wren6676dab2016-12-21 18:26:27 -05001898 StatusBarNotification sbn = sanitizeSbn(pkg, userId,
1899 mEnqueuedNotifications.get(i).sbn);
1900 if (sbn != null) {
1901 map.put(sbn.getKey(), sbn); // pending update overwrites existing post here
Dan Sandler994349c2015-04-15 11:02:54 -04001902 }
1903 }
Julia Reynoldsfeb73412017-04-18 09:28:22 -04001904 final ArrayList<StatusBarNotification> list = new ArrayList<>(map.size());
1905 list.addAll(map.values());
1906 return new ParceledListSlice<StatusBarNotification>(list);
Dan Sandler994349c2015-04-15 11:02:54 -04001907 }
Dan Sandler994349c2015-04-15 11:02:54 -04001908 }
1909
Chris Wren6676dab2016-12-21 18:26:27 -05001910 private StatusBarNotification sanitizeSbn(String pkg, int userId,
1911 StatusBarNotification sbn) {
1912 if (sbn.getPackageName().equals(pkg) && sbn.getUserId() == userId
1913 && (sbn.getNotification().flags
1914 & Notification.FLAG_AUTOGROUP_SUMMARY) == 0) {
1915 // We could pass back a cloneLight() but clients might get confused and
1916 // try to send this thing back to notify() again, which would not work
1917 // very well.
1918 return new StatusBarNotification(
1919 sbn.getPackageName(),
1920 sbn.getOpPkg(),
Chris Wren6676dab2016-12-21 18:26:27 -05001921 sbn.getId(), sbn.getTag(), sbn.getUid(), sbn.getInitialPid(),
1922 sbn.getNotification().clone(),
1923 sbn.getUser(), sbn.getOverrideGroupKey(), sbn.getPostTime());
1924 }
1925 return null;
1926 }
1927
Dan Sandler994349c2015-04-15 11:02:54 -04001928 /**
Adam Lesinski182f73f2013-12-05 16:48:06 -08001929 * System-only API for getting a list of recent (cleared, no longer shown) notifications.
1930 *
1931 * Requires ACCESS_NOTIFICATIONS which is signature|system.
1932 */
1933 @Override
1934 public StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count) {
1935 // enforce() will ensure the calling uid has the correct permission
1936 getContext().enforceCallingOrSelfPermission(
1937 android.Manifest.permission.ACCESS_NOTIFICATIONS,
1938 "NotificationManagerService.getHistoricalNotifications");
1939
1940 StatusBarNotification[] tmp = null;
1941 int uid = Binder.getCallingUid();
1942
1943 // noteOp will check to make sure the callingPkg matches the uid
1944 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
1945 == AppOpsManager.MODE_ALLOWED) {
1946 synchronized (mArchive) {
1947 tmp = mArchive.getArray(count);
1948 }
1949 }
1950 return tmp;
1951 }
1952
1953 /**
1954 * Register a listener binder directly with the notification manager.
1955 *
1956 * Only works with system callers. Apps should extend
1957 * {@link android.service.notification.NotificationListenerService}.
1958 */
1959 @Override
1960 public void registerListener(final INotificationListener listener,
Chris Wren0efdb882016-03-01 17:17:47 -05001961 final ComponentName component, final int userid) {
Christoph Studer3e144d32014-05-22 16:48:40 +02001962 enforceSystemOrSystemUI("INotificationManager.registerListener");
Chris Wren0efdb882016-03-01 17:17:47 -05001963 mListeners.registerService(listener, component, userid);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001964 }
1965
1966 /**
1967 * Remove a listener binder directly
1968 */
1969 @Override
Chris Wrene0ba7eb2016-03-04 17:30:43 -05001970 public void unregisterListener(INotificationListener token, int userid) {
Chris Wrenb7c81092016-03-10 11:41:10 -05001971 mListeners.unregisterService(token, userid);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001972 }
1973
1974 /**
1975 * Allow an INotificationListener to simulate a "clear all" operation.
1976 *
1977 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onClearAllNotifications}
1978 *
1979 * @param token The binder for the listener, to check that the caller is allowed
1980 */
1981 @Override
John Spurlocka4294292014-03-24 18:02:32 -04001982 public void cancelNotificationsFromListener(INotificationListener token, String[] keys) {
John Spurlocke6a7d932014-03-13 12:29:00 -04001983 final int callingUid = Binder.getCallingUid();
1984 final int callingPid = Binder.getCallingPid();
Adam Lesinski182f73f2013-12-05 16:48:06 -08001985 long identity = Binder.clearCallingIdentity();
1986 try {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05001987 synchronized (mNotificationLock) {
John Spurlock7340fc82014-04-24 18:50:12 -04001988 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
John Spurlocka4294292014-03-24 18:02:32 -04001989 if (keys != null) {
1990 final int N = keys.length;
1991 for (int i = 0; i < N; i++) {
1992 NotificationRecord r = mNotificationsByKey.get(keys[i]);
Griff Hazen335e1f02014-09-11 14:49:31 -07001993 if (r == null) continue;
Kenny Guya263e4e2014-03-03 18:24:03 +00001994 final int userId = r.sbn.getUserId();
1995 if (userId != info.userid && userId != UserHandle.USER_ALL &&
John Spurlockb408e8e2014-04-23 21:12:45 -04001996 !mUserProfiles.isCurrentProfile(userId)) {
Kenny Guya263e4e2014-03-03 18:24:03 +00001997 throw new SecurityException("Disallowed call from listener: "
John Spurlock7340fc82014-04-24 18:50:12 -04001998 + info.service);
Kenny Guya263e4e2014-03-03 18:24:03 +00001999 }
Griff Hazen335e1f02014-09-11 14:49:31 -07002000 cancelNotificationFromListenerLocked(info, callingUid, callingPid,
2001 r.sbn.getPackageName(), r.sbn.getTag(), r.sbn.getId(),
2002 userId);
John Spurlocka4294292014-03-24 18:02:32 -04002003 }
2004 } else {
2005 cancelAllLocked(callingUid, callingPid, info.userid,
Kenny Guya263e4e2014-03-03 18:24:03 +00002006 REASON_LISTENER_CANCEL_ALL, info, info.supportsProfiles());
John Spurlocka4294292014-03-24 18:02:32 -04002007 }
Adam Lesinskie8240262014-03-26 16:01:00 -07002008 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08002009 } finally {
2010 Binder.restoreCallingIdentity(identity);
2011 }
2012 }
2013
Chris Wrenab41eec2016-01-04 18:01:27 -05002014 /**
2015 * Handle request from an approved listener to re-enable itself.
2016 *
2017 * @param component The componenet to be re-enabled, caller must match package.
2018 */
2019 @Override
2020 public void requestBindListener(ComponentName component) {
2021 checkCallerIsSystemOrSameApp(component.getPackageName());
2022 long identity = Binder.clearCallingIdentity();
2023 try {
Julia Reynoldse46bb372016-03-17 11:05:58 -04002024 ManagedServices manager =
Julia Reynolds77b2cc92016-11-08 14:41:09 -05002025 mNotificationAssistants.isComponentEnabledForCurrentProfiles(component)
2026 ? mNotificationAssistants
Chris Wrenab41eec2016-01-04 18:01:27 -05002027 : mListeners;
2028 manager.setComponentState(component, true);
2029 } finally {
2030 Binder.restoreCallingIdentity(identity);
2031 }
2032 }
2033
2034 @Override
2035 public void requestUnbindListener(INotificationListener token) {
2036 long identity = Binder.clearCallingIdentity();
2037 try {
2038 // allow bound services to disable themselves
Julia Reynoldsfeb73412017-04-18 09:28:22 -04002039 synchronized (mNotificationLock) {
2040 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2041 info.getOwner().setComponentState(info.component, false);
2042 }
Chris Wrenab41eec2016-01-04 18:01:27 -05002043 } finally {
2044 Binder.restoreCallingIdentity(identity);
2045 }
2046 }
2047
Amith Yamasanif47e51e2015-04-17 10:02:15 -07002048 @Override
2049 public void setNotificationsShownFromListener(INotificationListener token, String[] keys) {
Amith Yamasanif47e51e2015-04-17 10:02:15 -07002050 long identity = Binder.clearCallingIdentity();
2051 try {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05002052 synchronized (mNotificationLock) {
Amith Yamasanif47e51e2015-04-17 10:02:15 -07002053 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2054 if (keys != null) {
2055 final int N = keys.length;
2056 for (int i = 0; i < N; i++) {
2057 NotificationRecord r = mNotificationsByKey.get(keys[i]);
2058 if (r == null) continue;
2059 final int userId = r.sbn.getUserId();
2060 if (userId != info.userid && userId != UserHandle.USER_ALL &&
2061 !mUserProfiles.isCurrentProfile(userId)) {
2062 throw new SecurityException("Disallowed call from listener: "
2063 + info.service);
2064 }
2065 if (!r.isSeen()) {
2066 if (DBG) Slog.d(TAG, "Marking notification as seen " + keys[i]);
2067 mAppUsageStats.reportEvent(r.sbn.getPackageName(),
Xiaohui Chenddbe4ca2015-08-13 16:20:56 -07002068 userId == UserHandle.USER_ALL ? UserHandle.USER_SYSTEM
Amith Yamasanif47e51e2015-04-17 10:02:15 -07002069 : userId,
Adam Lesinskic8e87292015-06-10 15:33:45 -07002070 UsageEvents.Event.USER_INTERACTION);
Amith Yamasanif47e51e2015-04-17 10:02:15 -07002071 r.setSeen();
2072 }
2073 }
2074 }
2075 }
2076 } finally {
2077 Binder.restoreCallingIdentity(identity);
2078 }
2079 }
2080
Julia Reynolds72f1cbb2016-09-19 14:57:31 -04002081 /**
2082 * Allow an INotificationListener to simulate clearing (dismissing) a single notification.
2083 *
2084 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onNotificationClear}
2085 *
Julia Reynolds79672302017-01-12 08:30:16 -05002086 * @param info The binder for the listener, to check that the caller is allowed
Julia Reynolds72f1cbb2016-09-19 14:57:31 -04002087 */
Julia Reynolds88860ce2017-06-01 16:55:49 -04002088 @GuardedBy("mNotificationLock")
John Spurlock7340fc82014-04-24 18:50:12 -04002089 private void cancelNotificationFromListenerLocked(ManagedServiceInfo info,
Kenny Guya263e4e2014-03-03 18:24:03 +00002090 int callingUid, int callingPid, String pkg, String tag, int id, int userId) {
John Spurlocka4294292014-03-24 18:02:32 -04002091 cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
2092 Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
2093 true,
Kenny Guya263e4e2014-03-03 18:24:03 +00002094 userId, REASON_LISTENER_CANCEL, info);
John Spurlocka4294292014-03-24 18:02:32 -04002095 }
2096
Adam Lesinski182f73f2013-12-05 16:48:06 -08002097 /**
Julia Reynolds79672302017-01-12 08:30:16 -05002098 * Allow an INotificationListener to snooze a single notification until a context.
2099 *
2100 * @param token The binder for the listener, to check that the caller is allowed
2101 */
2102 @Override
2103 public void snoozeNotificationUntilContextFromListener(INotificationListener token,
2104 String key, String snoozeCriterionId) {
2105 long identity = Binder.clearCallingIdentity();
2106 try {
Julia Reynoldsfeb73412017-04-18 09:28:22 -04002107 synchronized (mNotificationLock) {
2108 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2109 snoozeNotificationInt(key, SNOOZE_UNTIL_UNSPECIFIED, snoozeCriterionId, info);
2110 }
Julia Reynolds79672302017-01-12 08:30:16 -05002111 } finally {
2112 Binder.restoreCallingIdentity(identity);
2113 }
2114 }
2115
2116 /**
2117 * Allow an INotificationListener to snooze a single notification until a time.
Julia Reynolds72f1cbb2016-09-19 14:57:31 -04002118 *
2119 * @param token The binder for the listener, to check that the caller is allowed
2120 */
2121 @Override
Julia Reynoldsb6c1f992016-11-22 09:26:46 -05002122 public void snoozeNotificationUntilFromListener(INotificationListener token, String key,
Julia Reynolds50989772017-02-23 14:32:16 -05002123 long duration) {
Julia Reynolds72f1cbb2016-09-19 14:57:31 -04002124 long identity = Binder.clearCallingIdentity();
2125 try {
Julia Reynoldsfeb73412017-04-18 09:28:22 -04002126 synchronized (mNotificationLock) {
2127 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2128 snoozeNotificationInt(key, duration, null, info);
2129 }
Julia Reynolds72f1cbb2016-09-19 14:57:31 -04002130 } finally {
2131 Binder.restoreCallingIdentity(identity);
2132 }
2133 }
2134
2135 /**
Julia Reynoldscf63ff12017-01-24 13:55:48 -05002136 * Allows the notification assistant to un-snooze a single notification.
Julia Reynoldsb6c1f992016-11-22 09:26:46 -05002137 *
Julia Reynoldscf63ff12017-01-24 13:55:48 -05002138 * @param token The binder for the assistant, to check that the caller is allowed
Julia Reynoldsb6c1f992016-11-22 09:26:46 -05002139 */
2140 @Override
Julia Reynoldscf63ff12017-01-24 13:55:48 -05002141 public void unsnoozeNotificationFromAssistant(INotificationListener token, String key) {
Julia Reynoldsb6c1f992016-11-22 09:26:46 -05002142 long identity = Binder.clearCallingIdentity();
2143 try {
Julia Reynoldsfeb73412017-04-18 09:28:22 -04002144 synchronized (mNotificationLock) {
2145 final ManagedServiceInfo info =
2146 mNotificationAssistants.checkServiceTokenLocked(token);
2147 unsnoozeNotificationInt(key, info);
2148 }
Julia Reynoldsb6c1f992016-11-22 09:26:46 -05002149 } finally {
2150 Binder.restoreCallingIdentity(identity);
2151 }
2152 }
2153
2154 /**
Adam Lesinski182f73f2013-12-05 16:48:06 -08002155 * Allow an INotificationListener to simulate clearing (dismissing) a single notification.
2156 *
2157 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onNotificationClear}
2158 *
2159 * @param token The binder for the listener, to check that the caller is allowed
2160 */
2161 @Override
2162 public void cancelNotificationFromListener(INotificationListener token, String pkg,
2163 String tag, int id) {
John Spurlocke6a7d932014-03-13 12:29:00 -04002164 final int callingUid = Binder.getCallingUid();
2165 final int callingPid = Binder.getCallingPid();
Adam Lesinski182f73f2013-12-05 16:48:06 -08002166 long identity = Binder.clearCallingIdentity();
2167 try {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05002168 synchronized (mNotificationLock) {
John Spurlock7340fc82014-04-24 18:50:12 -04002169 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
Kenny Guya263e4e2014-03-03 18:24:03 +00002170 if (info.supportsProfiles()) {
2171 Log.e(TAG, "Ignoring deprecated cancelNotification(pkg, tag, id) "
2172 + "from " + info.component
2173 + " use cancelNotification(key) instead.");
2174 } else {
2175 cancelNotificationFromListenerLocked(info, callingUid, callingPid,
2176 pkg, tag, id, info.userid);
2177 }
Adam Lesinskie8240262014-03-26 16:01:00 -07002178 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08002179 } finally {
2180 Binder.restoreCallingIdentity(identity);
2181 }
2182 }
2183
2184 /**
2185 * Allow an INotificationListener to request the list of outstanding notifications seen by
2186 * the current user. Useful when starting up, after which point the listener callbacks
2187 * should be used.
2188 *
2189 * @param token The binder for the listener, to check that the caller is allowed
Dan Sandlerea75fdd2014-08-12 12:29:19 -04002190 * @param keys An array of notification keys to fetch, or null to fetch everything
Chris Wrenf9536642014-04-17 10:01:54 -04002191 * @returns The return value will contain the notifications specified in keys, in that
2192 * order, or if keys is null, all the notifications, in natural order.
Adam Lesinski182f73f2013-12-05 16:48:06 -08002193 */
2194 @Override
Christoph Studercee44ba2014-05-20 18:36:43 +02002195 public ParceledListSlice<StatusBarNotification> getActiveNotificationsFromListener(
Christoph Studerb82bc782014-08-20 14:29:43 +02002196 INotificationListener token, String[] keys, int trim) {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05002197 synchronized (mNotificationLock) {
John Spurlock7340fc82014-04-24 18:50:12 -04002198 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
Dan Sandlerea75fdd2014-08-12 12:29:19 -04002199 final boolean getKeys = keys != null;
2200 final int N = getKeys ? keys.length : mNotificationList.size();
Christoph Studerb82bc782014-08-20 14:29:43 +02002201 final ArrayList<StatusBarNotification> list
2202 = new ArrayList<StatusBarNotification>(N);
Christoph Studercee44ba2014-05-20 18:36:43 +02002203 for (int i=0; i<N; i++) {
Dan Sandlerea75fdd2014-08-12 12:29:19 -04002204 final NotificationRecord r = getKeys
2205 ? mNotificationsByKey.get(keys[i])
2206 : mNotificationList.get(i);
Christoph Studerb82bc782014-08-20 14:29:43 +02002207 if (r == null) continue;
2208 StatusBarNotification sbn = r.sbn;
2209 if (!isVisibleToListener(sbn, info)) continue;
2210 StatusBarNotification sbnToSend =
2211 (trim == TRIM_FULL) ? sbn : sbn.cloneLight();
2212 list.add(sbnToSend);
Adam Lesinski182f73f2013-12-05 16:48:06 -08002213 }
Christoph Studercee44ba2014-05-20 18:36:43 +02002214 return new ParceledListSlice<StatusBarNotification>(list);
Adam Lesinski182f73f2013-12-05 16:48:06 -08002215 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08002216 }
2217
Julia Reynoldscf63ff12017-01-24 13:55:48 -05002218 /**
2219 * Allow an INotificationListener to request the list of outstanding snoozed notifications
2220 * seen by the current user. Useful when starting up, after which point the listener
2221 * callbacks should be used.
2222 *
2223 * @param token The binder for the listener, to check that the caller is allowed
2224 * @returns The return value will contain the notifications specified in keys, in that
2225 * order, or if keys is null, all the notifications, in natural order.
2226 */
2227 @Override
2228 public ParceledListSlice<StatusBarNotification> getSnoozedNotificationsFromListener(
2229 INotificationListener token, int trim) {
2230 synchronized (mNotificationLock) {
2231 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2232 List<NotificationRecord> snoozedRecords = mSnoozeHelper.getSnoozed();
2233 final int N = snoozedRecords.size();
2234 final ArrayList<StatusBarNotification> list = new ArrayList<>(N);
2235 for (int i=0; i < N; i++) {
2236 final NotificationRecord r = snoozedRecords.get(i);
2237 if (r == null) continue;
2238 StatusBarNotification sbn = r.sbn;
2239 if (!isVisibleToListener(sbn, info)) continue;
2240 StatusBarNotification sbnToSend =
2241 (trim == TRIM_FULL) ? sbn : sbn.cloneLight();
2242 list.add(sbnToSend);
2243 }
2244 return new ParceledListSlice<>(list);
2245 }
2246 }
2247
Adam Lesinski182f73f2013-12-05 16:48:06 -08002248 @Override
John Spurlockd8afe3c2014-08-01 14:04:07 -04002249 public void requestHintsFromListener(INotificationListener token, int hints) {
2250 final long identity = Binder.clearCallingIdentity();
2251 try {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05002252 synchronized (mNotificationLock) {
John Spurlockd8afe3c2014-08-01 14:04:07 -04002253 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
Bryce Lee7219ada2016-04-08 10:54:23 -07002254 final int disableEffectsMask = HINT_HOST_DISABLE_EFFECTS
2255 | HINT_HOST_DISABLE_NOTIFICATION_EFFECTS
2256 | HINT_HOST_DISABLE_CALL_EFFECTS;
2257 final boolean disableEffects = (hints & disableEffectsMask) != 0;
John Spurlockd8afe3c2014-08-01 14:04:07 -04002258 if (disableEffects) {
Bryce Lee7219ada2016-04-08 10:54:23 -07002259 addDisabledHints(info, hints);
John Spurlockd8afe3c2014-08-01 14:04:07 -04002260 } else {
Bryce Lee7219ada2016-04-08 10:54:23 -07002261 removeDisabledHints(info, hints);
John Spurlockd8afe3c2014-08-01 14:04:07 -04002262 }
John Spurlockd8afe3c2014-08-01 14:04:07 -04002263 updateListenerHintsLocked();
John Spurlockb4782522014-08-22 14:54:46 -04002264 updateEffectsSuppressorLocked();
John Spurlock1fa865f2014-07-21 14:56:39 -04002265 }
John Spurlockd8afe3c2014-08-01 14:04:07 -04002266 } finally {
2267 Binder.restoreCallingIdentity(identity);
John Spurlock1fa865f2014-07-21 14:56:39 -04002268 }
2269 }
2270
2271 @Override
John Spurlockd8afe3c2014-08-01 14:04:07 -04002272 public int getHintsFromListener(INotificationListener token) {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05002273 synchronized (mNotificationLock) {
John Spurlockd8afe3c2014-08-01 14:04:07 -04002274 return mListenerHints;
John Spurlock1fa865f2014-07-21 14:56:39 -04002275 }
2276 }
2277
2278 @Override
Christoph Studer85a384b2014-08-27 20:16:15 +02002279 public void requestInterruptionFilterFromListener(INotificationListener token,
2280 int interruptionFilter) throws RemoteException {
2281 final long identity = Binder.clearCallingIdentity();
2282 try {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05002283 synchronized (mNotificationLock) {
John Spurlock661f2cf2014-11-17 10:29:10 -05002284 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2285 mZenModeHelper.requestFromListener(info.component, interruptionFilter);
Christoph Studer85a384b2014-08-27 20:16:15 +02002286 updateInterruptionFilterLocked();
2287 }
2288 } finally {
2289 Binder.restoreCallingIdentity(identity);
2290 }
2291 }
2292
2293 @Override
2294 public int getInterruptionFilterFromListener(INotificationListener token)
2295 throws RemoteException {
2296 synchronized (mNotificationLight) {
2297 return mInterruptionFilter;
2298 }
2299 }
2300
2301 @Override
Christoph Studerb82bc782014-08-20 14:29:43 +02002302 public void setOnNotificationPostedTrimFromListener(INotificationListener token, int trim)
2303 throws RemoteException {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05002304 synchronized (mNotificationLock) {
Christoph Studerb82bc782014-08-20 14:29:43 +02002305 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2306 if (info == null) return;
2307 mListeners.setOnNotificationPostedTrimLocked(info, trim);
2308 }
2309 }
2310
2311 @Override
John Spurlockb2278d62015-04-07 12:47:12 -04002312 public int getZenMode() {
2313 return mZenModeHelper.getZenMode();
2314 }
2315
2316 @Override
John Spurlock056c5192014-04-20 21:52:01 -04002317 public ZenModeConfig getZenModeConfig() {
Julia Reynoldsbb983d202017-01-06 09:54:20 -05002318 enforceSystemOrSystemUI("INotificationManager.getZenModeConfig");
John Spurlock056c5192014-04-20 21:52:01 -04002319 return mZenModeHelper.getConfig();
2320 }
2321
2322 @Override
John Spurlockb2278d62015-04-07 12:47:12 -04002323 public void setZenMode(int mode, Uri conditionId, String reason) throws RemoteException {
Julia Reynoldsbb983d202017-01-06 09:54:20 -05002324 enforceSystemOrSystemUI("INotificationManager.setZenMode");
John Spurlockcdb57ae2015-02-11 19:04:11 -05002325 final long identity = Binder.clearCallingIdentity();
2326 try {
Julia Reynolds44ad6ff2016-07-06 09:47:45 -04002327 mZenModeHelper.setManualZenMode(mode, conditionId, null, reason);
John Spurlockcdb57ae2015-02-11 19:04:11 -05002328 } finally {
2329 Binder.restoreCallingIdentity(identity);
2330 }
2331 }
2332
2333 @Override
Julia Reynolds361e82d32016-02-26 18:19:49 -05002334 public List<ZenModeConfig.ZenRule> getZenRules() throws RemoteException {
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04002335 enforcePolicyAccess(Binder.getCallingUid(), "getAutomaticZenRules");
Julia Reynolds361e82d32016-02-26 18:19:49 -05002336 return mZenModeHelper.getZenRules();
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04002337 }
2338
2339 @Override
Julia Reynolds4fe98d62015-10-06 16:23:41 -04002340 public AutomaticZenRule getAutomaticZenRule(String id) throws RemoteException {
2341 Preconditions.checkNotNull(id, "Id is null");
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04002342 enforcePolicyAccess(Binder.getCallingUid(), "getAutomaticZenRule");
Julia Reynolds4fe98d62015-10-06 16:23:41 -04002343 return mZenModeHelper.getAutomaticZenRule(id);
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04002344 }
2345
2346 @Override
Julia Reynolds361e82d32016-02-26 18:19:49 -05002347 public String addAutomaticZenRule(AutomaticZenRule automaticZenRule)
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04002348 throws RemoteException {
2349 Preconditions.checkNotNull(automaticZenRule, "automaticZenRule is null");
2350 Preconditions.checkNotNull(automaticZenRule.getName(), "Name is null");
2351 Preconditions.checkNotNull(automaticZenRule.getOwner(), "Owner is null");
2352 Preconditions.checkNotNull(automaticZenRule.getConditionId(), "ConditionId is null");
Julia Reynolds4fe98d62015-10-06 16:23:41 -04002353 enforcePolicyAccess(Binder.getCallingUid(), "addAutomaticZenRule");
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04002354
Julia Reynolds4fe98d62015-10-06 16:23:41 -04002355 return mZenModeHelper.addAutomaticZenRule(automaticZenRule,
2356 "addAutomaticZenRule");
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04002357 }
2358
2359 @Override
Julia Reynolds361e82d32016-02-26 18:19:49 -05002360 public boolean updateAutomaticZenRule(String id, AutomaticZenRule automaticZenRule)
Julia Reynolds4fe98d62015-10-06 16:23:41 -04002361 throws RemoteException {
2362 Preconditions.checkNotNull(automaticZenRule, "automaticZenRule is null");
2363 Preconditions.checkNotNull(automaticZenRule.getName(), "Name is null");
2364 Preconditions.checkNotNull(automaticZenRule.getOwner(), "Owner is null");
2365 Preconditions.checkNotNull(automaticZenRule.getConditionId(), "ConditionId is null");
2366 enforcePolicyAccess(Binder.getCallingUid(), "updateAutomaticZenRule");
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04002367
Julia Reynolds361e82d32016-02-26 18:19:49 -05002368 return mZenModeHelper.updateAutomaticZenRule(id, automaticZenRule,
Julia Reynolds4fe98d62015-10-06 16:23:41 -04002369 "updateAutomaticZenRule");
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04002370 }
2371
2372 @Override
Julia Reynolds4fe98d62015-10-06 16:23:41 -04002373 public boolean removeAutomaticZenRule(String id) throws RemoteException {
2374 Preconditions.checkNotNull(id, "Id is null");
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04002375 // Verify that they can modify zen rules.
2376 enforcePolicyAccess(Binder.getCallingUid(), "removeAutomaticZenRule");
2377
Julia Reynolds4fe98d62015-10-06 16:23:41 -04002378 return mZenModeHelper.removeAutomaticZenRule(id, "removeAutomaticZenRule");
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04002379 }
2380
2381 @Override
Julia Reynoldsc8e54e82015-11-30 16:43:05 -05002382 public boolean removeAutomaticZenRules(String packageName) throws RemoteException {
2383 Preconditions.checkNotNull(packageName, "Package name is null");
2384 enforceSystemOrSystemUI("removeAutomaticZenRules");
2385
2386 return mZenModeHelper.removeAutomaticZenRules(packageName, "removeAutomaticZenRules");
2387 }
2388
2389 @Override
Julia Reynolds43b70cd2016-01-14 15:05:34 -05002390 public int getRuleInstanceCount(ComponentName owner) throws RemoteException {
2391 Preconditions.checkNotNull(owner, "Owner is null");
2392 enforceSystemOrSystemUI("getRuleInstanceCount");
2393
2394 return mZenModeHelper.getCurrentInstanceCount(owner);
2395 }
2396
2397 @Override
John Spurlock80774932015-05-07 17:38:50 -04002398 public void setInterruptionFilter(String pkg, int filter) throws RemoteException {
2399 enforcePolicyAccess(pkg, "setInterruptionFilter");
2400 final int zen = NotificationManager.zenModeFromInterruptionFilter(filter, -1);
2401 if (zen == -1) throw new IllegalArgumentException("Invalid filter: " + filter);
2402 final long identity = Binder.clearCallingIdentity();
2403 try {
Julia Reynolds44ad6ff2016-07-06 09:47:45 -04002404 mZenModeHelper.setManualZenMode(zen, null, pkg, "setInterruptionFilter");
John Spurlock80774932015-05-07 17:38:50 -04002405 } finally {
2406 Binder.restoreCallingIdentity(identity);
2407 }
2408 }
2409
2410 @Override
John Spurlocka7d92b12015-05-13 14:48:02 -04002411 public void notifyConditions(final String pkg, IConditionProvider provider,
2412 final Condition[] conditions) {
John Spurlocke77bb362014-04-26 10:24:59 -04002413 final ManagedServiceInfo info = mConditionProviders.checkServiceToken(provider);
2414 checkCallerIsSystemOrSameApp(pkg);
John Spurlocka7d92b12015-05-13 14:48:02 -04002415 mHandler.post(new Runnable() {
2416 @Override
2417 public void run() {
2418 mConditionProviders.notifyConditions(pkg, info, conditions);
2419 }
2420 });
John Spurlocke77bb362014-04-26 10:24:59 -04002421 }
2422
Julia Reynolds38e6ca42016-08-08 08:38:09 -04002423 @Override
2424 public void requestUnbindProvider(IConditionProvider provider) {
2425 long identity = Binder.clearCallingIdentity();
2426 try {
2427 // allow bound services to disable themselves
2428 final ManagedServiceInfo info = mConditionProviders.checkServiceToken(provider);
2429 info.getOwner().setComponentState(info.component, false);
2430 } finally {
2431 Binder.restoreCallingIdentity(identity);
2432 }
2433 }
2434
2435 @Override
2436 public void requestBindProvider(ComponentName component) {
2437 checkCallerIsSystemOrSameApp(component.getPackageName());
2438 long identity = Binder.clearCallingIdentity();
2439 try {
2440 mConditionProviders.setComponentState(component, true);
2441 } finally {
2442 Binder.restoreCallingIdentity(identity);
2443 }
2444 }
2445
John Spurlocke77bb362014-04-26 10:24:59 -04002446 private void enforceSystemOrSystemUI(String message) {
Geoffrey Pitsch27684152017-05-02 11:41:31 -04002447 if (isCallerSystemOrPhone()) return;
John Spurlocke77bb362014-04-26 10:24:59 -04002448 getContext().enforceCallingPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
2449 message);
John Spurlock7340fc82014-04-24 18:50:12 -04002450 }
2451
Julia Reynolds48034f82016-03-09 10:15:16 -05002452 private void enforceSystemOrSystemUIOrSamePackage(String pkg, String message) {
2453 try {
2454 checkCallerIsSystemOrSameApp(pkg);
2455 } catch (SecurityException e) {
2456 getContext().enforceCallingPermission(
2457 android.Manifest.permission.STATUS_BAR_SERVICE,
2458 message);
2459 }
2460 }
2461
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04002462 private void enforcePolicyAccess(int uid, String method) {
2463 if (PackageManager.PERMISSION_GRANTED == getContext().checkCallingPermission(
2464 android.Manifest.permission.MANAGE_NOTIFICATIONS)) {
2465 return;
2466 }
2467 boolean accessAllowed = false;
2468 String[] packages = getContext().getPackageManager().getPackagesForUid(uid);
2469 final int packageCount = packages.length;
2470 for (int i = 0; i < packageCount; i++) {
2471 if (checkPolicyAccess(packages[i])) {
2472 accessAllowed = true;
2473 }
2474 }
2475 if (!accessAllowed) {
2476 Slog.w(TAG, "Notification policy access denied calling " + method);
2477 throw new SecurityException("Notification policy access denied");
2478 }
2479 }
2480
John Spurlock80774932015-05-07 17:38:50 -04002481 private void enforcePolicyAccess(String pkg, String method) {
Julia Reynolds6ee26172015-09-28 11:34:48 -04002482 if (PackageManager.PERMISSION_GRANTED == getContext().checkCallingPermission(
2483 android.Manifest.permission.MANAGE_NOTIFICATIONS)) {
2484 return;
2485 }
Julia Reynolds0cd1b782016-06-29 08:43:00 -04002486 checkCallerIsSameApp(pkg);
John Spurlock80774932015-05-07 17:38:50 -04002487 if (!checkPolicyAccess(pkg)) {
2488 Slog.w(TAG, "Notification policy access denied calling " + method);
2489 throw new SecurityException("Notification policy access denied");
John Spurlock1fc476d2015-04-14 16:05:20 -04002490 }
2491 }
2492
John Spurlock80774932015-05-07 17:38:50 -04002493 private boolean checkPackagePolicyAccess(String pkg) {
John Spurlock7c74f782015-06-04 13:01:42 -04002494 return mPolicyAccess.isPackageGranted(pkg);
John Spurlock80774932015-05-07 17:38:50 -04002495 }
2496
2497 private boolean checkPolicyAccess(String pkg) {
Julia Reynolds0867b3a2016-03-30 17:29:54 -04002498 try {
2499 int uid = getContext().getPackageManager().getPackageUidAsUser(
2500 pkg, UserHandle.getCallingUserId());
2501 if (PackageManager.PERMISSION_GRANTED == ActivityManager.checkComponentPermission(
2502 android.Manifest.permission.MANAGE_NOTIFICATIONS, uid,
2503 -1, true)) {
2504 return true;
2505 }
2506 } catch (NameNotFoundException e) {
2507 return false;
Julia Reynoldsa2d01022016-03-18 15:03:43 -04002508 }
John Spurlock80774932015-05-07 17:38:50 -04002509 return checkPackagePolicyAccess(pkg) || mListeners.isComponentEnabledForPackage(pkg);
John Spurlock1fc476d2015-04-14 16:05:20 -04002510 }
2511
John Spurlock7340fc82014-04-24 18:50:12 -04002512 @Override
Adam Lesinski182f73f2013-12-05 16:48:06 -08002513 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Jeff Sharkey6df866a2017-03-31 14:08:23 -06002514 if (!DumpUtils.checkDumpAndUsageStatsPermission(getContext(), TAG, pw)) return;
Chris Wrene4b38802015-07-07 15:54:19 -04002515 final DumpFilter filter = DumpFilter.parseFromArguments(args);
2516 if (filter != null && filter.stats) {
2517 dumpJson(pw, filter);
Julia Reynoldsc9842c12017-02-07 12:46:41 -05002518 } else if (filter != null && filter.proto) {
2519 dumpProto(fd, filter);
Chris Wrene4b38802015-07-07 15:54:19 -04002520 } else {
2521 dumpImpl(pw, filter);
2522 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08002523 }
John Spurlockb4782522014-08-22 14:54:46 -04002524
2525 @Override
2526 public ComponentName getEffectsSuppressor() {
Bryce Leeba3d8952016-04-12 12:39:15 -07002527 return !mEffectsSuppressors.isEmpty() ? mEffectsSuppressors.get(0) : null;
John Spurlockb4782522014-08-22 14:54:46 -04002528 }
John Spurlock2b122f42014-08-27 16:29:47 -04002529
2530 @Override
2531 public boolean matchesCallFilter(Bundle extras) {
2532 enforceSystemOrSystemUI("INotificationManager.matchesCallFilter");
Christoph Studer12aeda82014-09-23 19:08:56 +02002533 return mZenModeHelper.matchesCallFilter(
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07002534 Binder.getCallingUserHandle(),
Christoph Studer12aeda82014-09-23 19:08:56 +02002535 extras,
2536 mRankingHelper.findExtractor(ValidateNotificationPeople.class),
2537 MATCHES_CALL_FILTER_CONTACTS_TIMEOUT_MS,
2538 MATCHES_CALL_FILTER_TIMEOUT_AFFINITY);
John Spurlock2b122f42014-08-27 16:29:47 -04002539 }
John Spurlock530052a2014-11-30 16:26:19 -05002540
2541 @Override
2542 public boolean isSystemConditionProviderEnabled(String path) {
Julia Reynoldsbb983d202017-01-06 09:54:20 -05002543 enforceSystemOrSystemUI("INotificationManager.isSystemConditionProviderEnabled");
John Spurlockb2278d62015-04-07 12:47:12 -04002544 return mConditionProviders.isSystemProviderEnabled(path);
John Spurlock530052a2014-11-30 16:26:19 -05002545 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08002546
Christopher Tatef9767d62015-04-08 14:35:43 -07002547 // Backup/restore interface
2548 @Override
2549 public byte[] getBackupPayload(int user) {
John Spurlock35ef0a62015-05-28 11:24:10 -04002550 if (DBG) Slog.d(TAG, "getBackupPayload u=" + user);
Xiaohui Chenddbe4ca2015-08-13 16:20:56 -07002551 //TODO: http://b/22388012
2552 if (user != UserHandle.USER_SYSTEM) {
John Spurlock35ef0a62015-05-28 11:24:10 -04002553 Slog.w(TAG, "getBackupPayload: cannot backup policy for user " + user);
2554 return null;
2555 }
songjinshi9bf22712017-02-04 10:47:45 +08002556 synchronized(mPolicyFile) {
2557 final ByteArrayOutputStream baos = new ByteArrayOutputStream();
2558 try {
2559 writePolicyXml(baos, true /*forBackup*/);
2560 return baos.toByteArray();
2561 } catch (IOException e) {
2562 Slog.w(TAG, "getBackupPayload: error writing payload for user " + user, e);
2563 }
John Spurlock35ef0a62015-05-28 11:24:10 -04002564 }
Christopher Tatef9767d62015-04-08 14:35:43 -07002565 return null;
2566 }
2567
2568 @Override
2569 public void applyRestore(byte[] payload, int user) {
John Spurlock35ef0a62015-05-28 11:24:10 -04002570 if (DBG) Slog.d(TAG, "applyRestore u=" + user + " payload="
2571 + (payload != null ? new String(payload, StandardCharsets.UTF_8) : null));
2572 if (payload == null) {
2573 Slog.w(TAG, "applyRestore: no payload to restore for user " + user);
2574 return;
2575 }
Xiaohui Chenddbe4ca2015-08-13 16:20:56 -07002576 //TODO: http://b/22388012
2577 if (user != UserHandle.USER_SYSTEM) {
John Spurlock35ef0a62015-05-28 11:24:10 -04002578 Slog.w(TAG, "applyRestore: cannot restore policy for user " + user);
2579 return;
2580 }
songjinshi9bf22712017-02-04 10:47:45 +08002581 synchronized(mPolicyFile) {
2582 final ByteArrayInputStream bais = new ByteArrayInputStream(payload);
2583 try {
2584 readPolicyXml(bais, true /*forRestore*/);
2585 savePolicyFile();
2586 } catch (NumberFormatException | XmlPullParserException | IOException e) {
2587 Slog.w(TAG, "applyRestore: error reading payload", e);
2588 }
John Spurlock35ef0a62015-05-28 11:24:10 -04002589 }
Christopher Tatef9767d62015-04-08 14:35:43 -07002590 }
2591
John Spurlock1fc476d2015-04-14 16:05:20 -04002592 @Override
John Spurlock80774932015-05-07 17:38:50 -04002593 public boolean isNotificationPolicyAccessGranted(String pkg) {
2594 return checkPolicyAccess(pkg);
John Spurlock1fc476d2015-04-14 16:05:20 -04002595 }
2596
2597 @Override
Julia Reynolds48034f82016-03-09 10:15:16 -05002598 public boolean isNotificationPolicyAccessGrantedForPackage(String pkg) {;
2599 enforceSystemOrSystemUIOrSamePackage(pkg,
2600 "request policy access status for another package");
Julia Reynoldsa2d01022016-03-18 15:03:43 -04002601 return checkPolicyAccess(pkg);
John Spurlock80774932015-05-07 17:38:50 -04002602 }
2603
2604 @Override
2605 public String[] getPackagesRequestingNotificationPolicyAccess()
2606 throws RemoteException {
2607 enforceSystemOrSystemUI("request policy access packages");
2608 final long identity = Binder.clearCallingIdentity();
2609 try {
John Spurlock7c74f782015-06-04 13:01:42 -04002610 return mPolicyAccess.getRequestingPackages();
John Spurlock80774932015-05-07 17:38:50 -04002611 } finally {
2612 Binder.restoreCallingIdentity(identity);
2613 }
2614 }
2615
2616 @Override
2617 public void setNotificationPolicyAccessGranted(String pkg, boolean granted)
2618 throws RemoteException {
2619 enforceSystemOrSystemUI("grant notification policy access");
2620 final long identity = Binder.clearCallingIdentity();
2621 try {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05002622 synchronized (mNotificationLock) {
John Spurlock80774932015-05-07 17:38:50 -04002623 mPolicyAccess.put(pkg, granted);
2624 }
2625 } finally {
2626 Binder.restoreCallingIdentity(identity);
2627 }
2628 }
2629
2630 @Override
2631 public Policy getNotificationPolicy(String pkg) {
2632 enforcePolicyAccess(pkg, "getNotificationPolicy");
John Spurlock1fc476d2015-04-14 16:05:20 -04002633 final long identity = Binder.clearCallingIdentity();
2634 try {
2635 return mZenModeHelper.getNotificationPolicy();
2636 } finally {
2637 Binder.restoreCallingIdentity(identity);
2638 }
2639 }
2640
2641 @Override
John Spurlock80774932015-05-07 17:38:50 -04002642 public void setNotificationPolicy(String pkg, Policy policy) {
2643 enforcePolicyAccess(pkg, "setNotificationPolicy");
John Spurlock1fc476d2015-04-14 16:05:20 -04002644 final long identity = Binder.clearCallingIdentity();
2645 try {
2646 mZenModeHelper.setNotificationPolicy(policy);
2647 } finally {
2648 Binder.restoreCallingIdentity(identity);
2649 }
2650 }
Chris Wren51017d02015-12-15 15:34:46 -05002651
2652 @Override
Julia Reynolds4b82f6d2017-01-04 10:47:41 -05002653 public void applyEnqueuedAdjustmentFromAssistant(INotificationListener token,
2654 Adjustment adjustment) throws RemoteException {
2655 final long identity = Binder.clearCallingIdentity();
2656 try {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05002657 synchronized (mNotificationLock) {
Julia Reynolds4b82f6d2017-01-04 10:47:41 -05002658 mNotificationAssistants.checkServiceTokenLocked(token);
2659 int N = mEnqueuedNotifications.size();
2660 for (int i = 0; i < N; i++) {
2661 final NotificationRecord n = mEnqueuedNotifications.get(i);
2662 if (Objects.equals(adjustment.getKey(), n.getKey())
2663 && Objects.equals(adjustment.getUser(), n.getUserId())) {
2664 applyAdjustment(n, adjustment);
2665 break;
2666 }
2667 }
2668 }
2669 } finally {
2670 Binder.restoreCallingIdentity(identity);
2671 }
2672 }
2673
2674 @Override
Julia Reynolds52e64d02016-12-09 15:36:12 -05002675 public void applyAdjustmentFromAssistant(INotificationListener token,
Julia Reynoldse46bb372016-03-17 11:05:58 -04002676 Adjustment adjustment) throws RemoteException {
Chris Wren51017d02015-12-15 15:34:46 -05002677 final long identity = Binder.clearCallingIdentity();
2678 try {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05002679 synchronized (mNotificationLock) {
Julia Reynolds77b2cc92016-11-08 14:41:09 -05002680 mNotificationAssistants.checkServiceTokenLocked(token);
Julia Reynolds4b82f6d2017-01-04 10:47:41 -05002681 NotificationRecord n = mNotificationsByKey.get(adjustment.getKey());
2682 applyAdjustment(n, adjustment);
Chris Wren51017d02015-12-15 15:34:46 -05002683 }
Julia Reynolds22f02b32016-12-01 15:05:13 -05002684 mRankingHandler.requestSort(true);
Julia Reynoldse46bb372016-03-17 11:05:58 -04002685 } finally {
2686 Binder.restoreCallingIdentity(identity);
2687 }
2688 }
2689
2690 @Override
Julia Reynolds52e64d02016-12-09 15:36:12 -05002691 public void applyAdjustmentsFromAssistant(INotificationListener token,
Julia Reynoldse46bb372016-03-17 11:05:58 -04002692 List<Adjustment> adjustments) throws RemoteException {
2693
2694 final long identity = Binder.clearCallingIdentity();
2695 try {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05002696 synchronized (mNotificationLock) {
Julia Reynolds77b2cc92016-11-08 14:41:09 -05002697 mNotificationAssistants.checkServiceTokenLocked(token);
Julia Reynoldse46bb372016-03-17 11:05:58 -04002698 for (Adjustment adjustment : adjustments) {
Julia Reynolds4b82f6d2017-01-04 10:47:41 -05002699 NotificationRecord n = mNotificationsByKey.get(adjustment.getKey());
2700 applyAdjustment(n, adjustment);
Julia Reynoldse46bb372016-03-17 11:05:58 -04002701 }
2702 }
Julia Reynolds22f02b32016-12-01 15:05:13 -05002703 mRankingHandler.requestSort(true);
Chris Wren51017d02015-12-15 15:34:46 -05002704 } finally {
2705 Binder.restoreCallingIdentity(identity);
2706 }
2707 }
Julia Reynolds73ed76b2017-04-04 17:04:38 -04002708
2709 @Override
2710 public void updateNotificationChannelFromPrivilegedListener(INotificationListener token,
Julia Reynoldsf27d6b22017-04-13 15:48:16 -04002711 String pkg, UserHandle user, NotificationChannel channel) throws RemoteException {
Julia Reynolds73ed76b2017-04-04 17:04:38 -04002712 Preconditions.checkNotNull(channel);
Julia Reynoldsf27d6b22017-04-13 15:48:16 -04002713 Preconditions.checkNotNull(pkg);
2714 Preconditions.checkNotNull(user);
Julia Reynolds73ed76b2017-04-04 17:04:38 -04002715
Julia Reynoldsf27d6b22017-04-13 15:48:16 -04002716 verifyPrivilegedListener(token, user);
2717 updateNotificationChannelInt(pkg, getUidForPackageAndUser(pkg, user), channel, true);
Julia Reynolds73ed76b2017-04-04 17:04:38 -04002718 }
2719
2720 @Override
2721 public ParceledListSlice<NotificationChannel> getNotificationChannelsFromPrivilegedListener(
Julia Reynoldsf27d6b22017-04-13 15:48:16 -04002722 INotificationListener token, String pkg, UserHandle user) throws RemoteException {
2723 Preconditions.checkNotNull(pkg);
2724 Preconditions.checkNotNull(user);
2725 verifyPrivilegedListener(token, user);
Julia Reynolds73ed76b2017-04-04 17:04:38 -04002726
Julia Reynoldsf27d6b22017-04-13 15:48:16 -04002727 return mRankingHelper.getNotificationChannels(pkg, getUidForPackageAndUser(pkg, user),
2728 false /* includeDeleted */);
Julia Reynolds73ed76b2017-04-04 17:04:38 -04002729 }
2730
2731 @Override
2732 public ParceledListSlice<NotificationChannelGroup>
2733 getNotificationChannelGroupsFromPrivilegedListener(
Julia Reynoldsf27d6b22017-04-13 15:48:16 -04002734 INotificationListener token, String pkg, UserHandle user) throws RemoteException {
2735 Preconditions.checkNotNull(pkg);
2736 Preconditions.checkNotNull(user);
2737 verifyPrivilegedListener(token, user);
2738
2739 List<NotificationChannelGroup> groups = new ArrayList<>();
2740 groups.addAll(mRankingHelper.getNotificationChannelGroups(
2741 pkg, getUidForPackageAndUser(pkg, user)));
2742 return new ParceledListSlice<>(groups);
2743 }
2744
2745 private void verifyPrivilegedListener(INotificationListener token, UserHandle user) {
Julia Reynoldsfeb73412017-04-18 09:28:22 -04002746 ManagedServiceInfo info;
2747 synchronized (mNotificationLock) {
2748 info = mListeners.checkServiceTokenLocked(token);
2749 }
Julia Reynoldsda781472017-04-12 09:41:16 -04002750 if (!hasCompanionDevice(info)) {
2751 throw new SecurityException(info + " does not have access");
2752 }
Julia Reynoldsf27d6b22017-04-13 15:48:16 -04002753 if (!info.enabledAndUserMatches(user.getIdentifier())) {
2754 throw new SecurityException(info + " does not have access");
2755 }
2756 }
Julia Reynolds73ed76b2017-04-04 17:04:38 -04002757
Julia Reynoldsf27d6b22017-04-13 15:48:16 -04002758 private int getUidForPackageAndUser(String pkg, UserHandle user) throws RemoteException {
2759 int uid = 0;
2760 long identity = Binder.clearCallingIdentity();
2761 try {
2762 uid = mPackageManager.getPackageUid(pkg, 0, user.getIdentifier());
2763 } finally {
2764 Binder.restoreCallingIdentity(identity);
2765 }
2766 return uid;
Julia Reynolds73ed76b2017-04-04 17:04:38 -04002767 }
John Spurlock1fc476d2015-04-14 16:05:20 -04002768 };
John Spurlocka4294292014-03-24 18:02:32 -04002769
Julia Reynolds4b82f6d2017-01-04 10:47:41 -05002770 private void applyAdjustment(NotificationRecord n, Adjustment adjustment) {
Julia Reynoldse46bb372016-03-17 11:05:58 -04002771 if (n == null) {
2772 return;
2773 }
Julia Reynoldse46bb372016-03-17 11:05:58 -04002774 if (adjustment.getSignals() != null) {
2775 Bundle.setDefusable(adjustment.getSignals(), true);
Julia Reynolds22f02b32016-12-01 15:05:13 -05002776 final ArrayList<String> people =
2777 adjustment.getSignals().getStringArrayList(Adjustment.KEY_PEOPLE);
2778 final ArrayList<SnoozeCriterion> snoozeCriterionList =
2779 adjustment.getSignals().getParcelableArrayList(Adjustment.KEY_SNOOZE_CRITERIA);
Julia Reynolds22f02b32016-12-01 15:05:13 -05002780 n.setPeopleOverride(people);
2781 n.setSnoozeCriteria(snoozeCriterionList);
Julia Reynoldse46bb372016-03-17 11:05:58 -04002782 }
2783 }
2784
Julia Reynolds88860ce2017-06-01 16:55:49 -04002785 @GuardedBy("mNotificationLock")
Julia Reynolds8f488d32016-10-14 10:59:01 -04002786 private void addAutogroupKeyLocked(String key) {
2787 NotificationRecord n = mNotificationsByKey.get(key);
2788 if (n == null) {
2789 return;
2790 }
Chris Wrenb3921792017-06-01 13:34:46 -04002791 n.setOverrideGroupKey(GroupHelper.AUTOGROUP_KEY);
Julia Reynolds8f488d32016-10-14 10:59:01 -04002792 EventLogTags.writeNotificationAutogrouped(key);
2793 }
2794
Julia Reynolds88860ce2017-06-01 16:55:49 -04002795 @GuardedBy("mNotificationLock")
Julia Reynolds8f488d32016-10-14 10:59:01 -04002796 private void removeAutogroupKeyLocked(String key) {
2797 NotificationRecord n = mNotificationsByKey.get(key);
2798 if (n == null) {
2799 return;
2800 }
Chris Wrenb3921792017-06-01 13:34:46 -04002801 n.setOverrideGroupKey(null);
Julia Reynolds8f488d32016-10-14 10:59:01 -04002802 EventLogTags.writeNotificationUnautogrouped(key);
2803 }
2804
2805 // Clears the 'fake' auto-group summary.
Julia Reynolds88860ce2017-06-01 16:55:49 -04002806 @GuardedBy("mNotificationLock")
Julia Reynolds8f488d32016-10-14 10:59:01 -04002807 private void clearAutogroupSummaryLocked(int userId, String pkg) {
2808 ArrayMap<String, String> summaries = mAutobundledSummaries.get(userId);
2809 if (summaries != null && summaries.containsKey(pkg)) {
2810 // Clear summary.
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05002811 final NotificationRecord removed = findNotificationByKeyLocked(summaries.remove(pkg));
Julia Reynolds8f488d32016-10-14 10:59:01 -04002812 if (removed != null) {
Julia Reynolds0839c022017-06-15 15:24:01 -04002813 boolean wasPosted = removeFromNotificationListsLocked(removed);
2814 cancelNotificationLocked(removed, false, REASON_UNAUTOBUNDLED, wasPosted);
Julia Reynoldse46bb372016-03-17 11:05:58 -04002815 }
2816 }
2817 }
2818
2819 // Posts a 'fake' summary for a package that has exceeded the solo-notification limit.
Julia Reynolds8f488d32016-10-14 10:59:01 -04002820 private void createAutoGroupSummary(int userId, String pkg, String triggeringKey) {
2821 NotificationRecord summaryRecord = null;
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05002822 synchronized (mNotificationLock) {
Julia Reynolds8f488d32016-10-14 10:59:01 -04002823 NotificationRecord notificationRecord = mNotificationsByKey.get(triggeringKey);
2824 if (notificationRecord == null) {
2825 // The notification could have been cancelled again already. A successive
2826 // adjustment will post a summary if needed.
2827 return;
Julia Reynoldse46bb372016-03-17 11:05:58 -04002828 }
Julia Reynolds8f488d32016-10-14 10:59:01 -04002829 final StatusBarNotification adjustedSbn = notificationRecord.sbn;
2830 userId = adjustedSbn.getUser().getIdentifier();
2831 ArrayMap<String, String> summaries = mAutobundledSummaries.get(userId);
2832 if (summaries == null) {
2833 summaries = new ArrayMap<>();
2834 }
2835 mAutobundledSummaries.put(userId, summaries);
2836 if (!summaries.containsKey(pkg)) {
2837 // Add summary
2838 final ApplicationInfo appInfo =
2839 adjustedSbn.getNotification().extras.getParcelable(
2840 Notification.EXTRA_BUILDER_APPLICATION_INFO);
2841 final Bundle extras = new Bundle();
2842 extras.putParcelable(Notification.EXTRA_BUILDER_APPLICATION_INFO, appInfo);
Geoffrey Pitschaf759c52017-02-15 09:35:38 -05002843 final String channelId = notificationRecord.getChannel().getId();
Julia Reynolds8f488d32016-10-14 10:59:01 -04002844 final Notification summaryNotification =
Geoffrey Pitschaf759c52017-02-15 09:35:38 -05002845 new Notification.Builder(getContext(), channelId)
2846 .setSmallIcon(adjustedSbn.getNotification().getSmallIcon())
Julia Reynolds8f488d32016-10-14 10:59:01 -04002847 .setGroupSummary(true)
Julia Reynolds9d5786e2017-04-28 10:26:32 -04002848 .setGroupAlertBehavior(Notification.GROUP_ALERT_CHILDREN)
Julia Reynolds8f488d32016-10-14 10:59:01 -04002849 .setGroup(GroupHelper.AUTOGROUP_KEY)
2850 .setFlag(Notification.FLAG_AUTOGROUP_SUMMARY, true)
2851 .setFlag(Notification.FLAG_GROUP_SUMMARY, true)
2852 .setColor(adjustedSbn.getNotification().color)
2853 .setLocalOnly(true)
2854 .build();
2855 summaryNotification.extras.putAll(extras);
2856 Intent appIntent = getContext().getPackageManager().getLaunchIntentForPackage(pkg);
2857 if (appIntent != null) {
2858 summaryNotification.contentIntent = PendingIntent.getActivityAsUser(
2859 getContext(), 0, appIntent, 0, null, UserHandle.of(userId));
2860 }
2861 final StatusBarNotification summarySbn =
2862 new StatusBarNotification(adjustedSbn.getPackageName(),
Julia Reynolds423b9fc2016-11-09 09:51:08 -05002863 adjustedSbn.getOpPkg(),
Julia Reynolds423b9fc2016-11-09 09:51:08 -05002864 Integer.MAX_VALUE,
Julia Reynolds8f488d32016-10-14 10:59:01 -04002865 GroupHelper.AUTOGROUP_KEY, adjustedSbn.getUid(),
2866 adjustedSbn.getInitialPid(), summaryNotification,
2867 adjustedSbn.getUser(), GroupHelper.AUTOGROUP_KEY,
2868 System.currentTimeMillis());
Julia Reynolds924eed12017-01-19 09:52:07 -05002869 summaryRecord = new NotificationRecord(getContext(), summarySbn,
Geoffrey Pitscha22f6442017-05-05 16:47:38 +00002870 notificationRecord.getChannel());
Julia Reynolds8f488d32016-10-14 10:59:01 -04002871 summaries.put(pkg, summarySbn.getKey());
2872 }
2873 }
Julia Reynoldsd94054f2017-02-01 11:11:06 -05002874 if (summaryRecord != null && checkDisqualifyingFeatures(userId, MY_UID,
2875 summaryRecord.sbn.getId(), summaryRecord.sbn.getTag(), summaryRecord)) {
Julia Reynolds8f488d32016-10-14 10:59:01 -04002876 mHandler.post(new EnqueueNotificationRunnable(userId, summaryRecord));
Julia Reynoldse46bb372016-03-17 11:05:58 -04002877 }
2878 }
2879
John Spurlock32fe4c62014-10-02 12:16:02 -04002880 private String disableNotificationEffects(NotificationRecord record) {
2881 if (mDisableNotificationEffects) {
2882 return "booleanState";
2883 }
2884 if ((mListenerHints & HINT_HOST_DISABLE_EFFECTS) != 0) {
2885 return "listenerHints";
2886 }
2887 if (mCallState != TelephonyManager.CALL_STATE_IDLE && !mZenModeHelper.isCall(record)) {
2888 return "callState";
2889 }
2890 return null;
Chris Wrene4b38802015-07-07 15:54:19 -04002891 };
2892
2893 private void dumpJson(PrintWriter pw, DumpFilter filter) {
2894 JSONObject dump = new JSONObject();
2895 try {
2896 dump.put("service", "Notification Manager");
Chris Wrenacf424a2016-03-15 12:48:55 -04002897 dump.put("bans", mRankingHelper.dumpBansJson(filter));
2898 dump.put("ranking", mRankingHelper.dumpJson(filter));
Chris Wrene4b38802015-07-07 15:54:19 -04002899 dump.put("stats", mUsageStats.dumpJson(filter));
Julia Reynoldsd373d782017-03-03 13:32:57 -05002900 dump.put("channels", mRankingHelper.dumpChannelsJson(filter));
Chris Wrene4b38802015-07-07 15:54:19 -04002901 } catch (JSONException e) {
2902 e.printStackTrace();
2903 }
2904 pw.println(dump);
John Spurlock1fa865f2014-07-21 14:56:39 -04002905 }
2906
Julia Reynoldsc9842c12017-02-07 12:46:41 -05002907 private void dumpProto(FileDescriptor fd, DumpFilter filter) {
2908 final ProtoOutputStream proto = new ProtoOutputStream(fd);
2909 synchronized (mNotificationLock) {
2910 long records = proto.start(NotificationServiceDumpProto.RECORDS);
2911 int N = mNotificationList.size();
2912 if (N > 0) {
2913 for (int i = 0; i < N; i++) {
2914 final NotificationRecord nr = mNotificationList.get(i);
2915 if (filter.filtered && !filter.matches(nr.sbn)) continue;
2916 nr.dump(proto, filter.redact);
2917 proto.write(NotificationRecordProto.STATE, NotificationServiceProto.POSTED);
2918 }
2919 }
2920 N = mEnqueuedNotifications.size();
2921 if (N > 0) {
2922 for (int i = 0; i < N; i++) {
2923 final NotificationRecord nr = mEnqueuedNotifications.get(i);
2924 if (filter.filtered && !filter.matches(nr.sbn)) continue;
2925 nr.dump(proto, filter.redact);
2926 proto.write(NotificationRecordProto.STATE, NotificationServiceProto.ENQUEUED);
2927 }
2928 }
Julia Reynolds520df6e2017-02-13 09:05:10 -05002929 List<NotificationRecord> snoozed = mSnoozeHelper.getSnoozed();
2930 N = snoozed.size();
2931 if (N > 0) {
2932 for (int i = 0; i < N; i++) {
2933 final NotificationRecord nr = snoozed.get(i);
2934 if (filter.filtered && !filter.matches(nr.sbn)) continue;
2935 nr.dump(proto, filter.redact);
2936 proto.write(NotificationRecordProto.STATE, NotificationServiceProto.SNOOZED);
2937 }
2938 }
Julia Reynoldsc9842c12017-02-07 12:46:41 -05002939 proto.end(records);
2940 }
Julia Reynolds520df6e2017-02-13 09:05:10 -05002941
2942 long zenLog = proto.start(NotificationServiceDumpProto.ZEN);
2943 mZenModeHelper.dump(proto);
2944 for (ComponentName suppressor : mEffectsSuppressors) {
2945 proto.write(ZenModeProto.SUPPRESSORS, suppressor.toString());
2946 }
2947 proto.end(zenLog);
2948
Julia Reynoldsc9842c12017-02-07 12:46:41 -05002949 proto.flush();
2950 }
2951
John Spurlock25e2d242014-06-27 13:58:23 -04002952 void dumpImpl(PrintWriter pw, DumpFilter filter) {
2953 pw.print("Current Notification Manager state");
Dan Sandlera1770312015-07-10 13:59:29 -04002954 if (filter.filtered) {
John Spurlock50806fc2014-07-15 10:22:02 -04002955 pw.print(" (filtered to "); pw.print(filter); pw.print(")");
John Spurlock25e2d242014-06-27 13:58:23 -04002956 }
2957 pw.println(':');
Adam Lesinski182f73f2013-12-05 16:48:06 -08002958 int N;
Julia Reynoldse6b53e62015-07-31 09:25:10 -04002959 final boolean zenOnly = filter.filtered && filter.zen;
Adam Lesinski182f73f2013-12-05 16:48:06 -08002960
John Spurlock50806fc2014-07-15 10:22:02 -04002961 if (!zenOnly) {
2962 synchronized (mToastQueue) {
2963 N = mToastQueue.size();
2964 if (N > 0) {
2965 pw.println(" Toast Queue:");
2966 for (int i=0; i<N; i++) {
2967 mToastQueue.get(i).dump(pw, " ", filter);
2968 }
2969 pw.println(" ");
Adam Lesinski182f73f2013-12-05 16:48:06 -08002970 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08002971 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08002972 }
2973
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05002974 synchronized (mNotificationLock) {
John Spurlock50806fc2014-07-15 10:22:02 -04002975 if (!zenOnly) {
2976 N = mNotificationList.size();
John Spurlock25e2d242014-06-27 13:58:23 -04002977 if (N > 0) {
John Spurlock50806fc2014-07-15 10:22:02 -04002978 pw.println(" Notification List:");
John Spurlock25e2d242014-06-27 13:58:23 -04002979 for (int i=0; i<N; i++) {
John Spurlock50806fc2014-07-15 10:22:02 -04002980 final NotificationRecord nr = mNotificationList.get(i);
Julia Reynoldse6b53e62015-07-31 09:25:10 -04002981 if (filter.filtered && !filter.matches(nr.sbn)) continue;
Dan Sandlera1770312015-07-10 13:59:29 -04002982 nr.dump(pw, " ", getContext(), filter.redact);
John Spurlock25e2d242014-06-27 13:58:23 -04002983 }
2984 pw.println(" ");
Adam Lesinski182f73f2013-12-05 16:48:06 -08002985 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08002986
Julia Reynoldse6b53e62015-07-31 09:25:10 -04002987 if (!filter.filtered) {
John Spurlock50806fc2014-07-15 10:22:02 -04002988 N = mLights.size();
2989 if (N > 0) {
2990 pw.println(" Lights List:");
2991 for (int i=0; i<N; i++) {
Chris Wren6054e612014-11-25 17:16:46 -05002992 if (i == N - 1) {
2993 pw.print(" > ");
2994 } else {
2995 pw.print(" ");
2996 }
2997 pw.println(mLights.get(i));
John Spurlock50806fc2014-07-15 10:22:02 -04002998 }
2999 pw.println(" ");
3000 }
John Spurlockcb566aa2014-08-03 22:58:28 -04003001 pw.println(" mUseAttentionLight=" + mUseAttentionLight);
3002 pw.println(" mNotificationPulseEnabled=" + mNotificationPulseEnabled);
Chris Wren6054e612014-11-25 17:16:46 -05003003 pw.println(" mSoundNotificationKey=" + mSoundNotificationKey);
3004 pw.println(" mVibrateNotificationKey=" + mVibrateNotificationKey);
John Spurlockd8afe3c2014-08-01 14:04:07 -04003005 pw.println(" mDisableNotificationEffects=" + mDisableNotificationEffects);
John Spurlock32fe4c62014-10-02 12:16:02 -04003006 pw.println(" mCallState=" + callStateToString(mCallState));
John Spurlock50806fc2014-07-15 10:22:02 -04003007 pw.println(" mSystemReady=" + mSystemReady);
Chris Wren763a9bb2016-05-31 17:14:12 -04003008 pw.println(" mMaxPackageEnqueueRate=" + mMaxPackageEnqueueRate);
John Spurlock50806fc2014-07-15 10:22:02 -04003009 }
3010 pw.println(" mArchive=" + mArchive.toString());
3011 Iterator<StatusBarNotification> iter = mArchive.descendingIterator();
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05003012 int j=0;
John Spurlock50806fc2014-07-15 10:22:02 -04003013 while (iter.hasNext()) {
3014 final StatusBarNotification sbn = iter.next();
3015 if (filter != null && !filter.matches(sbn)) continue;
3016 pw.println(" " + sbn);
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05003017 if (++j >= 5) {
John Spurlock50806fc2014-07-15 10:22:02 -04003018 if (iter.hasNext()) pw.println(" ...");
3019 break;
3020 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08003021 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08003022
Julia Reynolds4b82f6d2017-01-04 10:47:41 -05003023 if (!zenOnly) {
3024 N = mEnqueuedNotifications.size();
3025 if (N > 0) {
3026 pw.println(" Enqueued Notification List:");
3027 for (int i = 0; i < N; i++) {
3028 final NotificationRecord nr = mEnqueuedNotifications.get(i);
3029 if (filter.filtered && !filter.matches(nr.sbn)) continue;
3030 nr.dump(pw, " ", getContext(), filter.redact);
3031 }
3032 pw.println(" ");
3033 }
Julia Reynolds520df6e2017-02-13 09:05:10 -05003034
3035 mSnoozeHelper.dump(pw, filter);
Julia Reynolds4b82f6d2017-01-04 10:47:41 -05003036 }
3037 }
3038
John Spurlock50806fc2014-07-15 10:22:02 -04003039 if (!zenOnly) {
John Spurlock50806fc2014-07-15 10:22:02 -04003040 pw.println("\n Ranking Config:");
3041 mRankingHelper.dump(pw, " ", filter);
Chris Wren54bbef42014-07-09 18:37:56 -04003042
John Spurlock50806fc2014-07-15 10:22:02 -04003043 pw.println("\n Notification listeners:");
3044 mListeners.dump(pw, filter);
John Spurlockd8afe3c2014-08-01 14:04:07 -04003045 pw.print(" mListenerHints: "); pw.println(mListenerHints);
3046 pw.print(" mListenersDisablingEffects: (");
3047 N = mListenersDisablingEffects.size();
John Spurlock1fa865f2014-07-21 14:56:39 -04003048 for (int i = 0; i < N; i++) {
Bryce Lee7219ada2016-04-08 10:54:23 -07003049 final int hint = mListenersDisablingEffects.keyAt(i);
3050 if (i > 0) pw.print(';');
3051 pw.print("hint[" + hint + "]:");
3052
3053 final ArraySet<ManagedServiceInfo> listeners =
3054 mListenersDisablingEffects.valueAt(i);
3055 final int listenerSize = listeners.size();
3056
3057 for (int j = 0; j < listenerSize; j++) {
3058 if (i > 0) pw.print(',');
3059 final ManagedServiceInfo listener = listeners.valueAt(i);
3060 pw.print(listener.component);
3061 }
John Spurlock1fa865f2014-07-21 14:56:39 -04003062 }
3063 pw.println(')');
Julia Reynolds77b2cc92016-11-08 14:41:09 -05003064 pw.println("\n Notification assistant services:");
3065 mNotificationAssistants.dump(pw, filter);
John Spurlock50806fc2014-07-15 10:22:02 -04003066 }
Julia Reynolds72f1cbb2016-09-19 14:57:31 -04003067
Julia Reynolds520df6e2017-02-13 09:05:10 -05003068 if (!filter.filtered || zenOnly) {
3069 pw.println("\n Zen Mode:");
3070 pw.print(" mInterruptionFilter="); pw.println(mInterruptionFilter);
3071 mZenModeHelper.dump(pw, " ");
3072
3073 pw.println("\n Zen Log:");
3074 ZenLog.dump(pw, " ");
Julia Reynolds72f1cbb2016-09-19 14:57:31 -04003075 }
3076
John Spurlock80774932015-05-07 17:38:50 -04003077 pw.println("\n Policy access:");
3078 pw.print(" mPolicyAccess: "); pw.println(mPolicyAccess);
John Spurlocke77bb362014-04-26 10:24:59 -04003079
3080 pw.println("\n Condition providers:");
John Spurlock25e2d242014-06-27 13:58:23 -04003081 mConditionProviders.dump(pw, filter);
Christoph Studer265c1052014-07-23 17:14:33 +02003082
3083 pw.println("\n Group summaries:");
3084 for (Entry<String, NotificationRecord> entry : mSummaryByGroupKey.entrySet()) {
3085 NotificationRecord r = entry.getValue();
3086 pw.println(" " + entry.getKey() + " -> " + r.getKey());
3087 if (mNotificationsByKey.get(r.getKey()) != r) {
3088 pw.println("!!!!!!LEAK: Record not found in mNotificationsByKey.");
Dan Sandlera1770312015-07-10 13:59:29 -04003089 r.dump(pw, " ", getContext(), filter.redact);
Christoph Studer265c1052014-07-23 17:14:33 +02003090 }
3091 }
Julia Reynolds520df6e2017-02-13 09:05:10 -05003092
3093 if (!zenOnly) {
3094 pw.println("\n Usage Stats:");
3095 mUsageStats.dump(pw, " ", filter);
3096 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003097 }
3098 }
3099
Adam Lesinski182f73f2013-12-05 16:48:06 -08003100 /**
3101 * The private API only accessible to the system process.
3102 */
3103 private final NotificationManagerInternal mInternalService = new NotificationManagerInternal() {
3104 @Override
Christoph Studer8fd7f1e2014-04-11 17:35:05 -04003105 public void enqueueNotification(String pkg, String opPkg, int callingUid, int callingPid,
Julia Reynoldsfea6f7b2017-04-19 13:50:12 -04003106 String tag, int id, Notification notification, int userId) {
Christoph Studer8fd7f1e2014-04-11 17:35:05 -04003107 enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, notification,
Julia Reynoldsfea6f7b2017-04-19 13:50:12 -04003108 userId);
Adam Lesinski182f73f2013-12-05 16:48:06 -08003109 }
Christoph Studer365e4c32014-09-18 20:35:36 +02003110
3111 @Override
3112 public void removeForegroundServiceFlagFromNotification(String pkg, int notificationId,
3113 int userId) {
3114 checkCallerIsSystem();
Geoffrey Pitsch87237d72017-04-13 13:44:09 -04003115 mHandler.post(new Runnable() {
3116 @Override
3117 public void run() {
3118 synchronized (mNotificationLock) {
Julia Reynoldsfeb73412017-04-18 09:28:22 -04003119 removeForegroundServiceFlagByListLocked(
3120 mEnqueuedNotifications, pkg, notificationId, userId);
3121 removeForegroundServiceFlagByListLocked(
3122 mNotificationList, pkg, notificationId, userId);
Julia Reynolds8f488d32016-10-14 10:59:01 -04003123 }
Geoffrey Pitsch87237d72017-04-13 13:44:09 -04003124 }
3125 });
3126 }
3127
Julia Reynolds88860ce2017-06-01 16:55:49 -04003128 @GuardedBy("mNotificationLock")
Geoffrey Pitsch87237d72017-04-13 13:44:09 -04003129 private void removeForegroundServiceFlagByListLocked(
Julia Reynolds88860ce2017-06-01 16:55:49 -04003130 ArrayList<NotificationRecord> notificationList, String pkg, int notificationId,
3131 int userId) {
3132 NotificationRecord r = findNotificationByListLocked(
3133 notificationList, pkg, null, notificationId, userId);
Geoffrey Pitsch87237d72017-04-13 13:44:09 -04003134 if (r == null) {
3135 return;
Christoph Studer365e4c32014-09-18 20:35:36 +02003136 }
Geoffrey Pitsch87237d72017-04-13 13:44:09 -04003137 StatusBarNotification sbn = r.sbn;
3138 // NoMan adds flags FLAG_NO_CLEAR and FLAG_ONGOING_EVENT when it sees
3139 // FLAG_FOREGROUND_SERVICE. Hence it's not enough to remove
3140 // FLAG_FOREGROUND_SERVICE, we have to revert to the flags we received
3141 // initially *and* force remove FLAG_FOREGROUND_SERVICE.
3142 sbn.getNotification().flags =
3143 (r.mOriginalFlags & ~Notification.FLAG_FOREGROUND_SERVICE);
3144 mRankingHelper.sort(mNotificationList);
3145 mListeners.notifyPostedLocked(sbn, sbn /* oldSbn */);
3146 mGroupHelper.onNotificationPosted(sbn);
Christoph Studer365e4c32014-09-18 20:35:36 +02003147 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08003148 };
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003149
Christoph Studer8fd7f1e2014-04-11 17:35:05 -04003150 void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,
Scott Greenwald9b05c612013-06-25 23:44:05 -04003151 final int callingPid, final String tag, final int id, final Notification notification,
Julia Reynoldsfea6f7b2017-04-19 13:50:12 -04003152 int incomingUserId) {
Daniel Sandler0da673f2012-04-11 12:33:16 -04003153 if (DBG) {
Adam Lesinski182f73f2013-12-05 16:48:06 -08003154 Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id
3155 + " notification=" + notification);
Daniel Sandler0da673f2012-04-11 12:33:16 -04003156 }
John Spurlock7340fc82014-04-24 18:50:12 -04003157 checkCallerIsSystemOrSameApp(pkg);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003158
Scott Greenwald9b05c612013-06-25 23:44:05 -04003159 final int userId = ActivityManager.handleIncomingUser(callingPid,
3160 callingUid, incomingUserId, true, false, "enqueueNotification", pkg);
Jeff Sharkey65c4a2b2012-09-25 17:22:27 -07003161 final UserHandle user = new UserHandle(userId);
Dianne Hackborn41203752012-08-31 14:05:51 -07003162
Julia Reynoldsd94054f2017-02-01 11:11:06 -05003163 if (pkg == null || notification == null) {
3164 throw new IllegalArgumentException("null not allowed: pkg=" + pkg
3165 + " id=" + id + " notification=" + notification);
3166 }
Svetoslav Ganov2a67ee82017-02-16 18:57:57 -08003167
3168 // The system can post notifications for any package, let us resolve that.
3169 final int notificationUid = resolveNotificationUid(opPkg, callingUid, userId);
3170
Julia Reynoldse46bb372016-03-17 11:05:58 -04003171 // Fix the notification as best we can.
3172 try {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05003173 final ApplicationInfo ai = mPackageManagerClient.getApplicationInfoAsUser(
Jeff Sharkey012bc7b2016-04-11 16:30:27 -06003174 pkg, PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
3175 (userId == UserHandle.USER_ALL) ? UserHandle.USER_SYSTEM : userId);
Julia Reynoldse071abd2017-03-22 10:52:11 -04003176 Notification.addFieldsFromContext(ai, notification);
Julia Reynoldse46bb372016-03-17 11:05:58 -04003177 } catch (NameNotFoundException e) {
3178 Slog.e(TAG, "Cannot create a context for sending app", e);
3179 return;
3180 }
3181
Chris Wren888b7a82016-06-17 15:47:19 -04003182 mUsageStats.registerEnqueuedByApp(pkg);
3183
Julia Reynoldsd94054f2017-02-01 11:11:06 -05003184 // setup local book-keeping
Julia Reynoldsbad42972017-04-25 13:52:49 -04003185 String channelId = notification.getChannelId();
3186 if (mIsTelevision && (new Notification.TvExtender(notification)).getChannelId() != null) {
3187 channelId = (new Notification.TvExtender(notification)).getChannelId();
Julia Reynolds5f20e9f2017-01-30 08:54:53 -05003188 }
Geoffrey Pitsch1f17e022017-01-03 16:44:20 -05003189 final NotificationChannel channel = mRankingHelper.getNotificationChannel(pkg,
Svetoslav Ganov2a67ee82017-02-16 18:57:57 -08003190 notificationUid, channelId, false /* includeDeleted */);
Geoffrey Pitsch1f17e022017-01-03 16:44:20 -05003191 if (channel == null) {
Geoffrey Pitsch96cac7f2017-03-23 17:57:12 -04003192 final String noChannelStr = "No Channel found for "
3193 + "pkg=" + pkg
3194 + ", channelId=" + channelId
Julia Reynoldsf26eb912017-05-22 15:47:06 -04003195 + ", id=" + id
3196 + ", tag=" + tag
Geoffrey Pitsch96cac7f2017-03-23 17:57:12 -04003197 + ", opPkg=" + opPkg
3198 + ", callingUid=" + callingUid
3199 + ", userId=" + userId
3200 + ", incomingUserId=" + incomingUserId
3201 + ", notificationUid=" + notificationUid
3202 + ", notification=" + notification;
Geoffrey Pitsch96cac7f2017-03-23 17:57:12 -04003203 Log.e(TAG, noChannelStr);
Geoffrey Pitsch4c6eef22017-04-19 10:26:45 -04003204 doChannelWarningToast("Developer warning for package \"" + pkg + "\"\n" +
Geoffrey Pitschcadb5dc2017-04-11 11:35:02 -04003205 "Failed to post notification on channel \"" + channelId + "\"\n" +
Geoffrey Pitsch86c11e602017-04-17 15:28:40 -04003206 "See log for more details");
Geoffrey Pitsch96cac7f2017-03-23 17:57:12 -04003207 return;
Geoffrey Pitsch1f17e022017-01-03 16:44:20 -05003208 }
Geoffrey Pitsch86c11e602017-04-17 15:28:40 -04003209
Chris Wrena61f1792016-08-04 11:24:42 -04003210 final StatusBarNotification n = new StatusBarNotification(
Svetoslav Ganov2a67ee82017-02-16 18:57:57 -08003211 pkg, opPkg, id, tag, notificationUid, callingPid, notification,
Julia Reynolds423b9fc2016-11-09 09:51:08 -05003212 user, null, System.currentTimeMillis());
Geoffrey Pitscha22f6442017-05-05 16:47:38 +00003213 final NotificationRecord r = new NotificationRecord(getContext(), n, channel);
Chris Wrena61f1792016-08-04 11:24:42 -04003214
Julia Reynolds4da79702017-06-01 11:06:10 -04003215 if (!checkDisqualifyingFeatures(userId, notificationUid, id, tag, r)) {
Julia Reynoldsd94054f2017-02-01 11:11:06 -05003216 return;
Joe Onoratobd73d012010-06-04 11:44:54 -07003217 }
3218
Felipe Lemedd85da62016-06-28 11:29:54 -07003219 // Whitelist pending intents.
3220 if (notification.allPendingIntents != null) {
3221 final int intentCount = notification.allPendingIntents.size();
3222 if (intentCount > 0) {
3223 final ActivityManagerInternal am = LocalServices
3224 .getService(ActivityManagerInternal.class);
3225 final long duration = LocalServices.getService(
3226 DeviceIdleController.LocalService.class).getNotificationWhitelistDuration();
3227 for (int i = 0; i < intentCount; i++) {
3228 PendingIntent pendingIntent = notification.allPendingIntents.valueAt(i);
3229 if (pendingIntent != null) {
Dianne Hackborn98305522017-05-05 17:53:53 -07003230 am.setPendingIntentWhitelistDuration(pendingIntent.getTarget(),
3231 WHITELIST_TOKEN, duration);
Felipe Lemedd85da62016-06-28 11:29:54 -07003232 }
3233 }
3234 }
3235 }
Felipe Lemea1b79bf2016-05-24 13:06:54 -07003236
Chris Wren47633422016-01-22 09:56:59 -05003237 mHandler.post(new EnqueueNotificationRunnable(userId, r));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003238 }
3239
Geoffrey Pitsch4c6eef22017-04-19 10:26:45 -04003240 private void doChannelWarningToast(CharSequence toastText) {
Geoffrey Pitsch507822d2017-05-11 12:57:22 -04003241 final int defaultWarningEnabled = Build.IS_DEBUGGABLE ? 1 : 0;
Geoffrey Pitsch2486f892017-05-22 10:53:44 -04003242 final boolean warningEnabled = Settings.Global.getInt(getContext().getContentResolver(),
Geoffrey Pitsch507822d2017-05-11 12:57:22 -04003243 Settings.Global.SHOW_NOTIFICATION_CHANNEL_WARNINGS, defaultWarningEnabled) != 0;
3244 if (warningEnabled) {
Geoffrey Pitsch2486f892017-05-22 10:53:44 -04003245 Toast toast = Toast.makeText(getContext(), mHandler.getLooper(), toastText,
Geoffrey Pitsch5bdddbd2017-05-26 10:50:05 -04003246 Toast.LENGTH_SHORT);
Geoffrey Pitschd34c1872017-05-04 16:02:15 -04003247 toast.show();
Geoffrey Pitsch86c11e602017-04-17 15:28:40 -04003248 }
3249 }
3250
Svetoslav Ganov2a67ee82017-02-16 18:57:57 -08003251 private int resolveNotificationUid(String opPackageName, int callingUid, int userId) {
3252 // The system can post notifications on behalf of any package it wants
Geoffrey Pitsch27684152017-05-02 11:41:31 -04003253 if (isCallerSystemOrPhone() && opPackageName != null && !"android".equals(opPackageName)) {
Svetoslav Ganov2a67ee82017-02-16 18:57:57 -08003254 try {
3255 return getContext().getPackageManager()
3256 .getPackageUidAsUser(opPackageName, userId);
3257 } catch (NameNotFoundException e) {
3258 /* ignore */
3259 }
3260 }
3261 return callingUid;
3262 }
3263
Julia Reynoldsd94054f2017-02-01 11:11:06 -05003264 /**
3265 * Checks if a notification can be posted. checks rate limiter, snooze helper, and blocking.
3266 *
3267 * Has side effects.
3268 */
3269 private boolean checkDisqualifyingFeatures(int userId, int callingUid, int id, String tag,
3270 NotificationRecord r) {
3271 final String pkg = r.sbn.getPackageName();
Geoffrey Pitsch27684152017-05-02 11:41:31 -04003272 final boolean isSystemNotification =
3273 isUidSystemOrPhone(callingUid) || ("android".equals(pkg));
Julia Reynoldsd94054f2017-02-01 11:11:06 -05003274 final boolean isNotificationFromListener = mListeners.isListenerPackage(pkg);
3275
3276 // Limit the number of notifications that any given package except the android
3277 // package or a registered listener can enqueue. Prevents DOS attacks and deals with leaks.
3278 if (!isSystemNotification && !isNotificationFromListener) {
3279 synchronized (mNotificationLock) {
3280 if (mNotificationsByKey.get(r.sbn.getKey()) != null) {
3281 // this is an update, rate limit updates only
3282 final float appEnqueueRate = mUsageStats.getAppEnqueueRate(pkg);
3283 if (appEnqueueRate > mMaxPackageEnqueueRate) {
3284 mUsageStats.registerOverRateQuota(pkg);
3285 final long now = SystemClock.elapsedRealtime();
3286 if ((now - mLastOverRateLogTime) > MIN_PACKAGE_OVERRATE_LOG_INTERVAL) {
3287 Slog.e(TAG, "Package enqueue rate is " + appEnqueueRate
3288 + ". Shedding events. package=" + pkg);
3289 mLastOverRateLogTime = now;
3290 }
3291 return false;
3292 }
Chad Brubaker6b68f102017-01-27 13:39:00 -08003293 } else if (isCallerInstantApp(pkg)) {
Julia Reynoldsa78cdff2017-04-26 10:19:25 -04003294 // Ephemeral apps have some special constraints for notifications.
Chad Brubaker6b68f102017-01-27 13:39:00 -08003295 // They are not allowed to create new notifications however they are allowed to
3296 // update notifications created by the system (e.g. a foreground service
3297 // notification).
3298 throw new SecurityException("Instant app " + pkg
3299 + " cannot create notifications");
Julia Reynoldsd94054f2017-02-01 11:11:06 -05003300 }
3301
3302 int count = 0;
3303 final int N = mNotificationList.size();
3304 for (int i=0; i<N; i++) {
3305 final NotificationRecord existing = mNotificationList.get(i);
3306 if (existing.sbn.getPackageName().equals(pkg)
3307 && existing.sbn.getUserId() == userId) {
3308 if (existing.sbn.getId() == id
3309 && TextUtils.equals(existing.sbn.getTag(), tag)) {
3310 break; // Allow updating existing notification
3311 }
3312 count++;
3313 if (count >= MAX_PACKAGE_NOTIFICATIONS) {
3314 mUsageStats.registerOverCountQuota(pkg);
3315 Slog.e(TAG, "Package has already posted " + count
3316 + " notifications. Not showing more. package=" + pkg);
3317 return false;
3318 }
3319 }
3320 }
3321 }
3322 }
3323
3324 // snoozed apps
3325 if (mSnoozeHelper.isSnoozed(userId, pkg, r.getKey())) {
Julia Reynolds520df6e2017-02-13 09:05:10 -05003326 MetricsLogger.action(r.getLogMaker()
3327 .setType(MetricsProto.MetricsEvent.TYPE_UPDATE)
3328 .setCategory(MetricsProto.MetricsEvent.NOTIFICATION_SNOOZED));
Julia Reynoldsd94054f2017-02-01 11:11:06 -05003329 if (DBG) {
3330 Slog.d(TAG, "Ignored enqueue for snoozed notification " + r.getKey());
3331 }
3332 mSnoozeHelper.update(userId, r);
3333 savePolicyFile();
3334 return false;
3335 }
3336
3337
3338 // blocked apps
3339 if (isBlocked(r, mUsageStats)) {
3340 return false;
3341 }
3342
3343 return true;
3344 }
3345
3346 protected boolean isBlocked(NotificationRecord r, NotificationUsageStats usageStats) {
3347 final String pkg = r.sbn.getPackageName();
3348 final int callingUid = r.sbn.getUid();
3349
3350 final boolean isPackageSuspended = isPackageSuspendedForUser(pkg, callingUid);
3351 if (isPackageSuspended) {
3352 Slog.e(TAG, "Suppressing notification from package due to package "
3353 + "suspended by administrator.");
3354 usageStats.registerSuspendedByAdmin(r);
3355 return isPackageSuspended;
3356 }
3357
Julia Reynolds4da79702017-06-01 11:06:10 -04003358 final boolean isBlocked =
3359 mRankingHelper.getImportance(pkg, callingUid) == NotificationManager.IMPORTANCE_NONE
Julia Reynolds5fe2eae2017-05-22 08:45:27 -04003360 || r.getChannel().getImportance() == NotificationManager.IMPORTANCE_NONE;
Julia Reynoldsd94054f2017-02-01 11:11:06 -05003361 if (isBlocked) {
3362 Slog.e(TAG, "Suppressing notification from package by user request.");
3363 usageStats.registerBlocked(r);
3364 }
3365 return isBlocked;
3366 }
3367
Julia Reynoldsa78cdff2017-04-26 10:19:25 -04003368 protected class SnoozeNotificationRunnable implements Runnable {
3369 private final String mKey;
3370 private final long mDuration;
3371 private final String mSnoozeCriterionId;
3372
3373 SnoozeNotificationRunnable(String key, long duration, String snoozeCriterionId) {
3374 mKey = key;
3375 mDuration = duration;
3376 mSnoozeCriterionId = snoozeCriterionId;
3377 }
3378
3379 @Override
3380 public void run() {
3381 synchronized (mNotificationLock) {
3382 final NotificationRecord r = findNotificationByKeyLocked(mKey);
3383 if (r != null) {
3384 snoozeLocked(r);
3385 }
3386 }
3387 }
3388
Julia Reynolds88860ce2017-06-01 16:55:49 -04003389 @GuardedBy("mNotificationLock")
Julia Reynoldsa78cdff2017-04-26 10:19:25 -04003390 void snoozeLocked(NotificationRecord r) {
3391 if (r.sbn.isGroup()) {
3392 final List<NotificationRecord> groupNotifications = findGroupNotificationsLocked(
3393 r.sbn.getPackageName(), r.sbn.getGroupKey(), r.sbn.getUserId());
3394 if (r.getNotification().isGroupSummary()) {
3395 // snooze summary and all children
3396 for (int i = 0; i < groupNotifications.size(); i++) {
3397 snoozeNotificationLocked(groupNotifications.get(i));
3398 }
3399 } else {
3400 // if there is a valid summary for this group, and we are snoozing the only
3401 // child, also snooze the summary
3402 if (mSummaryByGroupKey.containsKey(r.sbn.getGroupKey())) {
3403 if (groupNotifications.size() != 2) {
3404 snoozeNotificationLocked(r);
3405 } else {
3406 // snooze summary and the one child
3407 for (int i = 0; i < groupNotifications.size(); i++) {
3408 snoozeNotificationLocked(groupNotifications.get(i));
3409 }
3410 }
3411 } else {
3412 snoozeNotificationLocked(r);
3413 }
3414 }
3415 } else {
3416 // just snooze the one notification
3417 snoozeNotificationLocked(r);
3418 }
3419 }
3420
Julia Reynolds88860ce2017-06-01 16:55:49 -04003421 @GuardedBy("mNotificationLock")
Julia Reynoldsa78cdff2017-04-26 10:19:25 -04003422 void snoozeNotificationLocked(NotificationRecord r) {
3423 MetricsLogger.action(r.getLogMaker()
3424 .setCategory(MetricsEvent.NOTIFICATION_SNOOZED)
3425 .setType(MetricsEvent.TYPE_CLOSE)
3426 .addTaggedData(MetricsEvent.NOTIFICATION_SNOOZED_CRITERIA,
3427 mSnoozeCriterionId == null ? 0 : 1));
Julia Reynolds0839c022017-06-15 15:24:01 -04003428 boolean wasPosted = removeFromNotificationListsLocked(r);
3429 cancelNotificationLocked(r, false, REASON_SNOOZED, wasPosted);
Julia Reynoldsa78cdff2017-04-26 10:19:25 -04003430 updateLightsLocked();
3431 if (mSnoozeCriterionId != null) {
3432 mNotificationAssistants.notifyAssistantSnoozedLocked(r.sbn, mSnoozeCriterionId);
3433 mSnoozeHelper.snooze(r);
3434 } else {
3435 mSnoozeHelper.snooze(r, mDuration);
3436 }
3437 savePolicyFile();
3438 }
3439 }
3440
Julia Reynoldsbaff4002016-12-15 11:34:26 -05003441 protected class EnqueueNotificationRunnable implements Runnable {
Chris Wren47633422016-01-22 09:56:59 -05003442 private final NotificationRecord r;
3443 private final int userId;
3444
3445 EnqueueNotificationRunnable(int userId, NotificationRecord r) {
3446 this.userId = userId;
3447 this.r = r;
3448 };
3449
3450 @Override
3451 public void run() {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05003452 synchronized (mNotificationLock) {
Julia Reynolds573c6532017-01-24 17:44:38 -05003453 mEnqueuedNotifications.add(r);
Julia Reynolds2a128742016-11-28 14:29:25 -05003454 scheduleTimeoutLocked(r);
Julia Reynolds573c6532017-01-24 17:44:38 -05003455
Julia Reynolds4b82f6d2017-01-04 10:47:41 -05003456 final StatusBarNotification n = r.sbn;
3457 if (DBG) Slog.d(TAG, "EnqueueNotificationRunnable.run for: " + n.getKey());
3458 NotificationRecord old = mNotificationsByKey.get(n.getKey());
3459 if (old != null) {
3460 // Retain ranking information from previous record
3461 r.copyRankingInformation(old);
3462 }
3463
3464 final int callingUid = n.getUid();
3465 final int callingPid = n.getInitialPid();
3466 final Notification notification = n.getNotification();
3467 final String pkg = n.getPackageName();
3468 final int id = n.getId();
3469 final String tag = n.getTag();
3470
3471 // Handle grouped notifications and bail out early if we
3472 // can to avoid extracting signals.
3473 handleGroupedNotificationLocked(r, old, callingUid, callingPid);
3474
Julia Reynoldsa78cdff2017-04-26 10:19:25 -04003475 // if this is a group child, unsnooze parent summary
3476 if (n.isGroup() && notification.isGroupChild()) {
3477 mSnoozeHelper.repostGroupSummary(pkg, r.getUserId(), n.getGroupKey());
3478 }
3479
Julia Reynolds4b82f6d2017-01-04 10:47:41 -05003480 // This conditional is a dirty hack to limit the logging done on
3481 // behalf of the download manager without affecting other apps.
3482 if (!pkg.equals("com.android.providers.downloads")
3483 || Log.isLoggable("DownloadManager", Log.VERBOSE)) {
3484 int enqueueStatus = EVENTLOG_ENQUEUE_STATUS_NEW;
Chris Wren6676dab2016-12-21 18:26:27 -05003485 if (old != null) {
Julia Reynolds4b82f6d2017-01-04 10:47:41 -05003486 enqueueStatus = EVENTLOG_ENQUEUE_STATUS_UPDATE;
Chris Wren6676dab2016-12-21 18:26:27 -05003487 }
Julia Reynolds4b82f6d2017-01-04 10:47:41 -05003488 EventLogTags.writeNotificationEnqueue(callingUid, callingPid,
3489 pkg, id, tag, userId, notification.toString(),
3490 enqueueStatus);
3491 }
Chris Wren6676dab2016-12-21 18:26:27 -05003492
Julia Reynolds4b82f6d2017-01-04 10:47:41 -05003493 mRankingHelper.extractSignals(r);
Chris Wren6676dab2016-12-21 18:26:27 -05003494
Julia Reynolds4b82f6d2017-01-04 10:47:41 -05003495 // tell the assistant service about the notification
3496 if (mNotificationAssistants.isEnabled()) {
3497 mNotificationAssistants.onNotificationEnqueued(r);
Julia Reynoldsd94054f2017-02-01 11:11:06 -05003498 mHandler.postDelayed(new PostNotificationRunnable(r.getKey()),
Julia Reynolds4b82f6d2017-01-04 10:47:41 -05003499 DELAY_FOR_ASSISTANT_TIME);
3500 } else {
Julia Reynoldsd94054f2017-02-01 11:11:06 -05003501 mHandler.post(new PostNotificationRunnable(r.getKey()));
Julia Reynolds4b82f6d2017-01-04 10:47:41 -05003502 }
3503 }
3504 }
Julia Reynolds4b82f6d2017-01-04 10:47:41 -05003505 }
3506
3507 protected class PostNotificationRunnable implements Runnable {
3508 private final String key;
Julia Reynolds4b82f6d2017-01-04 10:47:41 -05003509
Julia Reynoldsd94054f2017-02-01 11:11:06 -05003510 PostNotificationRunnable(String key) {
Julia Reynolds4b82f6d2017-01-04 10:47:41 -05003511 this.key = key;
3512 }
3513
3514 @Override
3515 public void run() {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05003516 synchronized (mNotificationLock) {
3517 try {
3518 NotificationRecord r = null;
Julia Reynolds4b82f6d2017-01-04 10:47:41 -05003519 int N = mEnqueuedNotifications.size();
3520 for (int i = 0; i < N; i++) {
3521 final NotificationRecord enqueued = mEnqueuedNotifications.get(i);
3522 if (Objects.equals(key, enqueued.getKey())) {
3523 r = enqueued;
3524 break;
Chris Wren6676dab2016-12-21 18:26:27 -05003525 }
Chris Wren6676dab2016-12-21 18:26:27 -05003526 }
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05003527 if (r == null) {
3528 Slog.i(TAG, "Cannot find enqueued record for key: " + key);
3529 return;
3530 }
Julia Reynolds4b82f6d2017-01-04 10:47:41 -05003531 NotificationRecord old = mNotificationsByKey.get(key);
3532 final StatusBarNotification n = r.sbn;
3533 final Notification notification = n.getNotification();
Chris Wren6676dab2016-12-21 18:26:27 -05003534 int index = indexOfNotificationLocked(n.getKey());
3535 if (index < 0) {
3536 mNotificationList.add(r);
3537 mUsageStats.registerPostedByApp(r);
3538 } else {
3539 old = mNotificationList.get(index);
3540 mNotificationList.set(index, r);
3541 mUsageStats.registerUpdatedByApp(r, old);
3542 // Make sure we don't lose the foreground service state.
3543 notification.flags |=
3544 old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;
3545 r.isUpdate = true;
3546 }
3547
3548 mNotificationsByKey.put(n.getKey(), r);
3549
3550 // Ensure if this is a foreground service that the proper additional
3551 // flags are set.
3552 if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
3553 notification.flags |= Notification.FLAG_ONGOING_EVENT
3554 | Notification.FLAG_NO_CLEAR;
3555 }
3556
3557 applyZenModeLocked(r);
3558 mRankingHelper.sort(mNotificationList);
3559
3560 if (notification.getSmallIcon() != null) {
3561 StatusBarNotification oldSbn = (old != null) ? old.sbn : null;
3562 mListeners.notifyPostedLocked(n, oldSbn);
Julia Reynolds8f488d32016-10-14 10:59:01 -04003563 mHandler.post(new Runnable() {
3564 @Override
3565 public void run() {
Chris Wren6676dab2016-12-21 18:26:27 -05003566 mGroupHelper.onNotificationPosted(n);
Julia Reynolds8f488d32016-10-14 10:59:01 -04003567 }
3568 });
Chris Wren6676dab2016-12-21 18:26:27 -05003569 } else {
3570 Slog.e(TAG, "Not posting notification without small icon: " + notification);
3571 if (old != null && !old.isCanceled) {
3572 mListeners.notifyRemovedLocked(n,
Julia Reynoldsf619bc52017-03-17 08:32:23 -04003573 NotificationListenerService.REASON_ERROR);
Chris Wren6676dab2016-12-21 18:26:27 -05003574 mHandler.post(new Runnable() {
3575 @Override
3576 public void run() {
3577 mGroupHelper.onNotificationRemoved(n);
3578 }
3579 });
3580 }
3581 // ATTENTION: in a future release we will bail out here
3582 // so that we do not play sounds, show lights, etc. for invalid
3583 // notifications
3584 Slog.e(TAG, "WARNING: In a future release this will crash the app: "
3585 + n.getPackageName());
Chris Wren47633422016-01-22 09:56:59 -05003586 }
Chris Wren47633422016-01-22 09:56:59 -05003587
Chris Wren6676dab2016-12-21 18:26:27 -05003588 buzzBeepBlinkLocked(r);
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05003589 } finally {
Julia Reynolds4b82f6d2017-01-04 10:47:41 -05003590 int N = mEnqueuedNotifications.size();
3591 for (int i = 0; i < N; i++) {
3592 final NotificationRecord enqueued = mEnqueuedNotifications.get(i);
3593 if (Objects.equals(key, enqueued.getKey())) {
3594 mEnqueuedNotifications.remove(i);
3595 break;
3596 }
3597 }
Chris Wren6676dab2016-12-21 18:26:27 -05003598 }
Chris Wren47633422016-01-22 09:56:59 -05003599 }
3600 }
3601 }
3602
Christoph Studer265c1052014-07-23 17:14:33 +02003603 /**
3604 * Ensures that grouped notification receive their special treatment.
3605 *
3606 * <p>Cancels group children if the new notification causes a group to lose
3607 * its summary.</p>
3608 *
3609 * <p>Updates mSummaryByGroupKey.</p>
3610 */
Julia Reynolds88860ce2017-06-01 16:55:49 -04003611 @GuardedBy("mNotificationLock")
Christoph Studer265c1052014-07-23 17:14:33 +02003612 private void handleGroupedNotificationLocked(NotificationRecord r, NotificationRecord old,
3613 int callingUid, int callingPid) {
3614 StatusBarNotification sbn = r.sbn;
3615 Notification n = sbn.getNotification();
Selim Cinek5b03ce92016-05-18 15:16:58 -07003616 if (n.isGroupSummary() && !sbn.isAppGroup()) {
3617 // notifications without a group shouldn't be a summary, otherwise autobundling can
3618 // lead to bugs
3619 n.flags &= ~Notification.FLAG_GROUP_SUMMARY;
3620 }
3621
Christoph Studer265c1052014-07-23 17:14:33 +02003622 String group = sbn.getGroupKey();
3623 boolean isSummary = n.isGroupSummary();
3624
3625 Notification oldN = old != null ? old.sbn.getNotification() : null;
3626 String oldGroup = old != null ? old.sbn.getGroupKey() : null;
3627 boolean oldIsSummary = old != null && oldN.isGroupSummary();
3628
3629 if (oldIsSummary) {
3630 NotificationRecord removedSummary = mSummaryByGroupKey.remove(oldGroup);
3631 if (removedSummary != old) {
3632 String removedKey =
3633 removedSummary != null ? removedSummary.getKey() : "<null>";
3634 Slog.w(TAG, "Removed summary didn't match old notification: old=" + old.getKey() +
3635 ", removed=" + removedKey);
3636 }
3637 }
3638 if (isSummary) {
3639 mSummaryByGroupKey.put(group, r);
3640 }
3641
3642 // Clear out group children of the old notification if the update
3643 // causes the group summary to go away. This happens when the old
3644 // notification was a summary and the new one isn't, or when the old
3645 // notification was a summary and its group key changed.
3646 if (oldIsSummary && (!isSummary || !oldGroup.equals(group))) {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05003647 cancelGroupChildrenLocked(old, callingUid, callingPid, null, false /* sendDelete */);
Christoph Studer265c1052014-07-23 17:14:33 +02003648 }
3649 }
3650
Chris Wren93bb8b82016-03-29 14:35:05 -04003651 @VisibleForTesting
Julia Reynolds88860ce2017-06-01 16:55:49 -04003652 @GuardedBy("mNotificationLock")
Julia Reynolds2a128742016-11-28 14:29:25 -05003653 void scheduleTimeoutLocked(NotificationRecord record) {
Julia Reynoldsbad42972017-04-25 13:52:49 -04003654 if (record.getNotification().getTimeoutAfter() > 0) {
Julia Reynolds2a128742016-11-28 14:29:25 -05003655 final PendingIntent pi = PendingIntent.getBroadcast(getContext(),
3656 REQUEST_CODE_TIMEOUT,
3657 new Intent(ACTION_NOTIFICATION_TIMEOUT)
3658 .setData(new Uri.Builder().scheme(SCHEME_TIMEOUT)
3659 .appendPath(record.getKey()).build())
3660 .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
3661 .putExtra(EXTRA_KEY, record.getKey()),
3662 PendingIntent.FLAG_UPDATE_CURRENT);
Julia Reynolds50989772017-02-23 14:32:16 -05003663 mAlarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP,
Julia Reynoldsbad42972017-04-25 13:52:49 -04003664 SystemClock.elapsedRealtime() + record.getNotification().getTimeoutAfter(), pi);
Julia Reynolds2a128742016-11-28 14:29:25 -05003665 }
3666 }
3667
3668 @VisibleForTesting
Julia Reynolds88860ce2017-06-01 16:55:49 -04003669 @GuardedBy("mNotificationLock")
Chris Wren93bb8b82016-03-29 14:35:05 -04003670 void buzzBeepBlinkLocked(NotificationRecord record) {
Chris Wren82ba59d2015-06-05 11:23:44 -04003671 boolean buzz = false;
3672 boolean beep = false;
3673 boolean blink = false;
3674
Chris Wrena3446562014-06-03 18:11:47 -04003675 final Notification notification = record.sbn.getNotification();
Chris Wren93bb8b82016-03-29 14:35:05 -04003676 final String key = record.getKey();
Chris Wrena3446562014-06-03 18:11:47 -04003677
3678 // Should this notification make noise, vibe, or use the LED?
Julia Reynolds85769912016-10-25 09:08:57 -04003679 final boolean aboveThreshold =
3680 record.getImportance() >= NotificationManager.IMPORTANCE_DEFAULT;
Chris Wrence00a232014-11-21 16:25:19 -05003681 final boolean canInterrupt = aboveThreshold && !record.isIntercepted();
Julia Reynoldsbb1d7d22017-04-18 10:30:51 -04003682 if (DBG)
Chris Wrena3446562014-06-03 18:11:47 -04003683 Slog.v(TAG,
3684 "pkg=" + record.sbn.getPackageName() + " canInterrupt=" + canInterrupt +
3685 " intercept=" + record.isIntercepted()
3686 );
3687
Chris Wrena3446562014-06-03 18:11:47 -04003688 // If we're not supposed to beep, vibrate, etc. then don't.
John Spurlock32fe4c62014-10-02 12:16:02 -04003689 final String disableEffects = disableNotificationEffects(record);
3690 if (disableEffects != null) {
3691 ZenLog.traceDisableEffects(record, disableEffects);
3692 }
Chris Wren93bb8b82016-03-29 14:35:05 -04003693
3694 // Remember if this notification already owns the notification channels.
3695 boolean wasBeep = key != null && key.equals(mSoundNotificationKey);
3696 boolean wasBuzz = key != null && key.equals(mVibrateNotificationKey);
Chris Wren93bb8b82016-03-29 14:35:05 -04003697 // These are set inside the conditional if the notification is allowed to make noise.
3698 boolean hasValidVibrate = false;
3699 boolean hasValidSound = false;
Chris Wrena3446562014-06-03 18:11:47 -04003700
Julia Reynolds7c96b582017-05-25 12:35:36 -04003701 if (isNotificationForCurrentUser(record)) {
3702 // If the notification will appear in the status bar, it should send an accessibility
3703 // event
3704 if (!record.isUpdate && record.getImportance() > IMPORTANCE_MIN) {
Chris Wren93bb8b82016-03-29 14:35:05 -04003705 sendAccessibilityEvent(notification, record.sbn.getPackageName());
Julia Reynolds7c96b582017-05-25 12:35:36 -04003706 }
Marta Białka39c992f2011-03-10 10:27:24 +01003707
Julia Reynolds7c96b582017-05-25 12:35:36 -04003708 if (disableEffects == null
3709 && canInterrupt
3710 && mSystemReady
3711 && mAudioManager != null) {
3712 if (DBG) Slog.v(TAG, "Interrupting!");
3713 Uri soundUri = record.getSound();
3714 hasValidSound = soundUri != null && !Uri.EMPTY.equals(soundUri);
3715 long[] vibration = record.getVibration();
3716 // Demote sound to vibration if vibration missing & phone in vibration mode.
3717 if (vibration == null
3718 && hasValidSound
3719 && (mAudioManager.getRingerModeInternal()
3720 == AudioManager.RINGER_MODE_VIBRATE)) {
3721 vibration = mFallbackVibrationPattern;
Chris Wren93bb8b82016-03-29 14:35:05 -04003722 }
Julia Reynolds7c96b582017-05-25 12:35:36 -04003723 hasValidVibrate = vibration != null;
Marta Białka39c992f2011-03-10 10:27:24 +01003724
Julia Reynolds7c96b582017-05-25 12:35:36 -04003725 if (!shouldMuteNotificationLocked(record)) {
3726 if (hasValidSound) {
3727 mSoundNotificationKey = key;
3728 if (mInCall) {
3729 playInCallNotification();
3730 beep = true;
3731 } else {
3732 beep = playSound(record, soundUri);
3733 }
3734 }
Chris Wren93bb8b82016-03-29 14:35:05 -04003735
Julia Reynolds7c96b582017-05-25 12:35:36 -04003736 final boolean ringerModeSilent =
3737 mAudioManager.getRingerModeInternal()
3738 == AudioManager.RINGER_MODE_SILENT;
3739 if (!mInCall && hasValidVibrate && !ringerModeSilent) {
3740 mVibrateNotificationKey = key;
3741
Jean-Michel Triviea0eb5f2017-05-25 18:32:40 -07003742 buzz = playVibration(record, vibration, hasValidSound);
Julia Reynolds7c96b582017-05-25 12:35:36 -04003743 }
Chris Wrena3446562014-06-03 18:11:47 -04003744 }
3745 }
Chris Wren93bb8b82016-03-29 14:35:05 -04003746 }
3747 // If a notification is updated to remove the actively playing sound or vibrate,
3748 // cancel that feedback now
3749 if (wasBeep && !hasValidSound) {
3750 clearSoundLocked();
3751 }
3752 if (wasBuzz && !hasValidVibrate) {
3753 clearVibrateLocked();
Chris Wrena3446562014-06-03 18:11:47 -04003754 }
3755
3756 // light
3757 // release the light
Chris Wren93bb8b82016-03-29 14:35:05 -04003758 boolean wasShowLights = mLights.remove(key);
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003759 if (record.getLight() != null && aboveThreshold
Julia Reynoldsf612869ae2015-11-05 16:48:55 -05003760 && ((record.getSuppressedVisualEffects()
Julia Reynoldsd5607292016-02-05 15:25:58 -05003761 & NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_OFF) == 0)) {
Chris Wren93bb8b82016-03-29 14:35:05 -04003762 mLights.add(key);
Chris Wrena3446562014-06-03 18:11:47 -04003763 updateLightsLocked();
Chris Wren5116a822014-06-04 15:59:50 -04003764 if (mUseAttentionLight) {
3765 mAttentionLight.pulse();
3766 }
Chris Wren82ba59d2015-06-05 11:23:44 -04003767 blink = true;
Chris Wrena3446562014-06-03 18:11:47 -04003768 } else if (wasShowLights) {
3769 updateLightsLocked();
3770 }
Chris Wren82ba59d2015-06-05 11:23:44 -04003771 if (buzz || beep || blink) {
Julia Reynolds445cfa82017-05-08 15:41:45 -04003772 MetricsLogger.action(record.getLogMaker()
3773 .setCategory(MetricsEvent.NOTIFICATION_ALERT)
3774 .setType(MetricsEvent.TYPE_OPEN)
3775 .setSubtype((buzz ? 1 : 0) | (beep ? 2 : 0) | (blink ? 4 : 0)));
3776 EventLogTags.writeNotificationAlert(key, buzz ? 1 : 0, beep ? 1 : 0, blink ? 1 : 0);
John Spurlockcad57682014-07-26 17:09:56 -04003777 }
Chris Wrena3446562014-06-03 18:11:47 -04003778 }
3779
Julia Reynolds88860ce2017-06-01 16:55:49 -04003780 @GuardedBy("mNotificationLock")
Julia Reynoldsa79c3712017-04-21 10:29:57 -04003781 boolean shouldMuteNotificationLocked(final NotificationRecord record) {
3782 final Notification notification = record.getNotification();
3783 if(record.isUpdate
3784 && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0) {
3785 return true;
3786 }
3787 if (record.sbn.isGroup()) {
Julia Reynolds30203152017-05-26 13:36:31 -04003788 return notification.suppressAlertingDueToGrouping();
Julia Reynoldsa79c3712017-04-21 10:29:57 -04003789 }
3790 return false;
3791 }
3792
Julia Reynolds0c299d42016-11-15 14:37:04 -05003793 private boolean playSound(final NotificationRecord record, Uri soundUri) {
3794 boolean looping = (record.getNotification().flags & Notification.FLAG_INSISTENT) != 0;
3795 // do not play notifications if there is a user of exclusive audio focus
Julia Reynolds2143e5d2017-01-17 16:28:48 -05003796 // or the device is in vibrate mode
3797 if (!mAudioManager.isAudioFocusExclusive() && mAudioManager.getRingerModeInternal()
3798 != AudioManager.RINGER_MODE_VIBRATE) {
Julia Reynolds0c299d42016-11-15 14:37:04 -05003799 final long identity = Binder.clearCallingIdentity();
3800 try {
3801 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
3802 if (player != null) {
3803 if (DBG) Slog.v(TAG, "Playing sound " + soundUri
3804 + " with attributes " + record.getAudioAttributes());
3805 player.playAsync(soundUri, record.sbn.getUser(), looping,
3806 record.getAudioAttributes());
3807 return true;
3808 }
3809 } catch (RemoteException e) {
3810 } finally {
3811 Binder.restoreCallingIdentity(identity);
3812 }
3813 }
3814 return false;
3815 }
3816
Jean-Michel Triviea0eb5f2017-05-25 18:32:40 -07003817 private boolean playVibration(final NotificationRecord record, long[] vibration,
3818 boolean delayVibForSound) {
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04003819 // Escalate privileges so we can use the vibrator even if the
3820 // notifying app does not have the VIBRATE permission.
3821 long identity = Binder.clearCallingIdentity();
3822 try {
Jean-Michel Triviea0eb5f2017-05-25 18:32:40 -07003823 final VibrationEffect effect;
3824 try {
3825 final boolean insistent =
3826 (record.getNotification().flags & Notification.FLAG_INSISTENT) != 0;
3827 effect = VibrationEffect.createWaveform(
3828 vibration, insistent ? 0 : -1 /*repeatIndex*/);
3829 } catch (IllegalArgumentException e) {
3830 Slog.e(TAG, "Error creating vibration waveform with pattern: " +
3831 Arrays.toString(vibration));
3832 return false;
3833 }
3834 if (delayVibForSound) {
3835 new Thread(() -> {
3836 // delay the vibration by the same amount as the notification sound
3837 final int waitMs = mAudioManager.getFocusRampTimeMs(
3838 AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK,
3839 record.getAudioAttributes());
3840 if (DBG) Slog.v(TAG, "Delaying vibration by " + waitMs + "ms");
3841 try {
3842 Thread.sleep(waitMs);
3843 } catch (InterruptedException e) { }
3844 mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
3845 effect, record.getAudioAttributes());
3846 }).start();
3847 } else {
3848 mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
3849 effect, record.getAudioAttributes());
3850 }
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04003851 return true;
3852 } finally{
3853 Binder.restoreCallingIdentity(identity);
3854 }
3855 }
3856
Julia Reynolds7c96b582017-05-25 12:35:36 -04003857 private boolean isNotificationForCurrentUser(NotificationRecord record) {
3858 final int currentUser;
3859 final long token = Binder.clearCallingIdentity();
3860 try {
3861 currentUser = ActivityManager.getCurrentUser();
3862 } finally {
3863 Binder.restoreCallingIdentity(token);
3864 }
3865 return (record.getUserId() == UserHandle.USER_ALL ||
3866 record.getUserId() == currentUser ||
3867 mUserProfiles.isCurrentProfile(record.getUserId()));
3868 }
3869
Marta Białka39c992f2011-03-10 10:27:24 +01003870 private void playInCallNotification() {
3871 new Thread() {
3872 @Override
3873 public void run() {
3874 // If toneGenerator creation fails, just continue the call
3875 // without playing the notification sound.
3876 try {
3877 synchronized (mInCallToneGeneratorLock) {
3878 if (mInCallToneGenerator != null) {
3879 // limit this tone to 1 second; BEEP2 should in fact be much shorter
3880 mInCallToneGenerator.startTone(ToneGenerator.TONE_PROP_BEEP2, 1000);
3881 }
3882 }
3883 } catch (RuntimeException e) {
3884 Log.w(TAG, "Exception from ToneGenerator: " + e);
3885 }
3886 }
3887 }.start();
3888 }
3889
Julia Reynolds88860ce2017-06-01 16:55:49 -04003890 @GuardedBy("mToastQueue")
Adam Lesinski182f73f2013-12-05 16:48:06 -08003891 void showNextToastLocked() {
3892 ToastRecord record = mToastQueue.get(0);
3893 while (record != null) {
3894 if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback);
3895 try {
Svetoslav Ganovaa076532016-08-01 19:16:43 -07003896 record.callback.show(record.token);
Adam Lesinski182f73f2013-12-05 16:48:06 -08003897 scheduleTimeoutLocked(record);
3898 return;
3899 } catch (RemoteException e) {
3900 Slog.w(TAG, "Object died trying to show notification " + record.callback
3901 + " in package " + record.pkg);
3902 // remove it from the list and let the process die
3903 int index = mToastQueue.indexOf(record);
3904 if (index >= 0) {
3905 mToastQueue.remove(index);
3906 }
Svetoslav Ganovaa076532016-08-01 19:16:43 -07003907 keepProcessAliveIfNeededLocked(record.pid);
Adam Lesinski182f73f2013-12-05 16:48:06 -08003908 if (mToastQueue.size() > 0) {
3909 record = mToastQueue.get(0);
3910 } else {
3911 record = null;
3912 }
3913 }
3914 }
3915 }
3916
Julia Reynolds88860ce2017-06-01 16:55:49 -04003917 @GuardedBy("mToastQueue")
Adam Lesinski182f73f2013-12-05 16:48:06 -08003918 void cancelToastLocked(int index) {
3919 ToastRecord record = mToastQueue.get(index);
3920 try {
3921 record.callback.hide();
3922 } catch (RemoteException e) {
3923 Slog.w(TAG, "Object died trying to hide notification " + record.callback
3924 + " in package " + record.pkg);
3925 // don't worry about this, we're about to remove it from
3926 // the list anyway
3927 }
Svetoslav Ganovaa076532016-08-01 19:16:43 -07003928
3929 ToastRecord lastToast = mToastQueue.remove(index);
Wale Ogunwaleac2561e2016-11-01 15:43:46 -07003930 mWindowManagerInternal.removeWindowToken(lastToast.token, true, DEFAULT_DISPLAY);
Svetoslav Ganovaa076532016-08-01 19:16:43 -07003931
3932 keepProcessAliveIfNeededLocked(record.pid);
Adam Lesinski182f73f2013-12-05 16:48:06 -08003933 if (mToastQueue.size() > 0) {
3934 // Show the next one. If the callback fails, this will remove
3935 // it from the list, so don't assume that the list hasn't changed
3936 // after this point.
3937 showNextToastLocked();
3938 }
3939 }
3940
Julia Reynolds88860ce2017-06-01 16:55:49 -04003941 @GuardedBy("mToastQueue")
Adam Lesinski182f73f2013-12-05 16:48:06 -08003942 private void scheduleTimeoutLocked(ToastRecord r)
3943 {
3944 mHandler.removeCallbacksAndMessages(r);
3945 Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r);
3946 long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY;
3947 mHandler.sendMessageDelayed(m, delay);
3948 }
3949
3950 private void handleTimeout(ToastRecord record)
3951 {
3952 if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback);
3953 synchronized (mToastQueue) {
3954 int index = indexOfToastLocked(record.pkg, record.callback);
3955 if (index >= 0) {
3956 cancelToastLocked(index);
3957 }
3958 }
3959 }
3960
Julia Reynolds88860ce2017-06-01 16:55:49 -04003961 @GuardedBy("mToastQueue")
Adam Lesinski182f73f2013-12-05 16:48:06 -08003962 int indexOfToastLocked(String pkg, ITransientNotification callback)
3963 {
3964 IBinder cbak = callback.asBinder();
3965 ArrayList<ToastRecord> list = mToastQueue;
3966 int len = list.size();
3967 for (int i=0; i<len; i++) {
3968 ToastRecord r = list.get(i);
3969 if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) {
3970 return i;
3971 }
3972 }
3973 return -1;
3974 }
3975
Julia Reynolds88860ce2017-06-01 16:55:49 -04003976 @GuardedBy("mToastQueue")
Svetoslav Ganovaa076532016-08-01 19:16:43 -07003977 void keepProcessAliveIfNeededLocked(int pid)
Adam Lesinski182f73f2013-12-05 16:48:06 -08003978 {
3979 int toastCount = 0; // toasts from this pid
3980 ArrayList<ToastRecord> list = mToastQueue;
3981 int N = list.size();
3982 for (int i=0; i<N; i++) {
3983 ToastRecord r = list.get(i);
3984 if (r.pid == pid) {
3985 toastCount++;
3986 }
3987 }
3988 try {
Dianne Hackbornf965f402017-05-04 23:27:23 -07003989 mAm.setProcessImportant(mForegroundToken, pid, toastCount > 0, "toast");
Adam Lesinski182f73f2013-12-05 16:48:06 -08003990 } catch (RemoteException e) {
3991 // Shouldn't happen.
3992 }
3993 }
3994
Chris Wrenf9536642014-04-17 10:01:54 -04003995 private void handleRankingReconsideration(Message message) {
Chris Wren470c1ac2014-05-21 15:28:10 -04003996 if (!(message.obj instanceof RankingReconsideration)) return;
3997 RankingReconsideration recon = (RankingReconsideration) message.obj;
3998 recon.run();
Chris Wren333a61c2014-05-28 16:40:57 -04003999 boolean changed;
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004000 synchronized (mNotificationLock) {
Chris Wren470c1ac2014-05-21 15:28:10 -04004001 final NotificationRecord record = mNotificationsByKey.get(recon.getKey());
4002 if (record == null) {
4003 return;
Chris Wrenf9536642014-04-17 10:01:54 -04004004 }
Chris Wren333a61c2014-05-28 16:40:57 -04004005 int indexBefore = findNotificationRecordIndexLocked(record);
4006 boolean interceptBefore = record.isIntercepted();
Julia Reynoldsaf4dc282017-06-23 16:13:20 -04004007 float contactAffinityBefore = record.getContactAffinity();
Chris Wren3ad4e3a2014-09-02 17:23:51 -04004008 int visibilityBefore = record.getPackageVisibilityOverride();
Chris Wren470c1ac2014-05-21 15:28:10 -04004009 recon.applyChangesLocked(record);
Chris Wren333a61c2014-05-28 16:40:57 -04004010 applyZenModeLocked(record);
Chris Wren54bbef42014-07-09 18:37:56 -04004011 mRankingHelper.sort(mNotificationList);
Chris Wren333a61c2014-05-28 16:40:57 -04004012 int indexAfter = findNotificationRecordIndexLocked(record);
4013 boolean interceptAfter = record.isIntercepted();
Julia Reynoldsaf4dc282017-06-23 16:13:20 -04004014 float contactAffinityAfter = record.getContactAffinity();
Chris Wren3ad4e3a2014-09-02 17:23:51 -04004015 int visibilityAfter = record.getPackageVisibilityOverride();
4016 changed = indexBefore != indexAfter || interceptBefore != interceptAfter
4017 || visibilityBefore != visibilityAfter;
Julia Reynoldsaf4dc282017-06-23 16:13:20 -04004018 if (interceptBefore && !interceptAfter
4019 && Float.compare(contactAffinityBefore, contactAffinityAfter) != 0) {
Chris Wrena3446562014-06-03 18:11:47 -04004020 buzzBeepBlinkLocked(record);
4021 }
Chris Wrenf9536642014-04-17 10:01:54 -04004022 }
Chris Wren333a61c2014-05-28 16:40:57 -04004023 if (changed) {
Chris Wren470c1ac2014-05-21 15:28:10 -04004024 scheduleSendRankingUpdate();
4025 }
4026 }
4027
Julia Reynolds22f02b32016-12-01 15:05:13 -05004028 private void handleRankingSort(Message msg) {
4029 if (!(msg.obj instanceof Boolean)) return;
Chris Wren89aa2262017-05-05 18:05:56 -04004030 if (mRankingHelper == null) return;
Julia Reynolds22f02b32016-12-01 15:05:13 -05004031 boolean forceUpdate = ((Boolean) msg.obj == null) ? false : (boolean) msg.obj;
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004032 synchronized (mNotificationLock) {
Chris Wren54bbef42014-07-09 18:37:56 -04004033 final int N = mNotificationList.size();
Julia Reynolds924eed12017-01-19 09:52:07 -05004034 // Any field that can change via one of the extractors or by the assistant
4035 // needs to be added here.
Chris Wren54bbef42014-07-09 18:37:56 -04004036 ArrayList<String> orderBefore = new ArrayList<String>(N);
Julia Reynoldse46bb372016-03-17 11:05:58 -04004037 ArrayList<String> groupOverrideBefore = new ArrayList<>(N);
Chris Wren3ad4e3a2014-09-02 17:23:51 -04004038 int[] visibilities = new int[N];
Julia Reynolds924eed12017-01-19 09:52:07 -05004039 boolean[] showBadges = new boolean[N];
Chris Wren54bbef42014-07-09 18:37:56 -04004040 for (int i = 0; i < N; i++) {
4041 final NotificationRecord r = mNotificationList.get(i);
4042 orderBefore.add(r.getKey());
Julia Reynoldse46bb372016-03-17 11:05:58 -04004043 groupOverrideBefore.add(r.sbn.getGroupKey());
Chris Wren3ad4e3a2014-09-02 17:23:51 -04004044 visibilities[i] = r.getPackageVisibilityOverride();
Julia Reynolds924eed12017-01-19 09:52:07 -05004045 showBadges[i] = r.canShowBadge();
Chris Wren54bbef42014-07-09 18:37:56 -04004046 mRankingHelper.extractSignals(r);
4047 }
Chris Wren19a02b02015-12-22 10:34:22 -05004048 mRankingHelper.sort(mNotificationList);
Chris Wren54bbef42014-07-09 18:37:56 -04004049 for (int i = 0; i < N; i++) {
Chris Wren3ad4e3a2014-09-02 17:23:51 -04004050 final NotificationRecord r = mNotificationList.get(i);
Julia Reynolds22f02b32016-12-01 15:05:13 -05004051 if (forceUpdate
4052 || !orderBefore.get(i).equals(r.getKey())
Julia Reynolds69766692016-02-01 15:35:08 -05004053 || visibilities[i] != r.getPackageVisibilityOverride()
Julia Reynolds924eed12017-01-19 09:52:07 -05004054 || !groupOverrideBefore.get(i).equals(r.sbn.getGroupKey())
4055 || showBadges[i] != r.canShowBadge()) {
Chris Wren54bbef42014-07-09 18:37:56 -04004056 scheduleSendRankingUpdate();
4057 return;
4058 }
4059 }
4060 }
4061 }
4062
Julia Reynolds88860ce2017-06-01 16:55:49 -04004063 @GuardedBy("mNotificationLock")
Julia Reynoldsc6b371b2016-06-14 08:31:03 -04004064 private void recordCallerLocked(NotificationRecord record) {
4065 if (mZenModeHelper.isCall(record)) {
4066 mZenModeHelper.recordCaller(record);
4067 }
4068 }
4069
Christoph Studerd5092bc2014-07-03 17:47:58 +02004070 // let zen mode evaluate this record
Julia Reynolds88860ce2017-06-01 16:55:49 -04004071 @GuardedBy("mNotificationLock")
Chris Wren333a61c2014-05-28 16:40:57 -04004072 private void applyZenModeLocked(NotificationRecord record) {
Christoph Studerd5092bc2014-07-03 17:47:58 +02004073 record.setIntercepted(mZenModeHelper.shouldIntercept(record));
Julia Reynoldsf612869ae2015-11-05 16:48:55 -05004074 if (record.isIntercepted()) {
Julia Reynoldsd5607292016-02-05 15:25:58 -05004075 int suppressed = (mZenModeHelper.shouldSuppressWhenScreenOff()
4076 ? SUPPRESSED_EFFECT_SCREEN_OFF : 0)
4077 | (mZenModeHelper.shouldSuppressWhenScreenOn()
4078 ? SUPPRESSED_EFFECT_SCREEN_ON : 0);
Julia Reynoldsf612869ae2015-11-05 16:48:55 -05004079 record.setSuppressedVisualEffects(suppressed);
Julia Reynolds445cfa82017-05-08 15:41:45 -04004080 } else {
4081 record.setSuppressedVisualEffects(0);
Julia Reynoldsf612869ae2015-11-05 16:48:55 -05004082 }
Chris Wren333a61c2014-05-28 16:40:57 -04004083 }
4084
Julia Reynolds88860ce2017-06-01 16:55:49 -04004085 @GuardedBy("mNotificationLock")
Chris Wren470c1ac2014-05-21 15:28:10 -04004086 private int findNotificationRecordIndexLocked(NotificationRecord target) {
Chris Wren54bbef42014-07-09 18:37:56 -04004087 return mRankingHelper.indexOf(mNotificationList, target);
Chris Wrenf9536642014-04-17 10:01:54 -04004088 }
4089
4090 private void scheduleSendRankingUpdate() {
Chris Wren52020492016-04-06 11:12:02 -04004091 if (!mHandler.hasMessages(MESSAGE_SEND_RANKING_UPDATE)) {
4092 Message m = Message.obtain(mHandler, MESSAGE_SEND_RANKING_UPDATE);
4093 mHandler.sendMessage(m);
4094 }
Chris Wrenf9536642014-04-17 10:01:54 -04004095 }
4096
4097 private void handleSendRankingUpdate() {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004098 synchronized (mNotificationLock) {
Chris Wren333a61c2014-05-28 16:40:57 -04004099 mListeners.notifyRankingUpdateLocked();
Chris Wrenf9536642014-04-17 10:01:54 -04004100 }
4101 }
4102
John Spurlockd8afe3c2014-08-01 14:04:07 -04004103 private void scheduleListenerHintsChanged(int state) {
4104 mHandler.removeMessages(MESSAGE_LISTENER_HINTS_CHANGED);
4105 mHandler.obtainMessage(MESSAGE_LISTENER_HINTS_CHANGED, state, 0).sendToTarget();
John Spurlock1fa865f2014-07-21 14:56:39 -04004106 }
4107
Christoph Studer85a384b2014-08-27 20:16:15 +02004108 private void scheduleInterruptionFilterChanged(int listenerInterruptionFilter) {
4109 mHandler.removeMessages(MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED);
4110 mHandler.obtainMessage(
4111 MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED,
4112 listenerInterruptionFilter,
4113 0).sendToTarget();
4114 }
4115
John Spurlockd8afe3c2014-08-01 14:04:07 -04004116 private void handleListenerHintsChanged(int hints) {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004117 synchronized (mNotificationLock) {
John Spurlockd8afe3c2014-08-01 14:04:07 -04004118 mListeners.notifyListenerHintsChangedLocked(hints);
John Spurlock1fa865f2014-07-21 14:56:39 -04004119 }
4120 }
4121
Christoph Studer85a384b2014-08-27 20:16:15 +02004122 private void handleListenerInterruptionFilterChanged(int interruptionFilter) {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004123 synchronized (mNotificationLock) {
Christoph Studer85a384b2014-08-27 20:16:15 +02004124 mListeners.notifyInterruptionFilterChanged(interruptionFilter);
4125 }
4126 }
4127
Adam Lesinski182f73f2013-12-05 16:48:06 -08004128 private final class WorkerHandler extends Handler
4129 {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004130 public WorkerHandler(Looper looper) {
4131 super(looper);
4132 }
4133
Adam Lesinski182f73f2013-12-05 16:48:06 -08004134 @Override
4135 public void handleMessage(Message msg)
4136 {
4137 switch (msg.what)
4138 {
4139 case MESSAGE_TIMEOUT:
4140 handleTimeout((ToastRecord)msg.obj);
4141 break;
John Spurlock056c5192014-04-20 21:52:01 -04004142 case MESSAGE_SAVE_POLICY_FILE:
4143 handleSavePolicyFile();
4144 break;
Chris Wrenf9536642014-04-17 10:01:54 -04004145 case MESSAGE_SEND_RANKING_UPDATE:
4146 handleSendRankingUpdate();
4147 break;
John Spurlockd8afe3c2014-08-01 14:04:07 -04004148 case MESSAGE_LISTENER_HINTS_CHANGED:
4149 handleListenerHintsChanged(msg.arg1);
John Spurlock1fa865f2014-07-21 14:56:39 -04004150 break;
Christoph Studer85a384b2014-08-27 20:16:15 +02004151 case MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED:
4152 handleListenerInterruptionFilterChanged(msg.arg1);
4153 break;
Chris Wrenf9536642014-04-17 10:01:54 -04004154 }
4155 }
4156
4157 }
4158
Chris Wren51017d02015-12-15 15:34:46 -05004159 private final class RankingHandlerWorker extends Handler implements RankingHandler
Chris Wrenf9536642014-04-17 10:01:54 -04004160 {
Chris Wren51017d02015-12-15 15:34:46 -05004161 public RankingHandlerWorker(Looper looper) {
Chris Wrenf9536642014-04-17 10:01:54 -04004162 super(looper);
4163 }
4164
4165 @Override
4166 public void handleMessage(Message msg) {
4167 switch (msg.what) {
4168 case MESSAGE_RECONSIDER_RANKING:
4169 handleRankingReconsideration(msg);
4170 break;
Chris Wren51017d02015-12-15 15:34:46 -05004171 case MESSAGE_RANKING_SORT:
Julia Reynolds22f02b32016-12-01 15:05:13 -05004172 handleRankingSort(msg);
Chris Wren54bbef42014-07-09 18:37:56 -04004173 break;
Adam Lesinski182f73f2013-12-05 16:48:06 -08004174 }
4175 }
Chris Wren51017d02015-12-15 15:34:46 -05004176
Julia Reynolds22f02b32016-12-01 15:05:13 -05004177 public void requestSort(boolean forceUpdate) {
Chris Wren51017d02015-12-15 15:34:46 -05004178 removeMessages(MESSAGE_RANKING_SORT);
Julia Reynolds22f02b32016-12-01 15:05:13 -05004179 Message msg = Message.obtain();
4180 msg.what = MESSAGE_RANKING_SORT;
4181 msg.obj = forceUpdate;
4182 sendMessage(msg);
Chris Wren51017d02015-12-15 15:34:46 -05004183 }
4184
4185 public void requestReconsideration(RankingReconsideration recon) {
4186 Message m = Message.obtain(this,
4187 NotificationManagerService.MESSAGE_RECONSIDER_RANKING, recon);
4188 long delay = recon.getDelay(TimeUnit.MILLISECONDS);
4189 sendMessageDelayed(m, delay);
4190 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08004191 }
4192
Adam Lesinski182f73f2013-12-05 16:48:06 -08004193 // Notifications
4194 // ============================================================================
4195 static int clamp(int x, int low, int high) {
4196 return (x < low) ? low : ((x > high) ? high : x);
4197 }
4198
4199 void sendAccessibilityEvent(Notification notification, CharSequence packageName) {
4200 AccessibilityManager manager = AccessibilityManager.getInstance(getContext());
svetoslavganov75986cf2009-05-14 22:28:01 -07004201 if (!manager.isEnabled()) {
4202 return;
4203 }
4204
4205 AccessibilityEvent event =
4206 AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
4207 event.setPackageName(packageName);
4208 event.setClassName(Notification.class.getName());
4209 event.setParcelableData(notification);
4210 CharSequence tickerText = notification.tickerText;
4211 if (!TextUtils.isEmpty(tickerText)) {
4212 event.getText().add(tickerText);
4213 }
4214
4215 manager.sendAccessibilityEvent(event);
4216 }
4217
Julia Reynolds0839c022017-06-15 15:24:01 -04004218 /**
4219 * Removes all NotificationsRecords with the same key as the given notification record
4220 * from both lists. Do not call this method while iterating over either list.
4221 */
Julia Reynolds88860ce2017-06-01 16:55:49 -04004222 @GuardedBy("mNotificationLock")
Julia Reynolds0839c022017-06-15 15:24:01 -04004223 private boolean removeFromNotificationListsLocked(NotificationRecord r) {
4224 // Remove from both lists, either list could have a separate Record for what is
4225 // effectively the same notification.
Geoffrey Pitschccc0b972017-02-15 10:52:26 -05004226 boolean wasPosted = false;
4227 NotificationRecord recordInList = null;
Julia Reynolds0839c022017-06-15 15:24:01 -04004228 if ((recordInList = findNotificationByListLocked(mNotificationList, r.getKey()))
4229 != null) {
Geoffrey Pitschccc0b972017-02-15 10:52:26 -05004230 mNotificationList.remove(recordInList);
4231 mNotificationsByKey.remove(recordInList.sbn.getKey());
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004232 wasPosted = true;
Geoffrey Pitschccc0b972017-02-15 10:52:26 -05004233 }
Geoffrey Pitsch27684152017-05-02 11:41:31 -04004234 while ((recordInList = findNotificationByListLocked(mEnqueuedNotifications, r.getKey()))
Geoffrey Pitschccc0b972017-02-15 10:52:26 -05004235 != null) {
4236 mEnqueuedNotifications.remove(recordInList);
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004237 }
Julia Reynolds0839c022017-06-15 15:24:01 -04004238 return wasPosted;
4239 }
4240
4241 @GuardedBy("mNotificationLock")
4242 private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason,
4243 boolean wasPosted) {
4244 final String canceledKey = r.getKey();
Julia Reynoldsc6b371b2016-06-14 08:31:03 -04004245
4246 // Record caller.
4247 recordCallerLocked(r);
4248
Joe Onorato46439ce2010-11-19 13:56:21 -08004249 // tell the app
4250 if (sendDelete) {
Daniel Sandlerfde19b12013-01-17 00:21:05 -05004251 if (r.getNotification().deleteIntent != null) {
Joe Onorato46439ce2010-11-19 13:56:21 -08004252 try {
Daniel Sandlerfde19b12013-01-17 00:21:05 -05004253 r.getNotification().deleteIntent.send();
Joe Onorato46439ce2010-11-19 13:56:21 -08004254 } catch (PendingIntent.CanceledException ex) {
4255 // do nothing - there's no relevant way to recover, and
4256 // no reason to let this propagate
Daniel Sandler4f91efd2013-04-25 16:38:41 -04004257 Slog.w(TAG, "canceled PendingIntent for " + r.sbn.getPackageName(), ex);
Joe Onorato46439ce2010-11-19 13:56:21 -08004258 }
4259 }
4260 }
4261
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004262 // Only cancel these if this notification actually got to be posted.
4263 if (wasPosted) {
4264 // status bar
4265 if (r.getNotification().getSmallIcon() != null) {
Julia Reynoldsa8b766f2017-03-07 16:30:21 -05004266 if (reason != REASON_SNOOZED) {
4267 r.isCanceled = true;
4268 }
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004269 mListeners.notifyRemovedLocked(r.sbn, reason);
4270 mHandler.post(new Runnable() {
4271 @Override
4272 public void run() {
4273 mGroupHelper.onNotificationRemoved(r.sbn);
4274 }
4275 });
4276 }
4277
4278 // sound
4279 if (canceledKey.equals(mSoundNotificationKey)) {
4280 mSoundNotificationKey = null;
4281 final long identity = Binder.clearCallingIdentity();
4282 try {
4283 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
4284 if (player != null) {
4285 player.stopAsync();
4286 }
4287 } catch (RemoteException e) {
4288 } finally {
4289 Binder.restoreCallingIdentity(identity);
Julia Reynolds8f488d32016-10-14 10:59:01 -04004290 }
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004291 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004292
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004293 // vibrate
4294 if (canceledKey.equals(mVibrateNotificationKey)) {
4295 mVibrateNotificationKey = null;
4296 long identity = Binder.clearCallingIdentity();
4297 try {
4298 mVibrator.cancel();
Jeff Sharkey098d5802012-04-26 17:30:34 -07004299 }
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004300 finally {
4301 Binder.restoreCallingIdentity(identity);
4302 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004303 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004304
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004305 // light
4306 mLights.remove(canceledKey);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004307 }
4308
Christoph Studer546bec82014-03-14 12:17:12 +01004309 // Record usage stats
Julia Reynoldse46bb372016-03-17 11:05:58 -04004310 // TODO: add unbundling stats?
Christoph Studer546bec82014-03-14 12:17:12 +01004311 switch (reason) {
Julia Reynoldsf619bc52017-03-17 08:32:23 -04004312 case REASON_CANCEL:
4313 case REASON_CANCEL_ALL:
Christoph Studer546bec82014-03-14 12:17:12 +01004314 case REASON_LISTENER_CANCEL:
4315 case REASON_LISTENER_CANCEL_ALL:
4316 mUsageStats.registerDismissedByUser(r);
4317 break;
Chris Wren9fa689f2015-11-20 16:44:53 -05004318 case REASON_APP_CANCEL:
4319 case REASON_APP_CANCEL_ALL:
Christoph Studer546bec82014-03-14 12:17:12 +01004320 mUsageStats.registerRemovedByApp(r);
4321 break;
Christoph Studer546bec82014-03-14 12:17:12 +01004322 }
4323
Christoph Studer265c1052014-07-23 17:14:33 +02004324 String groupKey = r.getGroupKey();
4325 NotificationRecord groupSummary = mSummaryByGroupKey.get(groupKey);
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004326 if (groupSummary != null && groupSummary.getKey().equals(canceledKey)) {
Christoph Studer265c1052014-07-23 17:14:33 +02004327 mSummaryByGroupKey.remove(groupKey);
4328 }
Julia Reynoldseae43fb2016-05-09 12:42:58 -04004329 final ArrayMap<String, String> summaries = mAutobundledSummaries.get(r.sbn.getUserId());
4330 if (summaries != null && r.sbn.getKey().equals(summaries.get(r.sbn.getPackageName()))) {
4331 summaries.remove(r.sbn.getPackageName());
Julia Reynoldse46bb372016-03-17 11:05:58 -04004332 }
Christoph Studercef37cf2014-07-25 14:18:17 +02004333
Daniel Sandler23d7c702013-03-07 16:32:06 -05004334 // Save it for users of getHistoricalNotifications()
4335 mArchive.record(r.sbn);
Christoph Studer81e5b5f2014-10-22 17:19:56 +02004336
Chris Wren6650e572015-05-15 17:19:25 -04004337 final long now = System.currentTimeMillis();
Chris Wren9eb5e102017-01-26 13:15:06 -05004338 MetricsLogger.action(r.getLogMaker(now)
4339 .setCategory(MetricsEvent.NOTIFICATION_ITEM)
4340 .setType(MetricsEvent.TYPE_DISMISS)
4341 .setSubtype(reason));
Chris Wrene6ddb8a2015-05-27 15:21:00 -04004342 EventLogTags.writeNotificationCanceled(canceledKey, reason,
4343 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004344 }
4345
4346 /**
Dianne Hackbornd8a43f62009-08-17 23:33:56 -07004347 * Cancels a notification ONLY if it has all of the {@code mustHaveFlags}
Doug Zongkerab5c49c2009-12-04 10:31:43 -08004348 * and none of the {@code mustNotHaveFlags}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004349 */
John Spurlocke6a7d932014-03-13 12:29:00 -04004350 void cancelNotification(final int callingUid, final int callingPid,
4351 final String pkg, final String tag, final int id,
Svetoslav Ganov835835e2013-08-04 20:17:52 -07004352 final int mustHaveFlags, final int mustNotHaveFlags, final boolean sendDelete,
John Spurlock7340fc82014-04-24 18:50:12 -04004353 final int userId, final int reason, final ManagedServiceInfo listener) {
Svetoslav Ganov835835e2013-08-04 20:17:52 -07004354 // In enqueueNotificationInternal notifications are added by scheduling the
4355 // work on the worker handler. Hence, we also schedule the cancel on this
4356 // handler to avoid a scenario where an add notification call followed by a
4357 // remove notification call ends up in not removing the notification.
4358 mHandler.post(new Runnable() {
4359 @Override
4360 public void run() {
Christoph Studere4ef156b2014-07-04 18:41:57 +02004361 String listenerName = listener == null ? null : listener.component.toShortString();
Chris Wrenbddb5bc2015-03-04 08:47:46 -08004362 if (DBG) EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, id, tag,
4363 userId, mustHaveFlags, mustNotHaveFlags, reason, listenerName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004364
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004365 synchronized (mNotificationLock) {
4366 // Look for the notification, searching both the posted and enqueued lists.
4367 NotificationRecord r = findNotificationLocked(pkg, tag, id, userId);
4368 if (r != null) {
4369 // The notification was found, check if it should be removed.
Doug Zongkerab5c49c2009-12-04 10:31:43 -08004370
Christoph Studer546bec82014-03-14 12:17:12 +01004371 // Ideally we'd do this in the caller of this method. However, that would
4372 // require the caller to also find the notification.
Julia Reynoldsf619bc52017-03-17 08:32:23 -04004373 if (reason == REASON_CLICK) {
Christoph Studer546bec82014-03-14 12:17:12 +01004374 mUsageStats.registerClickedByUser(r);
4375 }
4376
Svetoslav Ganov835835e2013-08-04 20:17:52 -07004377 if ((r.getNotification().flags & mustHaveFlags) != mustHaveFlags) {
4378 return;
4379 }
4380 if ((r.getNotification().flags & mustNotHaveFlags) != 0) {
4381 return;
4382 }
4383
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004384 // Cancel the notification.
Julia Reynolds0839c022017-06-15 15:24:01 -04004385 boolean wasPosted = removeFromNotificationListsLocked(r);
4386 cancelNotificationLocked(r, sendDelete, reason, wasPosted);
Christoph Studer265c1052014-07-23 17:14:33 +02004387 cancelGroupChildrenLocked(r, callingUid, callingPid, listenerName,
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004388 sendDelete);
Svetoslav Ganov835835e2013-08-04 20:17:52 -07004389 updateLightsLocked();
Julia Reynolds72f1cbb2016-09-19 14:57:31 -04004390 } else {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004391 // No notification was found, assume that it is snoozed and cancel it.
Julia Reynoldsa8b766f2017-03-07 16:30:21 -05004392 if (reason != REASON_SNOOZED) {
4393 final boolean wasSnoozed = mSnoozeHelper.cancel(userId, pkg, tag, id);
4394 if (wasSnoozed) {
4395 savePolicyFile();
4396 }
Julia Reynolds72f1cbb2016-09-19 14:57:31 -04004397 }
Svetoslav Ganov835835e2013-08-04 20:17:52 -07004398 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004399 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004400 }
Svetoslav Ganov835835e2013-08-04 20:17:52 -07004401 });
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004402 }
4403
4404 /**
Daniel Sandler321e9c52012-10-12 10:59:26 -07004405 * Determine whether the userId applies to the notification in question, either because
4406 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard).
4407 */
4408 private boolean notificationMatchesUserId(NotificationRecord r, int userId) {
4409 return
4410 // looking for USER_ALL notifications? match everything
4411 userId == UserHandle.USER_ALL
4412 // a notification sent to USER_ALL matches any query
Daniel Sandlerfde19b12013-01-17 00:21:05 -05004413 || r.getUserId() == UserHandle.USER_ALL
Daniel Sandler321e9c52012-10-12 10:59:26 -07004414 // an exact user match
Daniel Sandlerfde19b12013-01-17 00:21:05 -05004415 || r.getUserId() == userId;
Daniel Sandler321e9c52012-10-12 10:59:26 -07004416 }
4417
4418 /**
Kenny Guy3a7c4a52014-03-03 18:24:03 +00004419 * Determine whether the userId applies to the notification in question, either because
4420 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard) or
Kenny Guy2a764942014-04-02 13:29:20 +01004421 * because it matches one of the users profiles.
Kenny Guy3a7c4a52014-03-03 18:24:03 +00004422 */
Kenny Guy2a764942014-04-02 13:29:20 +01004423 private boolean notificationMatchesCurrentProfiles(NotificationRecord r, int userId) {
Kenny Guya263e4e2014-03-03 18:24:03 +00004424 return notificationMatchesUserId(r, userId)
John Spurlockb408e8e2014-04-23 21:12:45 -04004425 || mUserProfiles.isCurrentProfile(r.getUserId());
Kenny Guy3a7c4a52014-03-03 18:24:03 +00004426 }
4427
4428 /**
Julia Reynoldsef37f282016-02-12 09:11:27 -05004429 * Cancels all notifications from a given package that have all of the
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004430 * {@code mustHaveFlags}.
4431 */
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004432 void cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, String channelId,
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04004433 int mustHaveFlags, int mustNotHaveFlags, boolean doit, int userId, int reason,
John Spurlock7340fc82014-04-24 18:50:12 -04004434 ManagedServiceInfo listener) {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004435 mHandler.post(new Runnable() {
4436 @Override
4437 public void run() {
4438 String listenerName = listener == null ? null : listener.component.toShortString();
4439 EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
4440 pkg, userId, mustHaveFlags, mustNotHaveFlags, reason,
4441 listenerName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004442
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004443 // Why does this parameter exist? Do we actually want to execute the above if doit
4444 // is false?
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08004445 if (!doit) {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004446 return;
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08004447 }
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004448
4449 synchronized (mNotificationLock) {
4450 FlagChecker flagChecker = (int flags) -> {
4451 if ((flags & mustHaveFlags) != mustHaveFlags) {
4452 return false;
4453 }
4454 if ((flags & mustNotHaveFlags) != 0) {
4455 return false;
4456 }
4457 return true;
4458 };
4459
4460 cancelAllNotificationsByListLocked(mNotificationList, callingUid, callingPid,
4461 pkg, true /*nullPkgIndicatesUserSwitch*/, channelId, flagChecker,
4462 false /*includeCurrentProfiles*/, userId, false /*sendDelete*/, reason,
Julia Reynolds0839c022017-06-15 15:24:01 -04004463 listenerName, true /* wasPosted */);
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004464 cancelAllNotificationsByListLocked(mEnqueuedNotifications, callingUid,
4465 callingPid, pkg, true /*nullPkgIndicatesUserSwitch*/, channelId,
4466 flagChecker, false /*includeCurrentProfiles*/, userId,
Julia Reynolds0839c022017-06-15 15:24:01 -04004467 false /*sendDelete*/, reason, listenerName, false /* wasPosted */);
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004468 mSnoozeHelper.cancel(userId, pkg);
Christoph Studere4ef156b2014-07-04 18:41:57 +02004469 }
4470 }
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004471 });
4472 }
4473
4474 private interface FlagChecker {
4475 // Returns false if these flags do not pass the defined flag test.
4476 public boolean apply(int flags);
4477 }
4478
Julia Reynolds88860ce2017-06-01 16:55:49 -04004479 @GuardedBy("mNotificationLock")
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004480 private void cancelAllNotificationsByListLocked(ArrayList<NotificationRecord> notificationList,
4481 int callingUid, int callingPid, String pkg, boolean nullPkgIndicatesUserSwitch,
4482 String channelId, FlagChecker flagChecker, boolean includeCurrentProfiles, int userId,
Julia Reynolds0839c022017-06-15 15:24:01 -04004483 boolean sendDelete, int reason, String listenerName, boolean wasPosted) {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004484 ArrayList<NotificationRecord> canceledNotifications = null;
4485 for (int i = notificationList.size() - 1; i >= 0; --i) {
4486 NotificationRecord r = notificationList.get(i);
4487 if (includeCurrentProfiles) {
4488 if (!notificationMatchesCurrentProfiles(r, userId)) {
4489 continue;
4490 }
4491 } else if (!notificationMatchesUserId(r, userId)) {
4492 continue;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004493 }
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004494 // Don't remove notifications to all, if there's no package name specified
4495 if (nullPkgIndicatesUserSwitch && pkg == null && r.getUserId() == UserHandle.USER_ALL) {
4496 continue;
4497 }
4498 if (!flagChecker.apply(r.getFlags())) {
4499 continue;
4500 }
4501 if (pkg != null && !r.sbn.getPackageName().equals(pkg)) {
4502 continue;
4503 }
4504 if (channelId != null && !channelId.equals(r.getChannel().getId())) {
4505 continue;
4506 }
4507
4508 if (canceledNotifications == null) {
4509 canceledNotifications = new ArrayList<>();
4510 }
Julia Reynolds0839c022017-06-15 15:24:01 -04004511 notificationList.remove(i);
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004512 canceledNotifications.add(r);
Julia Reynolds0839c022017-06-15 15:24:01 -04004513 cancelNotificationLocked(r, sendDelete, reason, wasPosted);
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004514 }
4515 if (canceledNotifications != null) {
4516 final int M = canceledNotifications.size();
4517 for (int i = 0; i < M; i++) {
4518 cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid,
4519 listenerName, false /* sendDelete */);
4520 }
4521 updateLightsLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004522 }
4523 }
4524
Julia Reynolds50989772017-02-23 14:32:16 -05004525 void snoozeNotificationInt(String key, long duration, String snoozeCriterionId,
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004526 ManagedServiceInfo listener) {
Julia Reynolds79672302017-01-12 08:30:16 -05004527 String listenerName = listener == null ? null : listener.component.toShortString();
Julia Reynoldsa8b766f2017-03-07 16:30:21 -05004528 if (duration <= 0 && snoozeCriterionId == null || key == null) {
Julia Reynoldscf63ff12017-01-24 13:55:48 -05004529 return;
4530 }
Julia Reynolds520df6e2017-02-13 09:05:10 -05004531
Julia Reynolds79672302017-01-12 08:30:16 -05004532 if (DBG) {
Julia Reynolds50989772017-02-23 14:32:16 -05004533 Slog.d(TAG, String.format("snooze event(%s, %d, %s, %s)", key, duration,
4534 snoozeCriterionId, listenerName));
Julia Reynolds79672302017-01-12 08:30:16 -05004535 }
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004536 // Needs to post so that it can cancel notifications not yet enqueued.
Julia Reynoldsa78cdff2017-04-26 10:19:25 -04004537 mHandler.post(new SnoozeNotificationRunnable(key, duration, snoozeCriterionId));
Julia Reynoldsb6c1f992016-11-22 09:26:46 -05004538 }
4539
4540 void unsnoozeNotificationInt(String key, ManagedServiceInfo listener) {
4541 String listenerName = listener == null ? null : listener.component.toShortString();
Julia Reynoldsb6c1f992016-11-22 09:26:46 -05004542 if (DBG) {
4543 Slog.d(TAG, String.format("unsnooze event(%s, %s)", key, listenerName));
4544 }
Julia Reynolds79672302017-01-12 08:30:16 -05004545 mSnoozeHelper.repost(key);
4546 savePolicyFile();
Julia Reynoldsb6c1f992016-11-22 09:26:46 -05004547 }
4548
Julia Reynolds88860ce2017-06-01 16:55:49 -04004549 @GuardedBy("mNotificationLock")
Adam Lesinski350159c2014-03-27 11:15:11 -07004550 void cancelAllLocked(int callingUid, int callingPid, int userId, int reason,
John Spurlock7340fc82014-04-24 18:50:12 -04004551 ManagedServiceInfo listener, boolean includeCurrentProfiles) {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004552 mHandler.post(new Runnable() {
4553 @Override
4554 public void run() {
4555 synchronized (mNotificationLock) {
4556 String listenerName =
4557 listener == null ? null : listener.component.toShortString();
4558 EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
4559 null, userId, 0, 0, reason, listenerName);
Christoph Studer546bec82014-03-14 12:17:12 +01004560
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004561 FlagChecker flagChecker = (int flags) -> {
4562 if ((flags & (Notification.FLAG_ONGOING_EVENT | Notification.FLAG_NO_CLEAR))
4563 != 0) {
4564 return false;
4565 }
4566 return true;
4567 };
4568
4569 cancelAllNotificationsByListLocked(mNotificationList, callingUid, callingPid,
4570 null, false /*nullPkgIndicatesUserSwitch*/, null, flagChecker,
4571 includeCurrentProfiles, userId, true /*sendDelete*/, reason,
Julia Reynolds0839c022017-06-15 15:24:01 -04004572 listenerName, true);
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004573 cancelAllNotificationsByListLocked(mEnqueuedNotifications, callingUid,
4574 callingPid, null, false /*nullPkgIndicatesUserSwitch*/, null,
4575 flagChecker, includeCurrentProfiles, userId, true /*sendDelete*/,
Julia Reynolds0839c022017-06-15 15:24:01 -04004576 reason, listenerName, false);
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004577 mSnoozeHelper.cancel(userId, includeCurrentProfiles);
Kenny Guya263e4e2014-03-03 18:24:03 +00004578 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004579 }
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004580 });
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004581 }
4582
Christoph Studere4ef156b2014-07-04 18:41:57 +02004583 // Warning: The caller is responsible for invoking updateLightsLocked().
Julia Reynolds88860ce2017-06-01 16:55:49 -04004584 @GuardedBy("mNotificationLock")
Christoph Studere4ef156b2014-07-04 18:41:57 +02004585 private void cancelGroupChildrenLocked(NotificationRecord r, int callingUid, int callingPid,
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004586 String listenerName, boolean sendDelete) {
Christoph Studere4ef156b2014-07-04 18:41:57 +02004587 Notification n = r.getNotification();
Christoph Studer3f31f5d2014-07-31 16:55:32 +02004588 if (!n.isGroupSummary()) {
Christoph Studere4ef156b2014-07-04 18:41:57 +02004589 return;
4590 }
4591
4592 String pkg = r.sbn.getPackageName();
Christoph Studere4ef156b2014-07-04 18:41:57 +02004593
4594 if (pkg == null) {
4595 if (DBG) Log.e(TAG, "No package for group summary: " + r.getKey());
4596 return;
4597 }
4598
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004599 cancelGroupChildrenByListLocked(mNotificationList, r, callingUid, callingPid, listenerName,
Julia Reynolds0839c022017-06-15 15:24:01 -04004600 sendDelete, true);
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004601 cancelGroupChildrenByListLocked(mEnqueuedNotifications, r, callingUid, callingPid,
Julia Reynolds0839c022017-06-15 15:24:01 -04004602 listenerName, sendDelete, false);
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004603 }
4604
Julia Reynolds88860ce2017-06-01 16:55:49 -04004605 @GuardedBy("mNotificationLock")
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004606 private void cancelGroupChildrenByListLocked(ArrayList<NotificationRecord> notificationList,
4607 NotificationRecord parentNotification, int callingUid, int callingPid,
Julia Reynolds0839c022017-06-15 15:24:01 -04004608 String listenerName, boolean sendDelete, boolean wasPosted) {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004609 final String pkg = parentNotification.sbn.getPackageName();
4610 final int userId = parentNotification.getUserId();
4611 final int reason = REASON_GROUP_SUMMARY_CANCELED;
4612 for (int i = notificationList.size() - 1; i >= 0; i--) {
4613 final NotificationRecord childR = notificationList.get(i);
4614 final StatusBarNotification childSbn = childR.sbn;
Julia Reynoldse46bb372016-03-17 11:05:58 -04004615 if ((childSbn.isGroup() && !childSbn.getNotification().isGroupSummary()) &&
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004616 childR.getGroupKey().equals(parentNotification.getGroupKey())
Selim Cinek1d359792017-01-13 14:43:43 -08004617 && (childR.getFlags() & Notification.FLAG_FOREGROUND_SERVICE) == 0) {
Christoph Studer265c1052014-07-23 17:14:33 +02004618 EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, childSbn.getId(),
4619 childSbn.getTag(), userId, 0, 0, reason, listenerName);
Julia Reynolds0839c022017-06-15 15:24:01 -04004620 notificationList.remove(i);
4621 cancelNotificationLocked(childR, sendDelete, reason, wasPosted);
Christoph Studere4ef156b2014-07-04 18:41:57 +02004622 }
4623 }
4624 }
4625
Julia Reynolds88860ce2017-06-01 16:55:49 -04004626 @GuardedBy("mNotificationLock")
Adam Lesinski182f73f2013-12-05 16:48:06 -08004627 void updateLightsLocked()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004628 {
The Android Open Source Project10592532009-03-18 17:39:46 -07004629 // handle notification lights
Chris Wren6054e612014-11-25 17:16:46 -05004630 NotificationRecord ledNotification = null;
4631 while (ledNotification == null && !mLights.isEmpty()) {
4632 final String owner = mLights.get(mLights.size() - 1);
4633 ledNotification = mNotificationsByKey.get(owner);
4634 if (ledNotification == null) {
4635 Slog.wtfStack(TAG, "LED Notification does not exist: " + owner);
4636 mLights.remove(owner);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004637 }
4638 }
Mike Lockwoodc22404a2009-12-02 11:15:02 -05004639
Mike Lockwood63b5ad92011-08-30 09:55:30 -04004640 // Don't flash while we are in a call or screen is on
Chris Wren6054e612014-11-25 17:16:46 -05004641 if (ledNotification == null || mInCall || mScreenOn) {
Mike Lockwood3cb67a32009-11-27 14:25:58 -05004642 mNotificationLight.turnOff();
The Android Open Source Project10592532009-03-18 17:39:46 -07004643 } else {
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05004644 NotificationRecord.Light light = ledNotification.getLight();
4645 if (light != null && mNotificationPulseEnabled) {
Mike Lockwood670f9322010-01-20 12:13:36 -05004646 // pulse repeatedly
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05004647 mNotificationLight.setFlashing(light.color, Light.LIGHT_FLASH_TIMED,
4648 light.onMs, light.offMs);
Mike Lockwood670f9322010-01-20 12:13:36 -05004649 }
The Android Open Source Project10592532009-03-18 17:39:46 -07004650 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004651 }
4652
Julia Reynolds88860ce2017-06-01 16:55:49 -04004653 @GuardedBy("mNotificationLock")
Julia Reynoldsa78cdff2017-04-26 10:19:25 -04004654 @NonNull List<NotificationRecord> findGroupNotificationsLocked(String pkg,
4655 String groupKey, int userId) {
4656 List<NotificationRecord> records = new ArrayList<>();
4657 records.addAll(findGroupNotificationByListLocked(mNotificationList, pkg, groupKey, userId));
4658 records.addAll(
4659 findGroupNotificationByListLocked(mEnqueuedNotifications, pkg, groupKey, userId));
4660 return records;
4661 }
4662
4663
Julia Reynolds88860ce2017-06-01 16:55:49 -04004664 @GuardedBy("mNotificationLock")
Julia Reynoldsa78cdff2017-04-26 10:19:25 -04004665 private @NonNull List<NotificationRecord> findGroupNotificationByListLocked(
4666 ArrayList<NotificationRecord> list, String pkg, String groupKey, int userId) {
4667 List<NotificationRecord> records = new ArrayList<>();
4668 final int len = list.size();
4669 for (int i = 0; i < len; i++) {
4670 NotificationRecord r = list.get(i);
4671 if (notificationMatchesUserId(r, userId) && r.getGroupKey().equals(groupKey)
4672 && r.sbn.getPackageName().equals(pkg)) {
4673 records.add(r);
4674 }
4675 }
4676 return records;
4677 }
4678
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004679 // Searches both enqueued and posted notifications by key.
4680 // TODO: need to combine a bunch of these getters with slightly different behavior.
4681 // TODO: Should enqueuing just add to mNotificationsByKey instead?
Julia Reynolds88860ce2017-06-01 16:55:49 -04004682 @GuardedBy("mNotificationLock")
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004683 private NotificationRecord findNotificationByKeyLocked(String key) {
Geoffrey Pitschccc0b972017-02-15 10:52:26 -05004684 NotificationRecord r;
4685 if ((r = findNotificationByListLocked(mNotificationList, key)) != null) {
4686 return r;
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004687 }
Geoffrey Pitschccc0b972017-02-15 10:52:26 -05004688 if ((r = findNotificationByListLocked(mEnqueuedNotifications, key)) != null) {
4689 return r;
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004690 }
4691 return null;
4692 }
4693
Julia Reynolds88860ce2017-06-01 16:55:49 -04004694 @GuardedBy("mNotificationLock")
Julia Reynoldsa78cdff2017-04-26 10:19:25 -04004695 NotificationRecord findNotificationLocked(String pkg, String tag, int id, int userId) {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004696 NotificationRecord r;
4697 if ((r = findNotificationByListLocked(mNotificationList, pkg, tag, id, userId)) != null) {
4698 return r;
4699 }
4700 if ((r = findNotificationByListLocked(mEnqueuedNotifications, pkg, tag, id, userId))
4701 != null) {
4702 return r;
4703 }
4704 return null;
4705 }
4706
Julia Reynolds88860ce2017-06-01 16:55:49 -04004707 @GuardedBy("mNotificationLock")
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004708 private NotificationRecord findNotificationByListLocked(ArrayList<NotificationRecord> list,
Geoffrey Pitschccc0b972017-02-15 10:52:26 -05004709 String pkg, String tag, int id, int userId) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004710 final int len = list.size();
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004711 for (int i = 0; i < len; i++) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004712 NotificationRecord r = list.get(i);
Vladimir Marko2526f332013-09-11 11:13:55 +01004713 if (notificationMatchesUserId(r, userId) && r.sbn.getId() == id &&
4714 TextUtils.equals(r.sbn.getTag(), tag) && r.sbn.getPackageName().equals(pkg)) {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004715 return r;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004716 }
4717 }
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004718 return null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004719 }
4720
Julia Reynolds88860ce2017-06-01 16:55:49 -04004721 @GuardedBy("mNotificationLock")
Geoffrey Pitschccc0b972017-02-15 10:52:26 -05004722 private NotificationRecord findNotificationByListLocked(ArrayList<NotificationRecord> list,
Julia Reynolds88860ce2017-06-01 16:55:49 -04004723 String key) {
Geoffrey Pitschccc0b972017-02-15 10:52:26 -05004724 final int N = list.size();
4725 for (int i = 0; i < N; i++) {
4726 if (key.equals(list.get(i).getKey())) {
4727 return list.get(i);
4728 }
4729 }
4730 return null;
4731 }
4732
Julia Reynolds88860ce2017-06-01 16:55:49 -04004733 @GuardedBy("mNotificationLock")
Christoph Studer71f18fd2014-05-20 17:02:04 +02004734 int indexOfNotificationLocked(String key) {
Christoph Studerc5115552014-06-12 20:22:31 +02004735 final int N = mNotificationList.size();
4736 for (int i = 0; i < N; i++) {
4737 if (key.equals(mNotificationList.get(i).getKey())) {
4738 return i;
4739 }
Christoph Studer71f18fd2014-05-20 17:02:04 +02004740 }
Christoph Studerc5115552014-06-12 20:22:31 +02004741 return -1;
Christoph Studer71f18fd2014-05-20 17:02:04 +02004742 }
4743
Mike Lockwoodc22404a2009-12-02 11:15:02 -05004744 private void updateNotificationPulse() {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004745 synchronized (mNotificationLock) {
Mike Lockwoodc22404a2009-12-02 11:15:02 -05004746 updateLightsLocked();
4747 }
4748 }
John Spurlocke677d712014-02-13 12:52:19 -05004749
Geoffrey Pitsch27684152017-05-02 11:41:31 -04004750 protected boolean isCallingUidSystem() {
4751 final int uid = Binder.getCallingUid();
4752 return uid == Process.SYSTEM_UID;
4753 }
4754
4755 protected boolean isUidSystemOrPhone(int uid) {
John Spurlock7340fc82014-04-24 18:50:12 -04004756 final int appid = UserHandle.getAppId(uid);
4757 return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0);
4758 }
John Spurlockb408e8e2014-04-23 21:12:45 -04004759
Geoffrey Pitsch27684152017-05-02 11:41:31 -04004760 // TODO: Most calls should probably move to isCallerSystem.
4761 protected boolean isCallerSystemOrPhone() {
4762 return isUidSystemOrPhone(Binder.getCallingUid());
John Spurlock7340fc82014-04-24 18:50:12 -04004763 }
4764
Julia Reynolds73ed76b2017-04-04 17:04:38 -04004765 private void checkCallerIsSystem() {
Geoffrey Pitsch27684152017-05-02 11:41:31 -04004766 if (isCallerSystemOrPhone()) {
John Spurlock7340fc82014-04-24 18:50:12 -04004767 return;
4768 }
4769 throw new SecurityException("Disallowed call for uid " + Binder.getCallingUid());
4770 }
4771
Geoffrey Pitsche75a66e2016-11-22 11:12:11 -05004772 private void checkCallerIsSystemOrSameApp(String pkg) {
Geoffrey Pitsch27684152017-05-02 11:41:31 -04004773 if (isCallerSystemOrPhone()) {
John Spurlock7340fc82014-04-24 18:50:12 -04004774 return;
4775 }
Julia Reynolds0cd1b782016-06-29 08:43:00 -04004776 checkCallerIsSameApp(pkg);
4777 }
4778
Chad Brubaker6b68f102017-01-27 13:39:00 -08004779 private boolean isCallerInstantApp(String pkg) {
4780 // System is always allowed to act for ephemeral apps.
Geoffrey Pitsch27684152017-05-02 11:41:31 -04004781 if (isCallerSystemOrPhone()) {
Chad Brubaker6b68f102017-01-27 13:39:00 -08004782 return false;
4783 }
4784
4785 mAppOps.checkPackage(Binder.getCallingUid(), pkg);
4786
4787 try {
4788 ApplicationInfo ai = mPackageManager.getApplicationInfo(pkg, 0,
4789 UserHandle.getCallingUserId());
4790 if (ai == null) {
4791 throw new SecurityException("Unknown package " + pkg);
4792 }
4793 return ai.isInstantApp();
4794 } catch (RemoteException re) {
4795 throw new SecurityException("Unknown package " + pkg, re);
4796 }
4797
4798 }
4799
Geoffrey Pitsche75a66e2016-11-22 11:12:11 -05004800 private void checkCallerIsSameApp(String pkg) {
John Spurlock7340fc82014-04-24 18:50:12 -04004801 final int uid = Binder.getCallingUid();
4802 try {
Geoffrey Pitsche75a66e2016-11-22 11:12:11 -05004803 ApplicationInfo ai = mPackageManager.getApplicationInfo(
John Spurlock7340fc82014-04-24 18:50:12 -04004804 pkg, 0, UserHandle.getCallingUserId());
Dan Sandler09afc2e2014-07-18 14:29:20 -04004805 if (ai == null) {
4806 throw new SecurityException("Unknown package " + pkg);
4807 }
John Spurlock7340fc82014-04-24 18:50:12 -04004808 if (!UserHandle.isSameApp(ai.uid, uid)) {
Geoffrey Pitsche75a66e2016-11-22 11:12:11 -05004809 throw new SecurityException("Calling uid " + uid + " gave package "
John Spurlock7340fc82014-04-24 18:50:12 -04004810 + pkg + " which is owned by uid " + ai.uid);
4811 }
4812 } catch (RemoteException re) {
4813 throw new SecurityException("Unknown package " + pkg + "\n" + re);
4814 }
4815 }
4816
John Spurlock32fe4c62014-10-02 12:16:02 -04004817 private static String callStateToString(int state) {
4818 switch (state) {
4819 case TelephonyManager.CALL_STATE_IDLE: return "CALL_STATE_IDLE";
4820 case TelephonyManager.CALL_STATE_RINGING: return "CALL_STATE_RINGING";
4821 case TelephonyManager.CALL_STATE_OFFHOOK: return "CALL_STATE_OFFHOOK";
4822 default: return "CALL_STATE_UNKNOWN_" + state;
4823 }
4824 }
4825
4826 private void listenForCallState() {
4827 TelephonyManager.from(getContext()).listen(new PhoneStateListener() {
4828 @Override
4829 public void onCallStateChanged(int state, String incomingNumber) {
4830 if (mCallState == state) return;
4831 if (DBG) Slog.d(TAG, "Call state changed: " + callStateToString(state));
4832 mCallState = state;
4833 }
4834 }, PhoneStateListener.LISTEN_CALL_STATE);
4835 }
4836
Christoph Studer05ad4822014-05-16 14:16:03 +02004837 /**
4838 * Generates a NotificationRankingUpdate from 'sbns', considering only
4839 * notifications visible to the given listener.
4840 */
Julia Reynolds88860ce2017-06-01 16:55:49 -04004841 @GuardedBy("mNotificationLock")
Chris Wren333a61c2014-05-28 16:40:57 -04004842 private NotificationRankingUpdate makeRankingUpdateLocked(ManagedServiceInfo info) {
Chris Wren333a61c2014-05-28 16:40:57 -04004843 final int N = mNotificationList.size();
4844 ArrayList<String> keys = new ArrayList<String>(N);
Christoph Studer1d599da2014-06-12 15:25:59 +02004845 ArrayList<String> interceptedKeys = new ArrayList<String>(N);
Chris Wrenbdf33762015-12-04 15:50:51 -05004846 ArrayList<Integer> importance = new ArrayList<>(N);
Julia Reynoldse46bb372016-03-17 11:05:58 -04004847 Bundle overrideGroupKeys = new Bundle();
Chris Wren3ad4e3a2014-09-02 17:23:51 -04004848 Bundle visibilityOverrides = new Bundle();
Julia Reynoldsf612869ae2015-11-05 16:48:55 -05004849 Bundle suppressedVisualEffects = new Bundle();
Chris Wrenbdf33762015-12-04 15:50:51 -05004850 Bundle explanation = new Bundle();
Julia Reynolds924eed12017-01-19 09:52:07 -05004851 Bundle channels = new Bundle();
Julia Reynolds22f02b32016-12-01 15:05:13 -05004852 Bundle overridePeople = new Bundle();
4853 Bundle snoozeCriteria = new Bundle();
Julia Reynolds924eed12017-01-19 09:52:07 -05004854 Bundle showBadge = new Bundle();
Chris Wren333a61c2014-05-28 16:40:57 -04004855 for (int i = 0; i < N; i++) {
4856 NotificationRecord record = mNotificationList.get(i);
Christoph Studercef37cf2014-07-25 14:18:17 +02004857 if (!isVisibleToListener(record.sbn, info)) {
Christoph Studer05ad4822014-05-16 14:16:03 +02004858 continue;
4859 }
Chris Wrenbdf33762015-12-04 15:50:51 -05004860 final String key = record.sbn.getKey();
4861 keys.add(key);
4862 importance.add(record.getImportance());
4863 if (record.getImportanceExplanation() != null) {
4864 explanation.putCharSequence(key, record.getImportanceExplanation());
4865 }
Chris Wren333a61c2014-05-28 16:40:57 -04004866 if (record.isIntercepted()) {
Chris Wrenbdf33762015-12-04 15:50:51 -05004867 interceptedKeys.add(key);
Julia Reynoldsf612869ae2015-11-05 16:48:55 -05004868
Christoph Studer05ad4822014-05-16 14:16:03 +02004869 }
Chris Wrenbdf33762015-12-04 15:50:51 -05004870 suppressedVisualEffects.putInt(key, record.getSuppressedVisualEffects());
Chris Wren3ad4e3a2014-09-02 17:23:51 -04004871 if (record.getPackageVisibilityOverride()
4872 != NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE) {
Chris Wrenbdf33762015-12-04 15:50:51 -05004873 visibilityOverrides.putInt(key, record.getPackageVisibilityOverride());
Chris Wren3ad4e3a2014-09-02 17:23:51 -04004874 }
Julia Reynoldse46bb372016-03-17 11:05:58 -04004875 overrideGroupKeys.putString(key, record.sbn.getOverrideGroupKey());
Julia Reynolds924eed12017-01-19 09:52:07 -05004876 channels.putParcelable(key, record.getChannel());
Julia Reynolds22f02b32016-12-01 15:05:13 -05004877 overridePeople.putStringArrayList(key, record.getPeopleOverride());
4878 snoozeCriteria.putParcelableArrayList(key, record.getSnoozeCriteria());
Julia Reynolds924eed12017-01-19 09:52:07 -05004879 showBadge.putBoolean(key, record.canShowBadge());
Christoph Studer05ad4822014-05-16 14:16:03 +02004880 }
Chris Wrenbdf33762015-12-04 15:50:51 -05004881 final int M = keys.size();
4882 String[] keysAr = keys.toArray(new String[M]);
Christoph Studer1d599da2014-06-12 15:25:59 +02004883 String[] interceptedKeysAr = interceptedKeys.toArray(new String[interceptedKeys.size()]);
Chris Wrenbdf33762015-12-04 15:50:51 -05004884 int[] importanceAr = new int[M];
4885 for (int i = 0; i < M; i++) {
4886 importanceAr[i] = importance.get(i);
4887 }
Chris Wren3ad4e3a2014-09-02 17:23:51 -04004888 return new NotificationRankingUpdate(keysAr, interceptedKeysAr, visibilityOverrides,
Julia Reynolds22f02b32016-12-01 15:05:13 -05004889 suppressedVisualEffects, importanceAr, explanation, overrideGroupKeys,
Julia Reynolds924eed12017-01-19 09:52:07 -05004890 channels, overridePeople, snoozeCriteria, showBadge);
Christoph Studer05ad4822014-05-16 14:16:03 +02004891 }
4892
Julia Reynoldsda781472017-04-12 09:41:16 -04004893 boolean hasCompanionDevice(ManagedServiceInfo info) {
Julia Reynolds73ed76b2017-04-04 17:04:38 -04004894 if (mCompanionManager == null) {
Julia Reynolds727a7282017-04-13 10:54:01 -04004895 mCompanionManager = getCompanionManager();
4896 }
4897 // Companion mgr doesn't exist on all device types
4898 if (mCompanionManager == null) {
4899 return false;
Julia Reynolds73ed76b2017-04-04 17:04:38 -04004900 }
Julia Reynoldsda781472017-04-12 09:41:16 -04004901 long identity = Binder.clearCallingIdentity();
4902 try {
4903 List<String> associations = mCompanionManager.getAssociations(
4904 info.component.getPackageName(), info.userid);
4905 if (!ArrayUtils.isEmpty(associations)) {
4906 return true;
4907 }
4908 } catch (SecurityException se) {
4909 // Not a privileged listener
4910 } catch (RemoteException re) {
4911 Slog.e(TAG, "Cannot reach companion device service", re);
4912 } catch (Exception e) {
4913 Slog.e(TAG, "Cannot verify listener " + info, e);
4914 } finally {
4915 Binder.restoreCallingIdentity(identity);
Julia Reynolds73ed76b2017-04-04 17:04:38 -04004916 }
Julia Reynoldsda781472017-04-12 09:41:16 -04004917 return false;
Julia Reynolds73ed76b2017-04-04 17:04:38 -04004918 }
4919
Julia Reynolds727a7282017-04-13 10:54:01 -04004920 protected ICompanionDeviceManager getCompanionManager() {
4921 return ICompanionDeviceManager.Stub.asInterface(
4922 ServiceManager.getService(Context.COMPANION_DEVICE_SERVICE));
4923 }
4924
Christoph Studercef37cf2014-07-25 14:18:17 +02004925 private boolean isVisibleToListener(StatusBarNotification sbn, ManagedServiceInfo listener) {
4926 if (!listener.enabledAndUserMatches(sbn.getUserId())) {
4927 return false;
4928 }
Justin Koh8d11a5a2014-08-04 18:29:49 -07004929 // TODO: remove this for older listeners.
Christoph Studercef37cf2014-07-25 14:18:17 +02004930 return true;
4931 }
4932
Andrei Stingaceanu355b2322016-02-12 16:43:51 +00004933 private boolean isPackageSuspendedForUser(String pkg, int uid) {
Andrei Stingaceanu0122f6512016-01-22 15:33:03 +00004934 int userId = UserHandle.getUserId(uid);
Andrei Stingaceanu0122f6512016-01-22 15:33:03 +00004935 try {
Geoffrey Pitsche75a66e2016-11-22 11:12:11 -05004936 return mPackageManager.isPackageSuspendedForUser(pkg, userId);
Andrei Stingaceanu0122f6512016-01-22 15:33:03 +00004937 } catch (RemoteException re) {
4938 throw new SecurityException("Could not talk to package manager service");
Andrei Stingaceanuefc4a342016-03-22 14:43:01 +00004939 } catch (IllegalArgumentException ex) {
4940 // Package not found.
4941 return false;
Andrei Stingaceanu0122f6512016-01-22 15:33:03 +00004942 }
Andrei Stingaceanu0122f6512016-01-22 15:33:03 +00004943 }
4944
Chris Wren47633422016-01-22 09:56:59 -05004945 private class TrimCache {
4946 StatusBarNotification heavy;
4947 StatusBarNotification sbnClone;
4948 StatusBarNotification sbnCloneLight;
4949
4950 TrimCache(StatusBarNotification sbn) {
4951 heavy = sbn;
4952 }
4953
4954 StatusBarNotification ForListener(ManagedServiceInfo info) {
4955 if (mListeners.getOnNotificationPostedTrim(info) == TRIM_LIGHT) {
4956 if (sbnCloneLight == null) {
4957 sbnCloneLight = heavy.cloneLight();
4958 }
4959 return sbnCloneLight;
4960 } else {
4961 if (sbnClone == null) {
4962 sbnClone = heavy.clone();
4963 }
4964 return sbnClone;
4965 }
4966 }
4967 }
4968
Julia Reynolds77b2cc92016-11-08 14:41:09 -05004969 public class NotificationAssistants extends ManagedServices {
Chris Wren51017d02015-12-15 15:34:46 -05004970
Julia Reynolds77b2cc92016-11-08 14:41:09 -05004971 public NotificationAssistants() {
Julia Reynolds88860ce2017-06-01 16:55:49 -04004972 super(getContext(), mHandler, mNotificationLock, mUserProfiles);
Chris Wren51017d02015-12-15 15:34:46 -05004973 }
4974
4975 @Override
4976 protected Config getConfig() {
4977 Config c = new Config();
Julia Reynolds77b2cc92016-11-08 14:41:09 -05004978 c.caption = "notification assistant service";
4979 c.serviceInterface = NotificationAssistantService.SERVICE_INTERFACE;
4980 c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_ASSISTANT;
4981 c.bindPermission = Manifest.permission.BIND_NOTIFICATION_ASSISTANT_SERVICE;
Chris Wren51017d02015-12-15 15:34:46 -05004982 c.settingsAction = Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS;
Chris Wrene0ba7eb2016-03-04 17:30:43 -05004983 c.clientLabel = R.string.notification_ranker_binding_label;
Chris Wren51017d02015-12-15 15:34:46 -05004984 return c;
4985 }
4986
4987 @Override
4988 protected IInterface asInterface(IBinder binder) {
4989 return INotificationListener.Stub.asInterface(binder);
4990 }
4991
4992 @Override
4993 protected boolean checkType(IInterface service) {
4994 return service instanceof INotificationListener;
4995 }
4996
4997 @Override
4998 protected void onServiceAdded(ManagedServiceInfo info) {
4999 mListeners.registerGuestService(info);
5000 }
5001
5002 @Override
Julia Reynolds88860ce2017-06-01 16:55:49 -04005003 @GuardedBy("mNotificationLock")
Chris Wren51017d02015-12-15 15:34:46 -05005004 protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
5005 mListeners.unregisterService(removed.service, removed.userid);
5006 }
Chris Wren47633422016-01-22 09:56:59 -05005007
5008 public void onNotificationEnqueued(final NotificationRecord r) {
5009 final StatusBarNotification sbn = r.sbn;
5010 TrimCache trimCache = new TrimCache(sbn);
5011
Chris Wren47633422016-01-22 09:56:59 -05005012 // There should be only one, but it's a list, so while we enforce
5013 // singularity elsewhere, we keep it general here, to avoid surprises.
Julia Reynolds00314d92017-04-14 10:01:24 -04005014 for (final ManagedServiceInfo info : NotificationAssistants.this.getServices()) {
Chris Wren47633422016-01-22 09:56:59 -05005015 boolean sbnVisible = isVisibleToListener(sbn, info);
5016 if (!sbnVisible) {
5017 continue;
5018 }
5019
5020 final int importance = r.getImportance();
5021 final boolean fromUser = r.isImportanceFromUser();
5022 final StatusBarNotification sbnToPost = trimCache.ForListener(info);
Chris Wrene0ba7eb2016-03-04 17:30:43 -05005023 mHandler.post(new Runnable() {
Chris Wren47633422016-01-22 09:56:59 -05005024 @Override
5025 public void run() {
Julia Reynoldsceecfcf2017-01-31 09:44:26 -05005026 notifyEnqueued(info, sbnToPost);
Chris Wren47633422016-01-22 09:56:59 -05005027 }
5028 });
5029 }
5030 }
5031
5032 private void notifyEnqueued(final ManagedServiceInfo info,
Julia Reynoldsceecfcf2017-01-31 09:44:26 -05005033 final StatusBarNotification sbn) {
Julia Reynolds77b2cc92016-11-08 14:41:09 -05005034 final INotificationListener assistant = (INotificationListener) info.service;
Chris Wren47633422016-01-22 09:56:59 -05005035 StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
5036 try {
Julia Reynoldsceecfcf2017-01-31 09:44:26 -05005037 assistant.onNotificationEnqueued(sbnHolder);
Chris Wren47633422016-01-22 09:56:59 -05005038 } catch (RemoteException ex) {
Julia Reynolds77b2cc92016-11-08 14:41:09 -05005039 Log.e(TAG, "unable to notify assistant (enqueued): " + assistant, ex);
Chris Wren47633422016-01-22 09:56:59 -05005040 }
5041 }
5042
Julia Reynolds79672302017-01-12 08:30:16 -05005043 /**
5044 * asynchronously notify the assistant that a notification has been snoozed until a
5045 * context
5046 */
Julia Reynolds88860ce2017-06-01 16:55:49 -04005047 @GuardedBy("mNotificationLock")
Julia Reynolds79672302017-01-12 08:30:16 -05005048 public void notifyAssistantSnoozedLocked(final StatusBarNotification sbn,
5049 final String snoozeCriterionId) {
5050 TrimCache trimCache = new TrimCache(sbn);
Julia Reynolds00314d92017-04-14 10:01:24 -04005051 for (final ManagedServiceInfo info : getServices()) {
Julia Reynolds79672302017-01-12 08:30:16 -05005052 final StatusBarNotification sbnToPost = trimCache.ForListener(info);
5053 mHandler.post(new Runnable() {
5054 @Override
5055 public void run() {
5056 final INotificationListener assistant =
5057 (INotificationListener) info.service;
5058 StatusBarNotificationHolder sbnHolder
5059 = new StatusBarNotificationHolder(sbnToPost);
5060 try {
5061 assistant.onNotificationSnoozedUntilContext(
5062 sbnHolder, snoozeCriterionId);
5063 } catch (RemoteException ex) {
5064 Log.e(TAG, "unable to notify assistant (snoozed): " + assistant, ex);
5065 }
5066 }
5067 });
5068 }
5069 }
5070
Chris Wren47633422016-01-22 09:56:59 -05005071 public boolean isEnabled() {
Julia Reynolds00314d92017-04-14 10:01:24 -04005072 return !getServices().isEmpty();
Chris Wren47633422016-01-22 09:56:59 -05005073 }
Chris Wren51017d02015-12-15 15:34:46 -05005074 }
5075
John Spurlock7340fc82014-04-24 18:50:12 -04005076 public class NotificationListeners extends ManagedServices {
5077
Christoph Studerb82bc782014-08-20 14:29:43 +02005078 private final ArraySet<ManagedServiceInfo> mLightTrimListeners = new ArraySet<>();
5079
John Spurlock7340fc82014-04-24 18:50:12 -04005080 public NotificationListeners() {
Julia Reynolds88860ce2017-06-01 16:55:49 -04005081 super(getContext(), mHandler, mNotificationLock, mUserProfiles);
John Spurlock7340fc82014-04-24 18:50:12 -04005082 }
5083
5084 @Override
5085 protected Config getConfig() {
5086 Config c = new Config();
5087 c.caption = "notification listener";
5088 c.serviceInterface = NotificationListenerService.SERVICE_INTERFACE;
5089 c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_LISTENERS;
5090 c.bindPermission = android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE;
5091 c.settingsAction = Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS;
5092 c.clientLabel = R.string.notification_listener_binding_label;
5093 return c;
5094 }
5095
5096 @Override
5097 protected IInterface asInterface(IBinder binder) {
5098 return INotificationListener.Stub.asInterface(binder);
5099 }
5100
5101 @Override
Chris Wren51017d02015-12-15 15:34:46 -05005102 protected boolean checkType(IInterface service) {
5103 return service instanceof INotificationListener;
5104 }
5105
5106 @Override
John Spurlock3b98b3f2014-05-01 09:08:48 -04005107 public void onServiceAdded(ManagedServiceInfo info) {
5108 final INotificationListener listener = (INotificationListener) info.service;
Chris Wren333a61c2014-05-28 16:40:57 -04005109 final NotificationRankingUpdate update;
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05005110 synchronized (mNotificationLock) {
Chris Wren333a61c2014-05-28 16:40:57 -04005111 update = makeRankingUpdateLocked(info);
Christoph Studer05ad4822014-05-16 14:16:03 +02005112 }
John Spurlock7340fc82014-04-24 18:50:12 -04005113 try {
Chris Wren333a61c2014-05-28 16:40:57 -04005114 listener.onListenerConnected(update);
John Spurlock7340fc82014-04-24 18:50:12 -04005115 } catch (RemoteException e) {
5116 // we tried
5117 }
5118 }
5119
John Spurlock1fa865f2014-07-21 14:56:39 -04005120 @Override
Julia Reynolds88860ce2017-06-01 16:55:49 -04005121 @GuardedBy("mNotificationLock")
John Spurlock1fa865f2014-07-21 14:56:39 -04005122 protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
Bryce Lee7219ada2016-04-08 10:54:23 -07005123 if (removeDisabledHints(removed)) {
John Spurlockd8afe3c2014-08-01 14:04:07 -04005124 updateListenerHintsLocked();
Christoph Studer0d6ef4b2014-12-02 15:00:48 +01005125 updateEffectsSuppressorLocked();
John Spurlock1fa865f2014-07-21 14:56:39 -04005126 }
Christoph Studerb82bc782014-08-20 14:29:43 +02005127 mLightTrimListeners.remove(removed);
5128 }
5129
Julia Reynolds88860ce2017-06-01 16:55:49 -04005130 @GuardedBy("mNotificationLock")
Christoph Studerb82bc782014-08-20 14:29:43 +02005131 public void setOnNotificationPostedTrimLocked(ManagedServiceInfo info, int trim) {
5132 if (trim == TRIM_LIGHT) {
5133 mLightTrimListeners.add(info);
5134 } else {
5135 mLightTrimListeners.remove(info);
5136 }
5137 }
5138
5139 public int getOnNotificationPostedTrim(ManagedServiceInfo info) {
5140 return mLightTrimListeners.contains(info) ? TRIM_LIGHT : TRIM_FULL;
John Spurlock1fa865f2014-07-21 14:56:39 -04005141 }
5142
John Spurlock7340fc82014-04-24 18:50:12 -04005143 /**
5144 * asynchronously notify all listeners about a new notification
Christoph Studercef37cf2014-07-25 14:18:17 +02005145 *
5146 * <p>
5147 * Also takes care of removing a notification that has been visible to a listener before,
5148 * but isn't anymore.
John Spurlock7340fc82014-04-24 18:50:12 -04005149 */
Julia Reynolds88860ce2017-06-01 16:55:49 -04005150 @GuardedBy("mNotificationLock")
Christoph Studercef37cf2014-07-25 14:18:17 +02005151 public void notifyPostedLocked(StatusBarNotification sbn, StatusBarNotification oldSbn) {
Christoph Studerb82bc782014-08-20 14:29:43 +02005152 // Lazily initialized snapshots of the notification.
Chris Wren47633422016-01-22 09:56:59 -05005153 TrimCache trimCache = new TrimCache(sbn);
Christoph Studerb82bc782014-08-20 14:29:43 +02005154
Julia Reynolds00314d92017-04-14 10:01:24 -04005155 for (final ManagedServiceInfo info : getServices()) {
Christoph Studercef37cf2014-07-25 14:18:17 +02005156 boolean sbnVisible = isVisibleToListener(sbn, info);
5157 boolean oldSbnVisible = oldSbn != null ? isVisibleToListener(oldSbn, info) : false;
5158 // This notification hasn't been and still isn't visible -> ignore.
5159 if (!oldSbnVisible && !sbnVisible) {
Christoph Studer05ad4822014-05-16 14:16:03 +02005160 continue;
Chris Wrenf9536642014-04-17 10:01:54 -04005161 }
Chris Wren333a61c2014-05-28 16:40:57 -04005162 final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
Christoph Studercef37cf2014-07-25 14:18:17 +02005163
5164 // This notification became invisible -> remove the old one.
5165 if (oldSbnVisible && !sbnVisible) {
5166 final StatusBarNotification oldSbnLightClone = oldSbn.cloneLight();
5167 mHandler.post(new Runnable() {
5168 @Override
5169 public void run() {
Julia Reynolds3aa5f1e2016-11-09 15:43:49 -05005170 notifyRemoved(info, oldSbnLightClone, update, REASON_USER_STOPPED);
Christoph Studercef37cf2014-07-25 14:18:17 +02005171 }
5172 });
Christoph Studer05ad4822014-05-16 14:16:03 +02005173 continue;
5174 }
Christoph Studercef37cf2014-07-25 14:18:17 +02005175
Chris Wren47633422016-01-22 09:56:59 -05005176 final StatusBarNotification sbnToPost = trimCache.ForListener(info);
Christoph Studer05ad4822014-05-16 14:16:03 +02005177 mHandler.post(new Runnable() {
5178 @Override
5179 public void run() {
Christoph Studerb82bc782014-08-20 14:29:43 +02005180 notifyPosted(info, sbnToPost, update);
Christoph Studer05ad4822014-05-16 14:16:03 +02005181 }
5182 });
Kenny Guy3a7c4a52014-03-03 18:24:03 +00005183 }
5184 }
Kenny Guy3a7c4a52014-03-03 18:24:03 +00005185
John Spurlock7340fc82014-04-24 18:50:12 -04005186 /**
5187 * asynchronously notify all listeners about a removed notification
5188 */
Julia Reynolds88860ce2017-06-01 16:55:49 -04005189 @GuardedBy("mNotificationLock")
Julia Reynolds3aa5f1e2016-11-09 15:43:49 -05005190 public void notifyRemovedLocked(StatusBarNotification sbn, int reason) {
John Spurlock7340fc82014-04-24 18:50:12 -04005191 // make a copy in case changes are made to the underlying Notification object
5192 // NOTE: this copy is lightweight: it doesn't include heavyweight parts of the
5193 // notification
5194 final StatusBarNotification sbnLight = sbn.cloneLight();
Julia Reynolds00314d92017-04-14 10:01:24 -04005195 for (final ManagedServiceInfo info : getServices()) {
Christoph Studercef37cf2014-07-25 14:18:17 +02005196 if (!isVisibleToListener(sbn, info)) {
Christoph Studer05ad4822014-05-16 14:16:03 +02005197 continue;
Chris Wrenf9536642014-04-17 10:01:54 -04005198 }
Chris Wren333a61c2014-05-28 16:40:57 -04005199 final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
Christoph Studer05ad4822014-05-16 14:16:03 +02005200 mHandler.post(new Runnable() {
5201 @Override
5202 public void run() {
Julia Reynolds3aa5f1e2016-11-09 15:43:49 -05005203 notifyRemoved(info, sbnLight, update, reason);
Christoph Studer05ad4822014-05-16 14:16:03 +02005204 }
5205 });
Chris Wrenf9536642014-04-17 10:01:54 -04005206 }
5207 }
5208
5209 /**
5210 * asynchronously notify all listeners about a reordering of notifications
Chris Wrenf9536642014-04-17 10:01:54 -04005211 */
Julia Reynolds88860ce2017-06-01 16:55:49 -04005212 @GuardedBy("mNotificationLock")
Chris Wren333a61c2014-05-28 16:40:57 -04005213 public void notifyRankingUpdateLocked() {
Julia Reynolds00314d92017-04-14 10:01:24 -04005214 for (final ManagedServiceInfo serviceInfo : getServices()) {
Christoph Studer05ad4822014-05-16 14:16:03 +02005215 if (!serviceInfo.isEnabledForCurrentProfiles()) {
5216 continue;
5217 }
Christoph Studercef37cf2014-07-25 14:18:17 +02005218 final NotificationRankingUpdate update = makeRankingUpdateLocked(serviceInfo);
John Spurlock7340fc82014-04-24 18:50:12 -04005219 mHandler.post(new Runnable() {
5220 @Override
5221 public void run() {
Chris Wren333a61c2014-05-28 16:40:57 -04005222 notifyRankingUpdate(serviceInfo, update);
John Spurlock7340fc82014-04-24 18:50:12 -04005223 }
5224 });
Kenny Guya263e4e2014-03-03 18:24:03 +00005225 }
Kenny Guya263e4e2014-03-03 18:24:03 +00005226 }
Kenny Guya263e4e2014-03-03 18:24:03 +00005227
Julia Reynolds88860ce2017-06-01 16:55:49 -04005228 @GuardedBy("mNotificationLock")
John Spurlockd8afe3c2014-08-01 14:04:07 -04005229 public void notifyListenerHintsChangedLocked(final int hints) {
Julia Reynolds00314d92017-04-14 10:01:24 -04005230 for (final ManagedServiceInfo serviceInfo : getServices()) {
John Spurlock1fa865f2014-07-21 14:56:39 -04005231 if (!serviceInfo.isEnabledForCurrentProfiles()) {
5232 continue;
5233 }
5234 mHandler.post(new Runnable() {
5235 @Override
5236 public void run() {
John Spurlockd8afe3c2014-08-01 14:04:07 -04005237 notifyListenerHintsChanged(serviceInfo, hints);
John Spurlock1fa865f2014-07-21 14:56:39 -04005238 }
5239 });
5240 }
5241 }
5242
Christoph Studer85a384b2014-08-27 20:16:15 +02005243 public void notifyInterruptionFilterChanged(final int interruptionFilter) {
Julia Reynolds00314d92017-04-14 10:01:24 -04005244 for (final ManagedServiceInfo serviceInfo : getServices()) {
Christoph Studer85a384b2014-08-27 20:16:15 +02005245 if (!serviceInfo.isEnabledForCurrentProfiles()) {
5246 continue;
5247 }
5248 mHandler.post(new Runnable() {
5249 @Override
5250 public void run() {
5251 notifyInterruptionFilterChanged(serviceInfo, interruptionFilter);
5252 }
5253 });
5254 }
5255 }
5256
Julia Reynoldsf27d6b22017-04-13 15:48:16 -04005257 protected void notifyNotificationChannelChanged(final String pkg, final UserHandle user,
Julia Reynolds73ed76b2017-04-04 17:04:38 -04005258 final NotificationChannel channel, final int modificationType) {
5259 if (channel == null) {
5260 return;
5261 }
5262 for (final ManagedServiceInfo serviceInfo : getServices()) {
Julia Reynoldsda781472017-04-12 09:41:16 -04005263 if (!serviceInfo.enabledAndUserMatches(UserHandle.getCallingUserId())) {
Julia Reynolds73ed76b2017-04-04 17:04:38 -04005264 continue;
5265 }
Julia Reynolds018aa622017-04-20 11:31:30 -04005266
Julia Reynoldsda781472017-04-12 09:41:16 -04005267 mHandler.post(new Runnable() {
5268 @Override
5269 public void run() {
Julia Reynolds018aa622017-04-20 11:31:30 -04005270 if (hasCompanionDevice(serviceInfo)) {
5271 notifyNotificationChannelChanged(
5272 serviceInfo, pkg, user, channel, modificationType);
5273 }
Julia Reynoldsda781472017-04-12 09:41:16 -04005274 }
5275 });
Julia Reynolds73ed76b2017-04-04 17:04:38 -04005276 }
5277 }
5278
Julia Reynoldsf27d6b22017-04-13 15:48:16 -04005279 protected void notifyNotificationChannelGroupChanged(
5280 final String pkg, final UserHandle user, final NotificationChannelGroup group,
5281 final int modificationType) {
Julia Reynolds73ed76b2017-04-04 17:04:38 -04005282 if (group == null) {
5283 return;
5284 }
5285 for (final ManagedServiceInfo serviceInfo : getServices()) {
Julia Reynoldsda781472017-04-12 09:41:16 -04005286 if (!serviceInfo.enabledAndUserMatches(UserHandle.getCallingUserId())) {
Julia Reynolds73ed76b2017-04-04 17:04:38 -04005287 continue;
5288 }
Julia Reynolds018aa622017-04-20 11:31:30 -04005289
Julia Reynoldsda781472017-04-12 09:41:16 -04005290 mHandler.post(new Runnable() {
5291 @Override
5292 public void run() {
Julia Reynolds018aa622017-04-20 11:31:30 -04005293 if (hasCompanionDevice(serviceInfo)) {
5294 notifyNotificationChannelGroupChanged(
5295 serviceInfo, pkg, user, group, modificationType);
5296 }
Julia Reynoldsda781472017-04-12 09:41:16 -04005297 }
5298 });
Julia Reynolds73ed76b2017-04-04 17:04:38 -04005299 }
5300 }
5301
Christoph Studercef37cf2014-07-25 14:18:17 +02005302 private void notifyPosted(final ManagedServiceInfo info,
Christoph Studer05ad4822014-05-16 14:16:03 +02005303 final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) {
Julia Reynoldsa75c7522017-03-21 17:34:25 -04005304 final INotificationListener listener = (INotificationListener) info.service;
Griff Hazen84a00ea2014-09-02 17:10:47 -07005305 StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
John Spurlock7340fc82014-04-24 18:50:12 -04005306 try {
Griff Hazen84a00ea2014-09-02 17:10:47 -07005307 listener.onNotificationPosted(sbnHolder, rankingUpdate);
John Spurlock7340fc82014-04-24 18:50:12 -04005308 } catch (RemoteException ex) {
5309 Log.e(TAG, "unable to notify listener (posted): " + listener, ex);
5310 }
5311 }
5312
Christoph Studercef37cf2014-07-25 14:18:17 +02005313 private void notifyRemoved(ManagedServiceInfo info, StatusBarNotification sbn,
Julia Reynolds3aa5f1e2016-11-09 15:43:49 -05005314 NotificationRankingUpdate rankingUpdate, int reason) {
John Spurlock7340fc82014-04-24 18:50:12 -04005315 if (!info.enabledAndUserMatches(sbn.getUserId())) {
5316 return;
5317 }
Christoph Studer05ad4822014-05-16 14:16:03 +02005318 final INotificationListener listener = (INotificationListener) info.service;
Griff Hazen84a00ea2014-09-02 17:10:47 -07005319 StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
John Spurlock7340fc82014-04-24 18:50:12 -04005320 try {
Julia Reynolds3aa5f1e2016-11-09 15:43:49 -05005321 listener.onNotificationRemoved(sbnHolder, rankingUpdate, reason);
John Spurlock7340fc82014-04-24 18:50:12 -04005322 } catch (RemoteException ex) {
5323 Log.e(TAG, "unable to notify listener (removed): " + listener, ex);
John Spurlockb408e8e2014-04-23 21:12:45 -04005324 }
Kenny Guya263e4e2014-03-03 18:24:03 +00005325 }
Chris Wrenf9536642014-04-17 10:01:54 -04005326
Christoph Studer05ad4822014-05-16 14:16:03 +02005327 private void notifyRankingUpdate(ManagedServiceInfo info,
5328 NotificationRankingUpdate rankingUpdate) {
5329 final INotificationListener listener = (INotificationListener) info.service;
Chris Wrenf9536642014-04-17 10:01:54 -04005330 try {
Christoph Studer05ad4822014-05-16 14:16:03 +02005331 listener.onNotificationRankingUpdate(rankingUpdate);
Chris Wrenf9536642014-04-17 10:01:54 -04005332 } catch (RemoteException ex) {
5333 Log.e(TAG, "unable to notify listener (ranking update): " + listener, ex);
5334 }
5335 }
John Spurlock1fa865f2014-07-21 14:56:39 -04005336
John Spurlockd8afe3c2014-08-01 14:04:07 -04005337 private void notifyListenerHintsChanged(ManagedServiceInfo info, int hints) {
John Spurlock1fa865f2014-07-21 14:56:39 -04005338 final INotificationListener listener = (INotificationListener) info.service;
5339 try {
John Spurlockd8afe3c2014-08-01 14:04:07 -04005340 listener.onListenerHintsChanged(hints);
John Spurlock1fa865f2014-07-21 14:56:39 -04005341 } catch (RemoteException ex) {
John Spurlockd8afe3c2014-08-01 14:04:07 -04005342 Log.e(TAG, "unable to notify listener (listener hints): " + listener, ex);
John Spurlock1fa865f2014-07-21 14:56:39 -04005343 }
5344 }
Justin Koh38156c52014-06-04 13:57:49 -07005345
Christoph Studer85a384b2014-08-27 20:16:15 +02005346 private void notifyInterruptionFilterChanged(ManagedServiceInfo info,
5347 int interruptionFilter) {
5348 final INotificationListener listener = (INotificationListener) info.service;
5349 try {
5350 listener.onInterruptionFilterChanged(interruptionFilter);
5351 } catch (RemoteException ex) {
5352 Log.e(TAG, "unable to notify listener (interruption filter): " + listener, ex);
5353 }
5354 }
5355
Julia Reynolds73ed76b2017-04-04 17:04:38 -04005356 void notifyNotificationChannelChanged(ManagedServiceInfo info,
Julia Reynoldsf27d6b22017-04-13 15:48:16 -04005357 final String pkg, final UserHandle user, final NotificationChannel channel,
Julia Reynolds73ed76b2017-04-04 17:04:38 -04005358 final int modificationType) {
5359 final INotificationListener listener = (INotificationListener) info.service;
5360 try {
Julia Reynoldsf27d6b22017-04-13 15:48:16 -04005361 listener.onNotificationChannelModification(pkg, user, channel, modificationType);
Julia Reynolds73ed76b2017-04-04 17:04:38 -04005362 } catch (RemoteException ex) {
5363 Log.e(TAG, "unable to notify listener (channel changed): " + listener, ex);
5364 }
5365 }
5366
5367 private void notifyNotificationChannelGroupChanged(ManagedServiceInfo info,
Julia Reynoldsf27d6b22017-04-13 15:48:16 -04005368 final String pkg, final UserHandle user, final NotificationChannelGroup group,
Julia Reynolds73ed76b2017-04-04 17:04:38 -04005369 final int modificationType) {
5370 final INotificationListener listener = (INotificationListener) info.service;
5371 try {
Julia Reynoldsf27d6b22017-04-13 15:48:16 -04005372 listener.onNotificationChannelGroupModification(pkg, user, group, modificationType);
Julia Reynolds73ed76b2017-04-04 17:04:38 -04005373 } catch (RemoteException ex) {
5374 Log.e(TAG, "unable to notify listener (channel group changed): " + listener, ex);
5375 }
5376 }
5377
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05005378 public boolean isListenerPackage(String packageName) {
Justin Koh38156c52014-06-04 13:57:49 -07005379 if (packageName == null) {
5380 return false;
5381 }
5382 // TODO: clean up locking object later
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05005383 synchronized (mNotificationLock) {
Julia Reynolds00314d92017-04-14 10:01:24 -04005384 for (final ManagedServiceInfo serviceInfo : getServices()) {
Justin Koh38156c52014-06-04 13:57:49 -07005385 if (packageName.equals(serviceInfo.component.getPackageName())) {
5386 return true;
5387 }
5388 }
5389 }
5390 return false;
5391 }
Kenny Guya263e4e2014-03-03 18:24:03 +00005392 }
John Spurlock25e2d242014-06-27 13:58:23 -04005393
5394 public static final class DumpFilter {
Dan Sandlera1770312015-07-10 13:59:29 -04005395 public boolean filtered = false;
John Spurlock25e2d242014-06-27 13:58:23 -04005396 public String pkgFilter;
John Spurlock50806fc2014-07-15 10:22:02 -04005397 public boolean zen;
Chris Wrene4b38802015-07-07 15:54:19 -04005398 public long since;
5399 public boolean stats;
Dan Sandlera1770312015-07-10 13:59:29 -04005400 public boolean redact = true;
Julia Reynoldsc9842c12017-02-07 12:46:41 -05005401 public boolean proto = false;
John Spurlock25e2d242014-06-27 13:58:23 -04005402
5403 public static DumpFilter parseFromArguments(String[] args) {
Dan Sandlera1770312015-07-10 13:59:29 -04005404 final DumpFilter filter = new DumpFilter();
5405 for (int ai = 0; ai < args.length; ai++) {
5406 final String a = args[ai];
Julia Reynoldsc9842c12017-02-07 12:46:41 -05005407 if ("--proto".equals(args[0])) {
5408 filter.proto = true;
5409 }
Dan Sandlera1770312015-07-10 13:59:29 -04005410 if ("--noredact".equals(a) || "--reveal".equals(a)) {
5411 filter.redact = false;
5412 } else if ("p".equals(a) || "pkg".equals(a) || "--package".equals(a)) {
5413 if (ai < args.length-1) {
5414 ai++;
5415 filter.pkgFilter = args[ai].trim().toLowerCase();
5416 if (filter.pkgFilter.isEmpty()) {
5417 filter.pkgFilter = null;
5418 } else {
5419 filter.filtered = true;
5420 }
5421 }
5422 } else if ("--zen".equals(a) || "zen".equals(a)) {
5423 filter.filtered = true;
5424 filter.zen = true;
5425 } else if ("--stats".equals(a)) {
5426 filter.stats = true;
5427 if (ai < args.length-1) {
5428 ai++;
Tobias Thierer28532d02016-04-21 14:52:10 +01005429 filter.since = Long.parseLong(args[ai]);
Dan Sandlera1770312015-07-10 13:59:29 -04005430 } else {
5431 filter.since = 0;
5432 }
5433 }
John Spurlock25e2d242014-06-27 13:58:23 -04005434 }
Dan Sandlera1770312015-07-10 13:59:29 -04005435 return filter;
John Spurlock25e2d242014-06-27 13:58:23 -04005436 }
5437
5438 public boolean matches(StatusBarNotification sbn) {
Dan Sandlera1770312015-07-10 13:59:29 -04005439 if (!filtered) return true;
5440 return zen ? true : sbn != null
John Spurlock50806fc2014-07-15 10:22:02 -04005441 && (matches(sbn.getPackageName()) || matches(sbn.getOpPkg()));
John Spurlock25e2d242014-06-27 13:58:23 -04005442 }
5443
5444 public boolean matches(ComponentName component) {
Dan Sandlera1770312015-07-10 13:59:29 -04005445 if (!filtered) return true;
5446 return zen ? true : component != null && matches(component.getPackageName());
John Spurlock25e2d242014-06-27 13:58:23 -04005447 }
5448
5449 public boolean matches(String pkg) {
Dan Sandlera1770312015-07-10 13:59:29 -04005450 if (!filtered) return true;
5451 return zen ? true : pkg != null && pkg.toLowerCase().contains(pkgFilter);
John Spurlock50806fc2014-07-15 10:22:02 -04005452 }
5453
5454 @Override
5455 public String toString() {
Chris Wrene4b38802015-07-07 15:54:19 -04005456 return stats ? "stats" : zen ? "zen" : ('\'' + pkgFilter + '\'');
John Spurlock25e2d242014-06-27 13:58:23 -04005457 }
5458 }
Griff Hazen84a00ea2014-09-02 17:10:47 -07005459
5460 /**
5461 * Wrapper for a StatusBarNotification object that allows transfer across a oneway
5462 * binder without sending large amounts of data over a oneway transaction.
5463 */
5464 private static final class StatusBarNotificationHolder
5465 extends IStatusBarNotificationHolder.Stub {
Griff Hazene9aac5f2014-09-05 20:04:09 -07005466 private StatusBarNotification mValue;
Griff Hazen84a00ea2014-09-02 17:10:47 -07005467
5468 public StatusBarNotificationHolder(StatusBarNotification value) {
5469 mValue = value;
5470 }
5471
Griff Hazene9aac5f2014-09-05 20:04:09 -07005472 /** Get the held value and clear it. This function should only be called once per holder */
Griff Hazen84a00ea2014-09-02 17:10:47 -07005473 @Override
5474 public StatusBarNotification get() {
Griff Hazene9aac5f2014-09-05 20:04:09 -07005475 StatusBarNotification value = mValue;
5476 mValue = null;
5477 return value;
Griff Hazen84a00ea2014-09-02 17:10:47 -07005478 }
5479 }
John Spurlock7c74f782015-06-04 13:01:42 -04005480
5481 private final class PolicyAccess {
5482 private static final String SEPARATOR = ":";
5483 private final String[] PERM = {
5484 android.Manifest.permission.ACCESS_NOTIFICATION_POLICY
5485 };
5486
5487 public boolean isPackageGranted(String pkg) {
5488 return pkg != null && getGrantedPackages().contains(pkg);
5489 }
5490
5491 public void put(String pkg, boolean granted) {
5492 if (pkg == null) return;
5493 final ArraySet<String> pkgs = getGrantedPackages();
5494 boolean changed;
5495 if (granted) {
5496 changed = pkgs.add(pkg);
5497 } else {
5498 changed = pkgs.remove(pkg);
5499 }
5500 if (!changed) return;
5501 final String setting = TextUtils.join(SEPARATOR, pkgs);
5502 final int currentUser = ActivityManager.getCurrentUser();
5503 Settings.Secure.putStringForUser(getContext().getContentResolver(),
5504 Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES,
5505 setting,
5506 currentUser);
5507 getContext().sendBroadcastAsUser(new Intent(NotificationManager
5508 .ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED)
5509 .setPackage(pkg)
5510 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), new UserHandle(currentUser), null);
5511 }
5512
5513 public ArraySet<String> getGrantedPackages() {
5514 final ArraySet<String> pkgs = new ArraySet<>();
Julia Reynoldsea6c4482015-08-13 09:01:33 -04005515
5516 long identity = Binder.clearCallingIdentity();
5517 try {
5518 final String setting = Settings.Secure.getStringForUser(
5519 getContext().getContentResolver(),
5520 Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES,
5521 ActivityManager.getCurrentUser());
5522 if (setting != null) {
5523 final String[] tokens = setting.split(SEPARATOR);
5524 for (int i = 0; i < tokens.length; i++) {
5525 String token = tokens[i];
5526 if (token != null) {
Andreas Gampe1ed71f32015-12-11 15:49:07 -08005527 token = token.trim();
Julia Reynoldsea6c4482015-08-13 09:01:33 -04005528 }
5529 if (TextUtils.isEmpty(token)) {
5530 continue;
5531 }
5532 pkgs.add(token);
John Spurlock7c74f782015-06-04 13:01:42 -04005533 }
John Spurlock7c74f782015-06-04 13:01:42 -04005534 }
Julia Reynoldsea6c4482015-08-13 09:01:33 -04005535 } finally {
5536 Binder.restoreCallingIdentity(identity);
John Spurlock7c74f782015-06-04 13:01:42 -04005537 }
5538 return pkgs;
5539 }
5540
5541 public String[] getRequestingPackages() throws RemoteException {
Geoffrey Pitsche75a66e2016-11-22 11:12:11 -05005542 final ParceledListSlice list = mPackageManager
John Spurlock7c74f782015-06-04 13:01:42 -04005543 .getPackagesHoldingPermissions(PERM, 0 /*flags*/,
5544 ActivityManager.getCurrentUser());
5545 final List<PackageInfo> pkgs = list.getList();
5546 if (pkgs == null || pkgs.isEmpty()) return new String[0];
5547 final int N = pkgs.size();
5548 final String[] rt = new String[N];
5549 for (int i = 0; i < N; i++) {
5550 rt[i] = pkgs.get(i).packageName;
5551 }
5552 return rt;
5553 }
5554 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005555}