blob: ea9597e8af8d99fc5673e6462f24d308314afcd4 [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 Reynoldsd71c5a92017-06-30 13:34:01 -040023import static android.content.pm.PackageManager.PERMISSION_GRANTED;
Julia Reynolds73ed76b2017-04-04 17:04:38 -040024import static android.service.notification.NotificationListenerService
25 .NOTIFICATION_CHANNEL_OR_GROUP_ADDED;
26import static android.service.notification.NotificationListenerService
27 .NOTIFICATION_CHANNEL_OR_GROUP_DELETED;
28import static android.service.notification.NotificationListenerService
29 .NOTIFICATION_CHANNEL_OR_GROUP_UPDATED;
Julia Reynolds3aa5f1e2016-11-09 15:43:49 -050030import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
31import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL_ALL;
32import static android.service.notification.NotificationListenerService.REASON_CHANNEL_BANNED;
Julia Reynoldsf619bc52017-03-17 08:32:23 -040033import static android.service.notification.NotificationListenerService.REASON_CANCEL;
34import static android.service.notification.NotificationListenerService.REASON_CANCEL_ALL;
35import static android.service.notification.NotificationListenerService.REASON_CLICK;
36import static android.service.notification.NotificationListenerService.REASON_ERROR;
Julia Reynolds3aa5f1e2016-11-09 15:43:49 -050037import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED;
38import static android.service.notification.NotificationListenerService.REASON_LISTENER_CANCEL;
39import static android.service.notification.NotificationListenerService.REASON_LISTENER_CANCEL_ALL;
40import static android.service.notification.NotificationListenerService.REASON_PACKAGE_BANNED;
41import static android.service.notification.NotificationListenerService.REASON_PACKAGE_CHANGED;
42import static android.service.notification.NotificationListenerService.REASON_PACKAGE_SUSPENDED;
43import static android.service.notification.NotificationListenerService.REASON_PROFILE_TURNED_OFF;
44import static android.service.notification.NotificationListenerService.REASON_SNOOZED;
Julia Reynolds2a128742016-11-28 14:29:25 -050045import static android.service.notification.NotificationListenerService.REASON_TIMEOUT;
Julia Reynolds3aa5f1e2016-11-09 15:43:49 -050046import static android.service.notification.NotificationListenerService.REASON_UNAUTOBUNDLED;
47import static android.service.notification.NotificationListenerService.REASON_USER_STOPPED;
Jason Monk63506742015-12-16 12:06:51 -050048import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_EFFECTS;
Bryce Lee7219ada2016-04-08 10:54:23 -070049import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_NOTIFICATION_EFFECTS;
50import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS;
Julia Reynoldsd5607292016-02-05 15:25:58 -050051import static android.service.notification.NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_OFF;
Julia Reynolds61721582016-01-05 08:35:25 -050052import static android.service.notification.NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_ON;
Christoph Studerb82bc782014-08-20 14:29:43 +020053import static android.service.notification.NotificationListenerService.TRIM_FULL;
54import static android.service.notification.NotificationListenerService.TRIM_LIGHT;
Felipe Lemea1b79bf2016-05-24 13:06:54 -070055
Wale Ogunwaleac2561e2016-11-01 15:43:46 -070056import static android.view.Display.DEFAULT_DISPLAY;
57import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
Jeff Sharkey098d5802012-04-26 17:30:34 -070058import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
svetoslavganov75986cf2009-05-14 22:28:01 -070059
Chris Wren51017d02015-12-15 15:34:46 -050060import android.Manifest;
Julia Reynoldsa78cdff2017-04-26 10:19:25 -040061import android.annotation.NonNull;
Wei Liu97e56662016-03-04 10:52:33 -080062import android.annotation.Nullable;
Dianne Hackborn41203752012-08-31 14:05:51 -070063import android.app.ActivityManager;
Felipe Lemea1b79bf2016-05-24 13:06:54 -070064import android.app.ActivityManagerInternal;
Julia Reynolds2a128742016-11-28 14:29:25 -050065import android.app.AlarmManager;
John Spurlock7340fc82014-04-24 18:50:12 -040066import android.app.AppGlobals;
Daniel Sandler4a900ac2013-01-30 14:04:10 -050067import android.app.AppOpsManager;
Julia Reynoldsa47a27f2015-08-24 08:31:47 -040068import android.app.AutomaticZenRule;
Julia Reynolds59e152e2017-01-25 17:42:53 -050069import android.app.NotificationChannelGroup;
Geoffrey Pitsche75a66e2016-11-22 11:12:11 -050070import android.app.backup.BackupManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080071import android.app.IActivityManager;
72import android.app.INotificationManager;
73import android.app.ITransientNotification;
74import android.app.Notification;
Julia Reynoldsb5e44b72016-08-16 15:00:25 -040075import android.app.NotificationChannel;
John Spurlock1fc476d2015-04-14 16:05:20 -040076import android.app.NotificationManager.Policy;
Geoffrey Pitsche75a66e2016-11-22 11:12:11 -050077import android.app.NotificationManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080078import android.app.PendingIntent;
79import android.app.StatusBarManager;
Amith Yamasanif47e51e2015-04-17 10:02:15 -070080import android.app.usage.UsageEvents;
Amith Yamasanif47e51e2015-04-17 10:02:15 -070081import android.app.usage.UsageStatsManagerInternal;
Julia Reynolds73ed76b2017-04-04 17:04:38 -040082import android.companion.ICompanionDeviceManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080083import android.content.BroadcastReceiver;
Daniel Sandler5feceeb2013-03-22 18:29:23 -070084import android.content.ComponentName;
Dianne Hackborn1dac2772009-06-26 18:16:48 -070085import android.content.ContentResolver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080086import android.content.Context;
87import android.content.Intent;
88import android.content.IntentFilter;
John Spurlock7340fc82014-04-24 18:50:12 -040089import android.content.pm.ApplicationInfo;
Kenny Guy70058402014-10-28 20:45:06 +000090import android.content.pm.IPackageManager;
Daniel Sandler4a900ac2013-01-30 14:04:10 -050091import android.content.pm.PackageInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080092import android.content.pm.PackageManager;
93import android.content.pm.PackageManager.NameNotFoundException;
Christoph Studercee44ba2014-05-20 18:36:43 +020094import android.content.pm.ParceledListSlice;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080095import android.content.res.Resources;
Dianne Hackborn1dac2772009-06-26 18:16:48 -070096import android.database.ContentObserver;
svetoslavganov75986cf2009-05-14 22:28:01 -070097import android.media.AudioManager;
John Spurlockcdb57ae2015-02-11 19:04:11 -050098import android.media.AudioManagerInternal;
Jeff Sharkey098d5802012-04-26 17:30:34 -070099import android.media.IRingtonePlayer;
Marta Białka39c992f2011-03-10 10:27:24 +0100100import android.media.ToneGenerator;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800101import android.net.Uri;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800102import android.os.Binder;
Geoffrey Pitsch86c11e602017-04-17 15:28:40 -0400103import android.os.Build;
John Spurlock2b122f42014-08-27 16:29:47 -0400104import android.os.Bundle;
John Spurlock056c5192014-04-20 21:52:01 -0400105import android.os.Environment;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800106import android.os.Handler;
Chris Wrenf9536642014-04-17 10:01:54 -0400107import android.os.HandlerThread;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800108import android.os.IBinder;
John Spurlock7340fc82014-04-24 18:50:12 -0400109import android.os.IInterface;
Chris Wrenf9536642014-04-17 10:01:54 -0400110import android.os.Looper;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800111import android.os.Message;
Dianne Hackbornd8a43f62009-08-17 23:33:56 -0700112import android.os.Process;
svetoslavganov75986cf2009-05-14 22:28:01 -0700113import android.os.RemoteException;
Julia Reynolds73ed76b2017-04-04 17:04:38 -0400114import android.os.ServiceManager;
Chris Wrenc8673a82016-05-17 17:11:29 -0400115import android.os.SystemClock;
Selim Cinekb5605e52015-02-20 18:21:41 +0100116import android.os.SystemProperties;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700117import android.os.UserHandle;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800118import android.os.Vibrator;
Michael Wright71216972017-01-31 18:33:54 +0000119import android.os.VibrationEffect;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800120import android.provider.Settings;
Julia Reynoldse46bb372016-03-17 11:05:58 -0400121import android.service.notification.Adjustment;
Chris Wren333a61c2014-05-28 16:40:57 -0400122import android.service.notification.Condition;
John Spurlock7340fc82014-04-24 18:50:12 -0400123import android.service.notification.IConditionProvider;
Chris Wren333a61c2014-05-28 16:40:57 -0400124import android.service.notification.INotificationListener;
Griff Hazen84a00ea2014-09-02 17:10:47 -0700125import android.service.notification.IStatusBarNotificationHolder;
Julia Reynolds77b2cc92016-11-08 14:41:09 -0500126import android.service.notification.NotificationAssistantService;
John Spurlock7340fc82014-04-24 18:50:12 -0400127import android.service.notification.NotificationListenerService;
Christoph Studer05ad4822014-05-16 14:16:03 +0200128import android.service.notification.NotificationRankingUpdate;
Julia Reynoldsc9842c12017-02-07 12:46:41 -0500129import android.service.notification.NotificationRecordProto;
130import android.service.notification.NotificationServiceDumpProto;
131import android.service.notification.NotificationServiceProto;
Julia Reynolds22f02b32016-12-01 15:05:13 -0500132import android.service.notification.SnoozeCriterion;
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700133import android.service.notification.StatusBarNotification;
John Spurlock056c5192014-04-20 21:52:01 -0400134import android.service.notification.ZenModeConfig;
Julia Reynolds520df6e2017-02-13 09:05:10 -0500135import android.service.notification.ZenModeProto;
Sailesh Nepale8bde702017-07-21 11:44:04 -0700136import android.telecom.TelecomManager;
John Spurlock32fe4c62014-10-02 12:16:02 -0400137import android.telephony.PhoneStateListener;
Daniel Sandlere96ffb12010-03-11 13:38:06 -0500138import android.telephony.TelephonyManager;
svetoslavganov75986cf2009-05-14 22:28:01 -0700139import android.text.TextUtils;
John Spurlocka4294292014-03-24 18:02:32 -0400140import android.util.ArrayMap;
John Spurlock1fa865f2014-07-21 14:56:39 -0400141import android.util.ArraySet;
Dianne Hackborn39606a02012-07-31 17:54:35 -0700142import android.util.AtomicFile;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800143import android.util.Log;
Andy Stadler110988c2010-12-03 14:29:16 -0800144import android.util.Slog;
Bryce Lee7219ada2016-04-08 10:54:23 -0700145import android.util.SparseArray;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400146import android.util.Xml;
Julia Reynoldsc9842c12017-02-07 12:46:41 -0500147import android.util.proto.ProtoOutputStream;
Svetoslav Ganovaa076532016-08-01 19:16:43 -0700148import android.view.WindowManagerInternal;
svetoslavganov75986cf2009-05-14 22:28:01 -0700149import android.view.accessibility.AccessibilityEvent;
150import android.view.accessibility.AccessibilityManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800151import android.widget.Toast;
Rubin Xu7eadc1b2016-02-01 16:13:45 +0000152
Scott Greenwald9a05b312013-06-28 00:37:54 -0400153import com.android.internal.R;
Julia Reynolds88860ce2017-06-01 16:55:49 -0400154import com.android.internal.annotations.GuardedBy;
Chris Wren93bb8b82016-03-29 14:35:05 -0400155import com.android.internal.annotations.VisibleForTesting;
Chris Wren9eb5e102017-01-26 13:15:06 -0500156import com.android.internal.logging.MetricsLogger;
Julia Reynolds520df6e2017-02-13 09:05:10 -0500157import com.android.internal.logging.nano.MetricsProto;
Chris Wren9eb5e102017-01-26 13:15:06 -0500158import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
Chris Wrend1dbc922015-06-19 17:51:16 -0400159import com.android.internal.statusbar.NotificationVisibility;
Julia Reynolds73ed76b2017-04-04 17:04:38 -0400160import com.android.internal.util.ArrayUtils;
Jeff Sharkeyfe9a53b2017-03-31 14:08:23 -0600161import com.android.internal.util.DumpUtils;
John Spurlock056c5192014-04-20 21:52:01 -0400162import com.android.internal.util.FastXmlSerializer;
Julia Reynoldsa47a27f2015-08-24 08:31:47 -0400163import com.android.internal.util.Preconditions;
Felipe Lemea1b79bf2016-05-24 13:06:54 -0700164import com.android.server.DeviceIdleController;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800165import com.android.server.EventLogTags;
Amith Yamasanif47e51e2015-04-17 10:02:15 -0700166import com.android.server.LocalServices;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800167import com.android.server.SystemService;
168import com.android.server.lights.Light;
169import com.android.server.lights.LightsManager;
John Spurlock7340fc82014-04-24 18:50:12 -0400170import com.android.server.notification.ManagedServices.ManagedServiceInfo;
Svetoslav Ganovaa076532016-08-01 19:16:43 -0700171import com.android.server.policy.PhoneWindowManager;
John Spurlockb408e8e2014-04-23 21:12:45 -0400172import com.android.server.statusbar.StatusBarManagerInternal;
Ruben Brunke24b9a62016-02-16 21:38:24 -0800173import com.android.server.notification.ManagedServices.UserProfiles;
Ruben Brunkdd18a0b2015-12-04 16:16:31 -0800174
John Spurlockb408e8e2014-04-23 21:12:45 -0400175import libcore.io.IoUtils;
Rubin Xu7eadc1b2016-02-01 16:13:45 +0000176
Chris Wrene4b38802015-07-07 15:54:19 -0400177import org.json.JSONException;
178import org.json.JSONObject;
Jeff Sharkey098d5802012-04-26 17:30:34 -0700179import org.xmlpull.v1.XmlPullParser;
180import org.xmlpull.v1.XmlPullParserException;
John Spurlock056c5192014-04-20 21:52:01 -0400181import org.xmlpull.v1.XmlSerializer;
Jeff Sharkey098d5802012-04-26 17:30:34 -0700182
John Spurlock35ef0a62015-05-28 11:24:10 -0400183import java.io.ByteArrayInputStream;
184import java.io.ByteArrayOutputStream;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400185import java.io.File;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800186import java.io.FileDescriptor;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400187import java.io.FileInputStream;
188import java.io.FileNotFoundException;
John Spurlock056c5192014-04-20 21:52:01 -0400189import java.io.FileOutputStream;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400190import java.io.IOException;
John Spurlock35ef0a62015-05-28 11:24:10 -0400191import java.io.InputStream;
192import java.io.OutputStream;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800193import java.io.PrintWriter;
Wojciech Staszkiewicz9e9e2e72015-05-08 14:58:46 +0100194import java.nio.charset.StandardCharsets;
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500195import java.util.ArrayDeque;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800196import java.util.ArrayList;
Michael Wrightbc4d0d92017-03-23 18:57:57 +0000197import java.util.Arrays;
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500198import java.util.Iterator;
John Spurlock7c74f782015-06-04 13:01:42 -0400199import java.util.List;
Chris Wrenacf424a2016-03-15 12:48:55 -0400200import java.util.Map;
Christoph Studer265c1052014-07-23 17:14:33 +0200201import java.util.Map.Entry;
Julia Reynolds4b82f6d2017-01-04 10:47:41 -0500202import java.util.Objects;
Chris Wren51017d02015-12-15 15:34:46 -0500203import java.util.concurrent.TimeUnit;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400204
Daniel Sandlerd0a2f862010-08-03 15:29:31 -0400205/** {@hide} */
Adam Lesinski182f73f2013-12-05 16:48:06 -0800206public class NotificationManagerService extends SystemService {
207 static final String TAG = "NotificationService";
Christoph Studer1f32c652014-11-26 15:32:20 +0100208 static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
Selim Cinek40412492015-12-08 18:03:22 -0800209 public static final boolean ENABLE_CHILD_NOTIFICATIONS
210 = SystemProperties.getBoolean("debug.child_notifs", true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800211
Adam Lesinski182f73f2013-12-05 16:48:06 -0800212 static final int MAX_PACKAGE_NOTIFICATIONS = 50;
Julia Reynolds6ad0aec2017-07-05 08:47:03 -0400213 static final float DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE = 5f;
Joe Onoratobd73d012010-06-04 11:44:54 -0700214
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800215 // message codes
Adam Lesinski182f73f2013-12-05 16:48:06 -0800216 static final int MESSAGE_TIMEOUT = 2;
John Spurlock056c5192014-04-20 21:52:01 -0400217 static final int MESSAGE_SAVE_POLICY_FILE = 3;
Chris Wren51017d02015-12-15 15:34:46 -0500218 static final int MESSAGE_SEND_RANKING_UPDATE = 4;
219 static final int MESSAGE_LISTENER_HINTS_CHANGED = 5;
220 static final int MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED = 6;
221
222 // ranking thread messages
223 private static final int MESSAGE_RECONSIDER_RANKING = 1000;
224 private static final int MESSAGE_RANKING_SORT = 1001;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800225
Svetoslav Ganovaa076532016-08-01 19:16:43 -0700226 static final int LONG_DELAY = PhoneWindowManager.TOAST_WINDOW_TIMEOUT;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800227 static final int SHORT_DELAY = 2000; // 2 seconds
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800228
Adam Lesinski182f73f2013-12-05 16:48:06 -0800229 static final long[] DEFAULT_VIBRATE_PATTERN = {0, 250, 250, 250};
Christoph Studer265c1052014-07-23 17:14:33 +0200230
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -0500231 static final long SNOOZE_UNTIL_UNSPECIFIED = -1;
232
Adam Lesinski182f73f2013-12-05 16:48:06 -0800233 static final int VIBRATE_PATTERN_MAXLEN = 8 * 2 + 1; // up to eight bumps
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800234
Adam Lesinski182f73f2013-12-05 16:48:06 -0800235 static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_NOTIFICATION;
Daniel Sandler526fa0e2012-12-04 14:51:50 -0500236
Adam Lesinski182f73f2013-12-05 16:48:06 -0800237 static final boolean ENABLE_BLOCKED_TOASTS = true;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400238
Christoph Studer12aeda82014-09-23 19:08:56 +0200239 // When #matchesCallFilter is called from the ringer, wait at most
240 // 3s to resolve the contacts. This timeout is required since
241 // ContactsProvider might take a long time to start up.
242 //
243 // Return STARRED_CONTACT when the timeout is hit in order to avoid
244 // missed calls in ZEN mode "Important".
245 static final int MATCHES_CALL_FILTER_CONTACTS_TIMEOUT_MS = 3000;
246 static final float MATCHES_CALL_FILTER_TIMEOUT_AFFINITY =
247 ValidateNotificationPeople.STARRED_CONTACT;
248
Christoph Studer265c1052014-07-23 17:14:33 +0200249 /** notification_enqueue status value for a newly enqueued notification. */
250 private static final int EVENTLOG_ENQUEUE_STATUS_NEW = 0;
251
252 /** notification_enqueue status value for an existing notification. */
253 private static final int EVENTLOG_ENQUEUE_STATUS_UPDATE = 1;
254
255 /** notification_enqueue status value for an ignored notification. */
256 private static final int EVENTLOG_ENQUEUE_STATUS_IGNORED = 2;
Chris Wrenc8673a82016-05-17 17:11:29 -0400257 private static final long MIN_PACKAGE_OVERRATE_LOG_INTERVAL = 5000; // milliseconds
Christoph Studer265c1052014-07-23 17:14:33 +0200258
Julia Reynolds4b82f6d2017-01-04 10:47:41 -0500259 private static final long DELAY_FOR_ASSISTANT_TIME = 100;
260
Julia Reynolds2a128742016-11-28 14:29:25 -0500261 private static final String ACTION_NOTIFICATION_TIMEOUT =
262 NotificationManagerService.class.getSimpleName() + ".TIMEOUT";
263 private static final int REQUEST_CODE_TIMEOUT = 1;
264 private static final String SCHEME_TIMEOUT = "timeout";
265 private static final String EXTRA_KEY = "key";
266
Adam Lesinski182f73f2013-12-05 16:48:06 -0800267 private IActivityManager mAm;
Geoffrey Pitsche75a66e2016-11-22 11:12:11 -0500268 private IPackageManager mPackageManager;
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -0500269 private PackageManager mPackageManagerClient;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800270 AudioManager mAudioManager;
John Spurlockcdb57ae2015-02-11 19:04:11 -0500271 AudioManagerInternal mAudioManagerInternal;
Wei Liu97e56662016-03-04 10:52:33 -0800272 @Nullable StatusBarManagerInternal mStatusBar;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800273 Vibrator mVibrator;
Svetoslav Ganovaa076532016-08-01 19:16:43 -0700274 private WindowManagerInternal mWindowManagerInternal;
Julia Reynolds2a128742016-11-28 14:29:25 -0500275 private AlarmManager mAlarmManager;
Julia Reynolds73ed76b2017-04-04 17:04:38 -0400276 private ICompanionDeviceManager mCompanionManager;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800277
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800278 final IBinder mForegroundToken = new Binder();
Chris Wren93bb8b82016-03-29 14:35:05 -0400279 private Handler mHandler;
Chris Wrenf9536642014-04-17 10:01:54 -0400280 private final HandlerThread mRankingThread = new HandlerThread("ranker",
281 Process.THREAD_PRIORITY_BACKGROUND);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800282
Adam Lesinski182f73f2013-12-05 16:48:06 -0800283 private Light mNotificationLight;
284 Light mAttentionLight;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800285
Daniel Sandleredbb3802012-11-13 20:49:47 -0800286 private long[] mFallbackVibrationPattern;
Chris Wren5116a822014-06-04 15:59:50 -0400287 private boolean mUseAttentionLight;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800288 boolean mSystemReady;
Daniel Sandleredbb3802012-11-13 20:49:47 -0800289
John Spurlockd8afe3c2014-08-01 14:04:07 -0400290 private boolean mDisableNotificationEffects;
John Spurlock32fe4c62014-10-02 12:16:02 -0400291 private int mCallState;
Chris Wren6054e612014-11-25 17:16:46 -0500292 private String mSoundNotificationKey;
293 private String mVibrateNotificationKey;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800294
Bryce Lee7219ada2016-04-08 10:54:23 -0700295 private final SparseArray<ArraySet<ManagedServiceInfo>> mListenersDisablingEffects =
296 new SparseArray<ArraySet<ManagedServiceInfo>>();
297 private List<ComponentName> mEffectsSuppressors = new ArrayList<ComponentName>();
John Spurlockd8afe3c2014-08-01 14:04:07 -0400298 private int mListenerHints; // right now, all hints are global
John Spurlock83104102015-02-12 23:25:12 -0500299 private int mInterruptionFilter = NotificationListenerService.INTERRUPTION_FILTER_UNKNOWN;
John Spurlock1fa865f2014-07-21 14:56:39 -0400300
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500301 // for enabling and disabling notification pulse behavior
Mike Lockwood63b5ad92011-08-30 09:55:30 -0400302 private boolean mScreenOn = true;
Daniel Sandlere96ffb12010-03-11 13:38:06 -0500303 private boolean mInCall = false;
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500304 private boolean mNotificationPulseEnabled;
305
Marta Białka39c992f2011-03-10 10:27:24 +0100306 // for generating notification tones in-call
307 private ToneGenerator mInCallToneGenerator;
308 private final Object mInCallToneGeneratorLock = new Object();
309
Daniel Sandler09a247e2013-02-14 10:24:17 -0500310 // used as a mutex for access to all active notifications & listeners
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -0500311 final Object mNotificationLock = new Object();
Julia Reynolds0839c022017-06-15 15:24:01 -0400312 @GuardedBy("mNotificationLock")
Adam Lesinski182f73f2013-12-05 16:48:06 -0800313 final ArrayList<NotificationRecord> mNotificationList =
Fred Quintana6ecaff12009-09-25 14:23:13 -0700314 new ArrayList<NotificationRecord>();
Julia Reynolds0839c022017-06-15 15:24:01 -0400315 @GuardedBy("mNotificationLock")
John Spurlocka4294292014-03-24 18:02:32 -0400316 final ArrayMap<String, NotificationRecord> mNotificationsByKey =
317 new ArrayMap<String, NotificationRecord>();
Julia Reynolds0839c022017-06-15 15:24:01 -0400318 @GuardedBy("mNotificationLock")
Chris Wren6676dab2016-12-21 18:26:27 -0500319 final ArrayList<NotificationRecord> mEnqueuedNotifications = new ArrayList<>();
Julia Reynolds0839c022017-06-15 15:24:01 -0400320 @GuardedBy("mNotificationLock")
Julia Reynoldseae43fb2016-05-09 12:42:58 -0400321 final ArrayMap<Integer, ArrayMap<String, String>> mAutobundledSummaries = new ArrayMap<>();
Adam Lesinski182f73f2013-12-05 16:48:06 -0800322 final ArrayList<ToastRecord> mToastQueue = new ArrayList<ToastRecord>();
Christoph Studer265c1052014-07-23 17:14:33 +0200323 final ArrayMap<String, NotificationRecord> mSummaryByGroupKey = new ArrayMap<>();
John Spurlock7c74f782015-06-04 13:01:42 -0400324 final PolicyAccess mPolicyAccess = new PolicyAccess();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800325
Chris Wren6054e612014-11-25 17:16:46 -0500326 // The last key in this list owns the hardware.
327 ArrayList<String> mLights = new ArrayList<>();
svetoslavganov75986cf2009-05-14 22:28:01 -0700328
Adam Lesinski182f73f2013-12-05 16:48:06 -0800329 private AppOpsManager mAppOps;
Amith Yamasanif47e51e2015-04-17 10:02:15 -0700330 private UsageStatsManagerInternal mAppUsageStats;
Daniel Sandler4a900ac2013-01-30 14:04:10 -0500331
Griff Hazen9f637d12014-06-10 11:13:51 -0700332 private Archive mArchive;
333
John Spurlock21258a32015-05-27 18:22:55 -0400334 // Persistent storage for notification policy
Daniel Sandler0da673f2012-04-11 12:33:16 -0400335 private AtomicFile mPolicyFile;
John Spurlock21258a32015-05-27 18:22:55 -0400336
Daniel Sandler0da673f2012-04-11 12:33:16 -0400337 private static final int DB_VERSION = 1;
338
John Spurlock21258a32015-05-27 18:22:55 -0400339 private static final String TAG_NOTIFICATION_POLICY = "notification-policy";
Daniel Sandler0da673f2012-04-11 12:33:16 -0400340 private static final String ATTR_VERSION = "version";
341
Chris Wren54bbef42014-07-09 18:37:56 -0400342 private RankingHelper mRankingHelper;
Scott Greenwald9a05b312013-06-28 00:37:54 -0400343
John Spurlockb408e8e2014-04-23 21:12:45 -0400344 private final UserProfiles mUserProfiles = new UserProfiles();
John Spurlock7340fc82014-04-24 18:50:12 -0400345 private NotificationListeners mListeners;
Julia Reynolds77b2cc92016-11-08 14:41:09 -0500346 private NotificationAssistants mNotificationAssistants;
John Spurlock7340fc82014-04-24 18:50:12 -0400347 private ConditionProviders mConditionProviders;
Christoph Studer1c3f81f2014-04-16 15:05:56 +0200348 private NotificationUsageStats mUsageStats;
Christoph Studer546bec82014-03-14 12:17:12 +0100349
John Spurlocke6a7d932014-03-13 12:29:00 -0400350 private static final int MY_UID = Process.myUid();
351 private static final int MY_PID = Process.myPid();
Dianne Hackborn98305522017-05-05 17:53:53 -0700352 private static final IBinder WHITELIST_TOKEN = new Binder();
Chris Wren51017d02015-12-15 15:34:46 -0500353 private RankingHandler mRankingHandler;
Chris Wrenc8673a82016-05-17 17:11:29 -0400354 private long mLastOverRateLogTime;
Chris Wren763a9bb2016-05-31 17:14:12 -0400355 private float mMaxPackageEnqueueRate = DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE;
John Spurlocke6a7d932014-03-13 12:29:00 -0400356
Julia Reynolds72f1cbb2016-09-19 14:57:31 -0400357 private SnoozeHelper mSnoozeHelper;
Julia Reynolds8f488d32016-10-14 10:59:01 -0400358 private GroupHelper mGroupHelper;
Julia Reynolds5f20e9f2017-01-30 08:54:53 -0500359 private boolean mIsTelevision;
Julia Reynolds72f1cbb2016-09-19 14:57:31 -0400360
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500361 private static class Archive {
Griff Hazen9f637d12014-06-10 11:13:51 -0700362 final int mBufferSize;
363 final ArrayDeque<StatusBarNotification> mBuffer;
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500364
Griff Hazen9f637d12014-06-10 11:13:51 -0700365 public Archive(int size) {
366 mBufferSize = size;
367 mBuffer = new ArrayDeque<StatusBarNotification>(mBufferSize);
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500368 }
Jeff Sharkey0c1baf92013-04-03 13:08:52 -0700369
Daniel Sandler5e62e3a2013-04-15 20:57:02 -0400370 public String toString() {
371 final StringBuilder sb = new StringBuilder();
372 final int N = mBuffer.size();
373 sb.append("Archive (");
374 sb.append(N);
375 sb.append(" notification");
376 sb.append((N==1)?")":"s)");
377 return sb.toString();
378 }
379
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500380 public void record(StatusBarNotification nr) {
Griff Hazen9f637d12014-06-10 11:13:51 -0700381 if (mBuffer.size() == mBufferSize) {
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500382 mBuffer.removeFirst();
383 }
Daniel Sandler26b81d52013-05-20 20:56:43 -0400384
385 // We don't want to store the heavy bits of the notification in the archive,
386 // but other clients in the system process might be using the object, so we
387 // store a (lightened) copy.
388 mBuffer.addLast(nr.cloneLight());
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500389 }
390
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500391 public Iterator<StatusBarNotification> descendingIterator() {
392 return mBuffer.descendingIterator();
393 }
Daniel Sandler78d0d252013-02-12 08:14:52 -0500394
395 public StatusBarNotification[] getArray(int count) {
Griff Hazen9f637d12014-06-10 11:13:51 -0700396 if (count == 0) count = mBufferSize;
Daniel Sandler78d0d252013-02-12 08:14:52 -0500397 final StatusBarNotification[] a
398 = new StatusBarNotification[Math.min(count, mBuffer.size())];
399 Iterator<StatusBarNotification> iter = descendingIterator();
400 int i=0;
401 while (iter.hasNext() && i < count) {
402 a[i++] = iter.next();
403 }
404 return a;
405 }
406
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500407 }
408
John Spurlock35ef0a62015-05-28 11:24:10 -0400409 private void readPolicyXml(InputStream stream, boolean forRestore)
410 throws XmlPullParserException, NumberFormatException, IOException {
411 final XmlPullParser parser = Xml.newPullParser();
412 parser.setInput(stream, StandardCharsets.UTF_8.name());
413
Chris Wrenacf424a2016-03-15 12:48:55 -0400414 while (parser.next() != END_DOCUMENT) {
John Spurlock35ef0a62015-05-28 11:24:10 -0400415 mZenModeHelper.readXml(parser, forRestore);
416 mRankingHelper.readXml(parser, forRestore);
417 }
418 }
419
John Spurlock056c5192014-04-20 21:52:01 -0400420 private void loadPolicyFile() {
John Spurlock21258a32015-05-27 18:22:55 -0400421 if (DBG) Slog.d(TAG, "loadPolicyFile");
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -0500422 synchronized (mPolicyFile) {
Daniel Sandler0da673f2012-04-11 12:33:16 -0400423
John Spurlock056c5192014-04-20 21:52:01 -0400424 FileInputStream infile = null;
425 try {
426 infile = mPolicyFile.openRead();
John Spurlock35ef0a62015-05-28 11:24:10 -0400427 readPolicyXml(infile, false /*forRestore*/);
John Spurlock056c5192014-04-20 21:52:01 -0400428 } catch (FileNotFoundException e) {
429 // No data yet
430 } catch (IOException e) {
431 Log.wtf(TAG, "Unable to read notification policy", e);
432 } catch (NumberFormatException e) {
433 Log.wtf(TAG, "Unable to parse notification policy", e);
434 } catch (XmlPullParserException e) {
435 Log.wtf(TAG, "Unable to parse notification policy", e);
436 } finally {
437 IoUtils.closeQuietly(infile);
438 }
439 }
440 }
441
442 public void savePolicyFile() {
443 mHandler.removeMessages(MESSAGE_SAVE_POLICY_FILE);
444 mHandler.sendEmptyMessage(MESSAGE_SAVE_POLICY_FILE);
445 }
446
447 private void handleSavePolicyFile() {
John Spurlock21258a32015-05-27 18:22:55 -0400448 if (DBG) Slog.d(TAG, "handleSavePolicyFile");
John Spurlock056c5192014-04-20 21:52:01 -0400449 synchronized (mPolicyFile) {
450 final FileOutputStream stream;
451 try {
452 stream = mPolicyFile.startWrite();
453 } catch (IOException e) {
454 Slog.w(TAG, "Failed to save policy file", e);
455 return;
456 }
457
458 try {
John Spurlock35ef0a62015-05-28 11:24:10 -0400459 writePolicyXml(stream, false /*forBackup*/);
John Spurlock056c5192014-04-20 21:52:01 -0400460 mPolicyFile.finishWrite(stream);
461 } catch (IOException e) {
462 Slog.w(TAG, "Failed to save policy file, restoring backup", e);
463 mPolicyFile.failWrite(stream);
Daniel Sandler0da673f2012-04-11 12:33:16 -0400464 }
465 }
John Spurlock35ef0a62015-05-28 11:24:10 -0400466 BackupManager.dataChanged(getContext().getPackageName());
467 }
468
469 private void writePolicyXml(OutputStream stream, boolean forBackup) throws IOException {
470 final XmlSerializer out = new FastXmlSerializer();
471 out.setOutput(stream, StandardCharsets.UTF_8.name());
472 out.startDocument(null, true);
473 out.startTag(null, TAG_NOTIFICATION_POLICY);
474 out.attribute(null, ATTR_VERSION, Integer.toString(DB_VERSION));
475 mZenModeHelper.writeXml(out, forBackup);
476 mRankingHelper.writeXml(out, forBackup);
477 out.endTag(null, TAG_NOTIFICATION_POLICY);
478 out.endDocument();
Daniel Sandler0da673f2012-04-11 12:33:16 -0400479 }
480
Chris Wren66189fc2015-06-25 14:04:33 -0400481 /** Use this to check if a package can post a notification or toast. */
482 private boolean checkNotificationOp(String pkg, int uid) {
483 return mAppOps.checkOp(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
Andrei Stingaceanu355b2322016-02-12 16:43:51 +0000484 == AppOpsManager.MODE_ALLOWED && !isPackageSuspendedForUser(pkg, uid);
Chris Wren66189fc2015-06-25 14:04:33 -0400485 }
486
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800487 private static final class ToastRecord
488 {
489 final int pid;
490 final String pkg;
491 final ITransientNotification callback;
492 int duration;
Svetoslav Ganovaa076532016-08-01 19:16:43 -0700493 Binder token;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800494
Svetoslav Ganovaa076532016-08-01 19:16:43 -0700495 ToastRecord(int pid, String pkg, ITransientNotification callback, int duration,
496 Binder token) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800497 this.pid = pid;
498 this.pkg = pkg;
499 this.callback = callback;
500 this.duration = duration;
Svetoslav Ganovaa076532016-08-01 19:16:43 -0700501 this.token = token;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800502 }
503
504 void update(int duration) {
505 this.duration = duration;
506 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800507
John Spurlock25e2d242014-06-27 13:58:23 -0400508 void dump(PrintWriter pw, String prefix, DumpFilter filter) {
509 if (filter != null && !filter.matches(pkg)) return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800510 pw.println(prefix + this);
511 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800512
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800513 @Override
514 public final String toString()
515 {
516 return "ToastRecord{"
517 + Integer.toHexString(System.identityHashCode(this))
518 + " pkg=" + pkg
519 + " callback=" + callback
520 + " duration=" + duration;
521 }
522 }
523
Julia Reynolds080361e2017-07-13 11:23:12 -0400524 @VisibleForTesting
525 final NotificationDelegate mNotificationDelegate = new NotificationDelegate() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800526
Adam Lesinski182f73f2013-12-05 16:48:06 -0800527 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800528 public void onSetDisabled(int status) {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -0500529 synchronized (mNotificationLock) {
John Spurlockd8afe3c2014-08-01 14:04:07 -0400530 mDisableNotificationEffects =
531 (status & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0;
John Spurlock32fe4c62014-10-02 12:16:02 -0400532 if (disableNotificationEffects(null) != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800533 // cancel whatever's going on
534 long identity = Binder.clearCallingIdentity();
535 try {
Adam Lesinski182f73f2013-12-05 16:48:06 -0800536 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
Jeff Sharkey098d5802012-04-26 17:30:34 -0700537 if (player != null) {
538 player.stopAsync();
539 }
540 } catch (RemoteException e) {
541 } finally {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800542 Binder.restoreCallingIdentity(identity);
543 }
544
545 identity = Binder.clearCallingIdentity();
546 try {
547 mVibrator.cancel();
Jeff Sharkey098d5802012-04-26 17:30:34 -0700548 } finally {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800549 Binder.restoreCallingIdentity(identity);
550 }
551 }
552 }
553 }
554
Adam Lesinski182f73f2013-12-05 16:48:06 -0800555 @Override
John Spurlocke6a7d932014-03-13 12:29:00 -0400556 public void onClearAll(int callingUid, int callingPid, int userId) {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -0500557 synchronized (mNotificationLock) {
Julia Reynoldsf619bc52017-03-17 08:32:23 -0400558 cancelAllLocked(callingUid, callingPid, userId, REASON_CANCEL_ALL, null,
Kenny Guya263e4e2014-03-03 18:24:03 +0000559 /*includeCurrentProfiles*/ true);
Adam Lesinskie8240262014-03-26 16:01:00 -0700560 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800561 }
562
Adam Lesinski182f73f2013-12-05 16:48:06 -0800563 @Override
Christoph Studer03b87a22014-04-30 17:33:27 +0200564 public void onNotificationClick(int callingUid, int callingPid, String key) {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -0500565 synchronized (mNotificationLock) {
Christoph Studer03b87a22014-04-30 17:33:27 +0200566 NotificationRecord r = mNotificationsByKey.get(key);
567 if (r == null) {
568 Log.w(TAG, "No notification with key: " + key);
569 return;
570 }
Chris Wrene6ddb8a2015-05-27 15:21:00 -0400571 final long now = System.currentTimeMillis();
Chris Wren9eb5e102017-01-26 13:15:06 -0500572 MetricsLogger.action(r.getLogMaker(now)
573 .setCategory(MetricsEvent.NOTIFICATION_ITEM)
574 .setType(MetricsEvent.TYPE_ACTION));
Chris Wrene6ddb8a2015-05-27 15:21:00 -0400575 EventLogTags.writeNotificationClicked(key,
576 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
577
Christoph Studer03b87a22014-04-30 17:33:27 +0200578 StatusBarNotification sbn = r.sbn;
579 cancelNotification(callingUid, callingPid, sbn.getPackageName(), sbn.getTag(),
580 sbn.getId(), Notification.FLAG_AUTO_CANCEL,
581 Notification.FLAG_FOREGROUND_SERVICE, false, r.getUserId(),
Julia Reynoldsf619bc52017-03-17 08:32:23 -0400582 REASON_CLICK, null);
Christoph Studer03b87a22014-04-30 17:33:27 +0200583 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800584 }
585
Adam Lesinski182f73f2013-12-05 16:48:06 -0800586 @Override
Christoph Studer4da84cd2014-10-21 17:24:20 +0200587 public void onNotificationActionClick(int callingUid, int callingPid, String key,
588 int actionIndex) {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -0500589 synchronized (mNotificationLock) {
Christoph Studer4da84cd2014-10-21 17:24:20 +0200590 NotificationRecord r = mNotificationsByKey.get(key);
591 if (r == null) {
592 Log.w(TAG, "No notification with key: " + key);
593 return;
594 }
Chris Wrene6ddb8a2015-05-27 15:21:00 -0400595 final long now = System.currentTimeMillis();
Chris Wren9eb5e102017-01-26 13:15:06 -0500596 MetricsLogger.action(r.getLogMaker(now)
597 .setCategory(MetricsEvent.NOTIFICATION_ITEM_ACTION)
598 .setType(MetricsEvent.TYPE_ACTION)
599 .setSubtype(actionIndex));
Chris Wrene6ddb8a2015-05-27 15:21:00 -0400600 EventLogTags.writeNotificationActionClicked(key, actionIndex,
601 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
Christoph Studer4da84cd2014-10-21 17:24:20 +0200602 // TODO: Log action click via UsageStats.
603 }
604 }
605
606 @Override
John Spurlocke6a7d932014-03-13 12:29:00 -0400607 public void onNotificationClear(int callingUid, int callingPid,
608 String pkg, String tag, int id, int userId) {
609 cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
Kenny Guy3a7c4a52014-03-03 18:24:03 +0000610 Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
Julia Reynoldsf619bc52017-03-17 08:32:23 -0400611 true, userId, REASON_CANCEL, null);
Daniel Sandler0f0b11c2010-08-04 15:54:58 -0400612 }
613
Adam Lesinski182f73f2013-12-05 16:48:06 -0800614 @Override
Chris Wrenb659c4f2015-06-25 17:12:27 -0400615 public void onPanelRevealed(boolean clearEffects, int items) {
Chris Wren9eb5e102017-01-26 13:15:06 -0500616 MetricsLogger.visible(getContext(), MetricsEvent.NOTIFICATION_PANEL);
Chris Wren621933f2017-06-14 15:59:03 -0400617 MetricsLogger.histogram(getContext(), "note_load", items);
Chris Wrenb659c4f2015-06-25 17:12:27 -0400618 EventLogTags.writeNotificationPanelRevealed(items);
Christoph Studer1f32c652014-11-26 15:32:20 +0100619 if (clearEffects) {
620 clearEffects();
621 }
622 }
623
624 @Override
625 public void onPanelHidden() {
Chris Wren9eb5e102017-01-26 13:15:06 -0500626 MetricsLogger.hidden(getContext(), MetricsEvent.NOTIFICATION_PANEL);
Christoph Studer1f32c652014-11-26 15:32:20 +0100627 EventLogTags.writeNotificationPanelHidden();
628 }
629
630 @Override
631 public void clearEffects() {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -0500632 synchronized (mNotificationLock) {
Christoph Studer1f32c652014-11-26 15:32:20 +0100633 if (DBG) Slog.d(TAG, "clearEffects");
Chris Wren93bb8b82016-03-29 14:35:05 -0400634 clearSoundLocked();
635 clearVibrateLocked();
636 clearLightsLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800637 }
638 }
Joe Onorato005847b2010-06-04 16:08:02 -0400639
Adam Lesinski182f73f2013-12-05 16:48:06 -0800640 @Override
John Spurlocke6a7d932014-03-13 12:29:00 -0400641 public void onNotificationError(int callingUid, int callingPid, String pkg, String tag, int id,
Kenny Guy3a7c4a52014-03-03 18:24:03 +0000642 int uid, int initialPid, String message, int userId) {
Daniel Sandlerd0a2f862010-08-03 15:29:31 -0400643 Slog.d(TAG, "onNotification error pkg=" + pkg + " tag=" + tag + " id=" + id
644 + "; will crashApplication(uid=" + uid + ", pid=" + initialPid + ")");
John Spurlocke6a7d932014-03-13 12:29:00 -0400645 cancelNotification(callingUid, callingPid, pkg, tag, id, 0, 0, false, userId,
Julia Reynoldsf619bc52017-03-17 08:32:23 -0400646 REASON_ERROR, null);
Dianne Hackborn9d39d0c2010-06-24 15:57:42 -0700647 long ident = Binder.clearCallingIdentity();
648 try {
Christopher Tate8aa8fe12017-01-20 17:50:32 -0800649 ActivityManager.getService().crashApplication(uid, initialPid, pkg, -1,
Dianne Hackborn9d39d0c2010-06-24 15:57:42 -0700650 "Bad notification posted from package " + pkg
651 + ": " + message);
652 } catch (RemoteException e) {
653 }
654 Binder.restoreCallingIdentity(ident);
Joe Onorato005847b2010-06-04 16:08:02 -0400655 }
John Spurlocke677d712014-02-13 12:52:19 -0500656
657 @Override
Chris Wrend1dbc922015-06-19 17:51:16 -0400658 public void onNotificationVisibilityChanged(NotificationVisibility[] newlyVisibleKeys,
659 NotificationVisibility[] noLongerVisibleKeys) {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -0500660 synchronized (mNotificationLock) {
Chris Wrend1dbc922015-06-19 17:51:16 -0400661 for (NotificationVisibility nv : newlyVisibleKeys) {
662 NotificationRecord r = mNotificationsByKey.get(nv.key);
Christoph Studerffeb0c32014-05-07 22:23:56 +0200663 if (r == null) continue;
Chris Wrend1dbc922015-06-19 17:51:16 -0400664 r.setVisibility(true, nv.rank);
665 nv.recycle();
Christoph Studerffeb0c32014-05-07 22:23:56 +0200666 }
667 // Note that we might receive this event after notifications
668 // have already left the system, e.g. after dismissing from the
669 // shade. Hence not finding notifications in
670 // mNotificationsByKey is not an exceptional condition.
Chris Wrend1dbc922015-06-19 17:51:16 -0400671 for (NotificationVisibility nv : noLongerVisibleKeys) {
672 NotificationRecord r = mNotificationsByKey.get(nv.key);
Christoph Studerffeb0c32014-05-07 22:23:56 +0200673 if (r == null) continue;
Chris Wrend1dbc922015-06-19 17:51:16 -0400674 r.setVisibility(false, nv.rank);
675 nv.recycle();
Christoph Studerffeb0c32014-05-07 22:23:56 +0200676 }
677 }
Christoph Studer92b389d2014-04-01 18:44:40 +0200678 }
Chris Wren78403d72014-07-28 10:23:24 +0100679
680 @Override
681 public void onNotificationExpansionChanged(String key,
682 boolean userAction, boolean expanded) {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -0500683 synchronized (mNotificationLock) {
Chris Wren78403d72014-07-28 10:23:24 +0100684 NotificationRecord r = mNotificationsByKey.get(key);
685 if (r != null) {
686 r.stats.onExpansionChanged(userAction, expanded);
Chris Wrene6ddb8a2015-05-27 15:21:00 -0400687 final long now = System.currentTimeMillis();
Chris Wren9eb5e102017-01-26 13:15:06 -0500688 MetricsLogger.action(r.getLogMaker(now)
689 .setCategory(MetricsEvent.NOTIFICATION_ITEM)
690 .setType(MetricsEvent.TYPE_DETAIL));
Chris Wrene6ddb8a2015-05-27 15:21:00 -0400691 EventLogTags.writeNotificationExpansion(key,
692 userAction ? 1 : 0, expanded ? 1 : 0,
693 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
Chris Wren78403d72014-07-28 10:23:24 +0100694 }
695 }
696 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800697 };
698
Julia Reynolds88860ce2017-06-01 16:55:49 -0400699 @GuardedBy("mNotificationLock")
Chris Wren93bb8b82016-03-29 14:35:05 -0400700 private void clearSoundLocked() {
701 mSoundNotificationKey = null;
702 long identity = Binder.clearCallingIdentity();
703 try {
704 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
705 if (player != null) {
706 player.stopAsync();
707 }
708 } catch (RemoteException e) {
709 } finally {
710 Binder.restoreCallingIdentity(identity);
711 }
712 }
713
Julia Reynolds88860ce2017-06-01 16:55:49 -0400714 @GuardedBy("mNotificationLock")
Chris Wren93bb8b82016-03-29 14:35:05 -0400715 private void clearVibrateLocked() {
716 mVibrateNotificationKey = null;
717 long identity = Binder.clearCallingIdentity();
718 try {
719 mVibrator.cancel();
720 } finally {
721 Binder.restoreCallingIdentity(identity);
722 }
723 }
724
Julia Reynolds88860ce2017-06-01 16:55:49 -0400725 @GuardedBy("mNotificationLock")
Chris Wren93bb8b82016-03-29 14:35:05 -0400726 private void clearLightsLocked() {
727 // light
728 mLights.clear();
729 updateLightsLocked();
730 }
731
Julia Reynolds2a128742016-11-28 14:29:25 -0500732 private final BroadcastReceiver mNotificationTimeoutReceiver = new BroadcastReceiver() {
733 @Override
734 public void onReceive(Context context, Intent intent) {
735 String action = intent.getAction();
736 if (action == null) {
737 return;
738 }
739 if (ACTION_NOTIFICATION_TIMEOUT.equals(action)) {
740 final NotificationRecord record;
741 synchronized (mNotificationLock) {
742 record = findNotificationByKeyLocked(intent.getStringExtra(EXTRA_KEY));
743 }
744 if (record != null) {
745 cancelNotification(record.sbn.getUid(), record.sbn.getInitialPid(),
746 record.sbn.getPackageName(), record.sbn.getTag(),
747 record.sbn.getId(), 0,
748 Notification.FLAG_FOREGROUND_SERVICE, true, record.getUserId(),
749 REASON_TIMEOUT, null);
750 }
751 }
752 }
753 };
754
Kenny Guy70058402014-10-28 20:45:06 +0000755 private final BroadcastReceiver mPackageIntentReceiver = new BroadcastReceiver() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800756 @Override
757 public void onReceive(Context context, Intent intent) {
758 String action = intent.getAction();
Dianne Hackborn29cd7f12015-01-08 10:37:05 -0800759 if (action == null) {
760 return;
761 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800762
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800763 boolean queryRestart = false;
Chris Wrenae9bb572013-05-15 14:50:28 -0400764 boolean queryRemove = false;
Daniel Sandler26ece572012-06-01 15:38:46 -0400765 boolean packageChanged = false;
John Spurlock79f78922013-05-16 09:10:05 -0400766 boolean cancelNotifications = true;
Andrei Stingaceanu0122f6512016-01-22 15:33:03 +0000767 int reason = REASON_PACKAGE_CHANGED;
Chris Wrenf9536642014-04-17 10:01:54 -0400768
Chris Wren3da73022013-05-10 14:41:21 -0400769 if (action.equals(Intent.ACTION_PACKAGE_ADDED)
Chris Wrenae9bb572013-05-15 14:50:28 -0400770 || (queryRemove=action.equals(Intent.ACTION_PACKAGE_REMOVED))
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800771 || action.equals(Intent.ACTION_PACKAGE_RESTARTED)
Daniel Sandler26ece572012-06-01 15:38:46 -0400772 || (packageChanged=action.equals(Intent.ACTION_PACKAGE_CHANGED))
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800773 || (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART))
Andrei Stingaceanu0122f6512016-01-22 15:33:03 +0000774 || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)
775 || action.equals(Intent.ACTION_PACKAGES_SUSPENDED)) {
Kenny Guy70058402014-10-28 20:45:06 +0000776 int changeUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
777 UserHandle.USER_ALL);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800778 String pkgList[] = null;
Julia Reynolds4036e8d2017-01-13 09:50:05 -0500779 int uidList[] = null;
Julia Reynolds6434eb22016-08-08 17:19:26 -0400780 boolean removingPackage = queryRemove &&
781 !intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
782 if (DBG) Slog.i(TAG, "action=" + action + " removing=" + removingPackage);
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800783 if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800784 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
Julia Reynolds4036e8d2017-01-13 09:50:05 -0500785 uidList = intent.getIntArrayExtra(Intent.EXTRA_CHANGED_UID_LIST);
Andrei Stingaceanu0122f6512016-01-22 15:33:03 +0000786 } else if (action.equals(Intent.ACTION_PACKAGES_SUSPENDED)) {
787 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
788 reason = REASON_PACKAGE_SUSPENDED;
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800789 } else if (queryRestart) {
790 pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
Julia Reynolds4036e8d2017-01-13 09:50:05 -0500791 uidList = new int[] {intent.getIntExtra(Intent.EXTRA_UID, -1)};
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800792 } else {
793 Uri uri = intent.getData();
794 if (uri == null) {
795 return;
796 }
797 String pkgName = uri.getSchemeSpecificPart();
798 if (pkgName == null) {
799 return;
800 }
Daniel Sandler26ece572012-06-01 15:38:46 -0400801 if (packageChanged) {
802 // We cancel notifications for packages which have just been disabled
Christopher Tate06e5fed2013-10-09 14:39:15 -0700803 try {
Geoffrey Pitsche75a66e2016-11-22 11:12:11 -0500804 final int enabled = mPackageManager.getApplicationEnabledSetting(
805 pkgName,
Kenny Guy70058402014-10-28 20:45:06 +0000806 changeUserId != UserHandle.USER_ALL ? changeUserId :
Geoffrey Pitsche75a66e2016-11-22 11:12:11 -0500807 UserHandle.USER_SYSTEM);
Christopher Tate06e5fed2013-10-09 14:39:15 -0700808 if (enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED
809 || enabled == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
810 cancelNotifications = false;
811 }
812 } catch (IllegalArgumentException e) {
813 // Package doesn't exist; probably racing with uninstall.
814 // cancelNotifications is already true, so nothing to do here.
815 if (DBG) {
816 Slog.i(TAG, "Exception trying to look up app enabled setting", e);
817 }
Kenny Guy70058402014-10-28 20:45:06 +0000818 } catch (RemoteException e) {
819 // Failed to talk to PackageManagerService Should never happen!
Daniel Sandler26ece572012-06-01 15:38:46 -0400820 }
821 }
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800822 pkgList = new String[]{pkgName};
Julia Reynolds4036e8d2017-01-13 09:50:05 -0500823 uidList = new int[] {intent.getIntExtra(Intent.EXTRA_UID, -1)};
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800824 }
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800825 if (pkgList != null && (pkgList.length > 0)) {
826 for (String pkgName : pkgList) {
John Spurlock79f78922013-05-16 09:10:05 -0400827 if (cancelNotifications) {
Julia Reynoldsb5e44b72016-08-16 15:00:25 -0400828 cancelAllNotificationsInt(MY_UID, MY_PID, pkgName, null, 0, 0,
829 !queryRestart, changeUserId, reason, null);
John Spurlock79f78922013-05-16 09:10:05 -0400830 }
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800831 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800832 }
Julia Reynolds6434eb22016-08-08 17:19:26 -0400833 mListeners.onPackagesChanged(removingPackage, pkgList);
Julia Reynolds77b2cc92016-11-08 14:41:09 -0500834 mNotificationAssistants.onPackagesChanged(removingPackage, pkgList);
Julia Reynolds6434eb22016-08-08 17:19:26 -0400835 mConditionProviders.onPackagesChanged(removingPackage, pkgList);
Julia Reynolds4036e8d2017-01-13 09:50:05 -0500836 mRankingHelper.onPackagesChanged(removingPackage, changeUserId, pkgList, uidList);
837 savePolicyFile();
Kenny Guy70058402014-10-28 20:45:06 +0000838 }
839 }
840 };
841
842 private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
843 @Override
844 public void onReceive(Context context, Intent intent) {
845 String action = intent.getAction();
846
847 if (action.equals(Intent.ACTION_SCREEN_ON)) {
Mike Lockwood63b5ad92011-08-30 09:55:30 -0400848 // Keep track of screen on/off state, but do not turn off the notification light
849 // until user passes through the lock screen or views the notification.
850 mScreenOn = true;
Christoph Studer1f32c652014-11-26 15:32:20 +0100851 updateNotificationPulse();
Mike Lockwood63b5ad92011-08-30 09:55:30 -0400852 } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
853 mScreenOn = false;
Christoph Studer1f32c652014-11-26 15:32:20 +0100854 updateNotificationPulse();
Daniel Sandlere96ffb12010-03-11 13:38:06 -0500855 } else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) {
John Spurlock5d2eeb12014-01-16 10:46:36 -0500856 mInCall = TelephonyManager.EXTRA_STATE_OFFHOOK
857 .equals(intent.getStringExtra(TelephonyManager.EXTRA_STATE));
Daniel Sandlere96ffb12010-03-11 13:38:06 -0500858 updateNotificationPulse();
Marta Białka39c992f2011-03-10 10:27:24 +0100859 synchronized (mInCallToneGeneratorLock) {
860 if (mInCall) {
861 if (mInCallToneGenerator == null) {
862 int relativeToneVolume = getContext().getResources().getInteger(
863 R.integer.config_inCallNotificationVolumeRelative);
864 if (relativeToneVolume < ToneGenerator.MIN_VOLUME
865 || relativeToneVolume > ToneGenerator.MAX_VOLUME) {
866 relativeToneVolume = ToneGenerator.MAX_VOLUME;
867 }
868 try {
869 mInCallToneGenerator = new ToneGenerator(
870 AudioManager.STREAM_VOICE_CALL, relativeToneVolume);
871 } catch (RuntimeException e) {
872 Log.e(TAG, "Error creating local tone generator: " + e);
873 mInCallToneGenerator = null;
874 }
875 }
876 } else {
877 if (mInCallToneGenerator != null) {
878 mInCallToneGenerator.release();
879 mInCallToneGenerator = null;
880 }
881 }
882 }
Dianne Hackborn80a4af22012-08-27 19:18:31 -0700883 } else if (action.equals(Intent.ACTION_USER_STOPPED)) {
884 int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
885 if (userHandle >= 0) {
Julia Reynoldsb5e44b72016-08-16 15:00:25 -0400886 cancelAllNotificationsInt(MY_UID, MY_PID, null, null, 0, 0, true, userHandle,
Julia Reynoldsef37f282016-02-12 09:11:27 -0500887 REASON_USER_STOPPED, null);
Dianne Hackborn80a4af22012-08-27 19:18:31 -0700888 }
Rubin Xue95057a2016-04-01 16:49:25 +0100889 } else if (action.equals(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)) {
Rubin Xu7eadc1b2016-02-01 16:13:45 +0000890 int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
Rubin Xue95057a2016-04-01 16:49:25 +0100891 if (userHandle >= 0) {
Julia Reynoldsb5e44b72016-08-16 15:00:25 -0400892 cancelAllNotificationsInt(MY_UID, MY_PID, null, null, 0, 0, true, userHandle,
Julia Reynoldsef37f282016-02-12 09:11:27 -0500893 REASON_PROFILE_TURNED_OFF, null);
Rubin Xu7eadc1b2016-02-01 16:13:45 +0000894 }
Mike Lockwood63b5ad92011-08-30 09:55:30 -0400895 } else if (action.equals(Intent.ACTION_USER_PRESENT)) {
896 // turn off LED when user passes through lock screen
897 mNotificationLight.turnOff();
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400898 } else if (action.equals(Intent.ACTION_USER_SWITCHED)) {
John Spurlock1b8b22b2015-05-20 09:47:13 -0400899 final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400900 // reload per-user settings
901 mSettingsObserver.update(null);
John Spurlockb408e8e2014-04-23 21:12:45 -0400902 mUserProfiles.updateCache(context);
Christoph Studerb53dfd42014-09-12 14:45:59 +0200903 // Refresh managed services
John Spurlock1b8b22b2015-05-20 09:47:13 -0400904 mConditionProviders.onUserSwitched(user);
905 mListeners.onUserSwitched(user);
Julia Reynolds77b2cc92016-11-08 14:41:09 -0500906 mNotificationAssistants.onUserSwitched(user);
John Spurlock21258a32015-05-27 18:22:55 -0400907 mZenModeHelper.onUserSwitched(user);
Kenny Guy3a7c4a52014-03-03 18:24:03 +0000908 } else if (action.equals(Intent.ACTION_USER_ADDED)) {
John Spurlockb408e8e2014-04-23 21:12:45 -0400909 mUserProfiles.updateCache(context);
John Spurlock21258a32015-05-27 18:22:55 -0400910 } else if (action.equals(Intent.ACTION_USER_REMOVED)) {
911 final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
912 mZenModeHelper.onUserRemoved(user);
Julia Reynolds2e9bf5f2017-05-03 13:23:30 -0400913 mRankingHelper.onUserRemoved(user);
914 savePolicyFile();
Julia Reynoldsa3dcaff2016-02-03 15:04:05 -0500915 } else if (action.equals(Intent.ACTION_USER_UNLOCKED)) {
916 final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
917 mConditionProviders.onUserUnlocked(user);
918 mListeners.onUserUnlocked(user);
Julia Reynolds77b2cc92016-11-08 14:41:09 -0500919 mNotificationAssistants.onUserUnlocked(user);
Julia Reynoldsa3dcaff2016-02-03 15:04:05 -0500920 mZenModeHelper.onUserUnlocked(user);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800921 }
922 }
923 };
924
John Spurlock7c74f782015-06-04 13:01:42 -0400925 private final class SettingsObserver extends ContentObserver {
Chris Wren89aa2262017-05-05 18:05:56 -0400926 private final Uri NOTIFICATION_BADGING_URI
927 = Settings.Secure.getUriFor(Settings.Secure.NOTIFICATION_BADGING);
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400928 private final Uri NOTIFICATION_LIGHT_PULSE_URI
929 = Settings.System.getUriFor(Settings.System.NOTIFICATION_LIGHT_PULSE);
Chris Wren763a9bb2016-05-31 17:14:12 -0400930 private final Uri NOTIFICATION_RATE_LIMIT_URI
931 = Settings.Global.getUriFor(Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE);
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400932
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700933 SettingsObserver(Handler handler) {
934 super(handler);
935 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800936
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700937 void observe() {
Adam Lesinski182f73f2013-12-05 16:48:06 -0800938 ContentResolver resolver = getContext().getContentResolver();
Chris Wren89aa2262017-05-05 18:05:56 -0400939 resolver.registerContentObserver(NOTIFICATION_BADGING_URI,
940 false, this, UserHandle.USER_ALL);
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400941 resolver.registerContentObserver(NOTIFICATION_LIGHT_PULSE_URI,
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700942 false, this, UserHandle.USER_ALL);
Chris Wren763a9bb2016-05-31 17:14:12 -0400943 resolver.registerContentObserver(NOTIFICATION_RATE_LIMIT_URI,
944 false, this, UserHandle.USER_ALL);
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400945 update(null);
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700946 }
947
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400948 @Override public void onChange(boolean selfChange, Uri uri) {
949 update(uri);
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700950 }
951
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400952 public void update(Uri uri) {
Adam Lesinski182f73f2013-12-05 16:48:06 -0800953 ContentResolver resolver = getContext().getContentResolver();
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400954 if (uri == null || NOTIFICATION_LIGHT_PULSE_URI.equals(uri)) {
zhoulei7e376972017-05-17 18:41:25 +0800955 boolean pulseEnabled = Settings.System.getIntForUser(resolver,
956 Settings.System.NOTIFICATION_LIGHT_PULSE, 0, UserHandle.USER_CURRENT) != 0;
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400957 if (mNotificationPulseEnabled != pulseEnabled) {
958 mNotificationPulseEnabled = pulseEnabled;
959 updateNotificationPulse();
960 }
961 }
Chris Wren763a9bb2016-05-31 17:14:12 -0400962 if (uri == null || NOTIFICATION_RATE_LIMIT_URI.equals(uri)) {
963 mMaxPackageEnqueueRate = Settings.Global.getFloat(resolver,
964 Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE, mMaxPackageEnqueueRate);
965 }
Chris Wren89aa2262017-05-05 18:05:56 -0400966 if (uri == null || NOTIFICATION_BADGING_URI.equals(uri)) {
967 mRankingHelper.updateBadgingEnabled();
968 }
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700969 }
970 }
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500971
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400972 private SettingsObserver mSettingsObserver;
John Spurlock056c5192014-04-20 21:52:01 -0400973 private ZenModeHelper mZenModeHelper;
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400974
Daniel Sandleredbb3802012-11-13 20:49:47 -0800975 static long[] getLongArray(Resources r, int resid, int maxlen, long[] def) {
976 int[] ar = r.getIntArray(resid);
977 if (ar == null) {
978 return def;
979 }
980 final int len = ar.length > maxlen ? maxlen : ar.length;
981 long[] out = new long[len];
982 for (int i=0; i<len; i++) {
983 out[i] = ar[i];
984 }
985 return out;
986 }
987
Jeff Brownb880d882014-02-10 19:47:07 -0800988 public NotificationManagerService(Context context) {
989 super(context);
Dianne Hackborn98305522017-05-05 17:53:53 -0700990 Notification.processWhitelistToken = WHITELIST_TOKEN;
Jeff Brownb880d882014-02-10 19:47:07 -0800991 }
992
Geoffrey Pitsche75a66e2016-11-22 11:12:11 -0500993 // TODO - replace these methods with a single VisibleForTesting constructor
Chris Wren93bb8b82016-03-29 14:35:05 -0400994 @VisibleForTesting
995 void setAudioManager(AudioManager audioMananger) {
996 mAudioManager = audioMananger;
997 }
998
999 @VisibleForTesting
1000 void setVibrator(Vibrator vibrator) {
1001 mVibrator = vibrator;
1002 }
1003
1004 @VisibleForTesting
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001005 void setLights(Light light) {
1006 mNotificationLight = light;
1007 mAttentionLight = light;
Julia Reynolds033a4122017-01-31 16:50:38 -05001008 mNotificationPulseEnabled = true;
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001009 }
1010
1011 @VisibleForTesting
1012 void setScreenOn(boolean on) {
1013 mScreenOn = on;
1014 }
1015
1016 @VisibleForTesting
Julia Reynolds080361e2017-07-13 11:23:12 -04001017 int getNotificationRecordCount() {
1018 synchronized (mNotificationLock) {
1019 int count = mNotificationList.size() + mNotificationsByKey.size()
1020 + mSummaryByGroupKey.size() + mEnqueuedNotifications.size();
1021 // subtract duplicates
1022 for (NotificationRecord posted : mNotificationList) {
1023 if (mNotificationsByKey.containsKey(posted.getKey())) {
1024 count--;
1025 }
1026 if (posted.sbn.isGroup() && posted.getNotification().isGroupSummary()) {
1027 count --;
1028 }
1029 }
1030
1031 return count;
1032 }
1033 }
1034
1035 @VisibleForTesting
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001036 void addNotification(NotificationRecord r) {
1037 mNotificationList.add(r);
1038 mNotificationsByKey.put(r.sbn.getKey(), r);
Julia Reynoldsa78cdff2017-04-26 10:19:25 -04001039 if (r.sbn.isGroup()) {
1040 mSummaryByGroupKey.put(r.getGroupKey(), r);
1041 }
1042 }
1043
1044 @VisibleForTesting
1045 void addEnqueuedNotification(NotificationRecord r) {
1046 mEnqueuedNotifications.add(r);
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001047 }
1048
1049 @VisibleForTesting
Chris Wren93bb8b82016-03-29 14:35:05 -04001050 void setSystemReady(boolean systemReady) {
1051 mSystemReady = systemReady;
1052 }
1053
1054 @VisibleForTesting
1055 void setHandler(Handler handler) {
1056 mHandler = handler;
1057 }
1058
Chris Wrend4054312016-06-24 17:07:40 -04001059 @VisibleForTesting
Julia Reynolds0c299d42016-11-15 14:37:04 -05001060 void setFallbackVibrationPattern(long[] vibrationPattern) {
1061 mFallbackVibrationPattern = vibrationPattern;
Chris Wrend4054312016-06-24 17:07:40 -04001062 }
1063
Geoffrey Pitsche75a66e2016-11-22 11:12:11 -05001064 @VisibleForTesting
1065 void setPackageManager(IPackageManager packageManager) {
1066 mPackageManager = packageManager;
1067 }
1068
Julia Reynolds5f20e9f2017-01-30 08:54:53 -05001069 @VisibleForTesting
1070 void setRankingHelper(RankingHelper rankingHelper) {
1071 mRankingHelper = rankingHelper;
1072 }
1073
1074 @VisibleForTesting
1075 void setIsTelevision(boolean isTelevision) {
1076 mIsTelevision = isTelevision;
1077 }
1078
Julia Reynolds5f8e0b882017-06-19 08:16:04 -04001079 @VisibleForTesting
1080 void setUsageStats(NotificationUsageStats us) {
1081 mUsageStats = us;
1082 }
1083
Geoffrey Pitschccbfbc72017-07-18 10:35:22 -04001084 // TODO: All tests should use this init instead of the one-off setters above.
Geoffrey Pitsche75a66e2016-11-22 11:12:11 -05001085 @VisibleForTesting
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05001086 void init(Looper looper, IPackageManager packageManager, PackageManager packageManagerClient,
Julia Reynolds73ed76b2017-04-04 17:04:38 -04001087 LightsManager lightsManager, NotificationListeners notificationListeners,
Geoffrey Pitschd5bcf212017-06-01 15:45:35 -04001088 ICompanionDeviceManager companionManager, SnoozeHelper snoozeHelper,
1089 NotificationUsageStats usageStats) {
Chris Wren54bbef42014-07-09 18:37:56 -04001090 Resources resources = getContext().getResources();
Chris Wren763a9bb2016-05-31 17:14:12 -04001091 mMaxPackageEnqueueRate = Settings.Global.getFloat(getContext().getContentResolver(),
1092 Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE,
1093 DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE);
1094
Sudheer Shankadc589ac2016-11-10 15:30:17 -08001095 mAm = ActivityManager.getService();
Geoffrey Pitsch03533712017-01-05 10:30:07 -05001096 mPackageManager = packageManager;
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05001097 mPackageManagerClient = packageManagerClient;
Adam Lesinski182f73f2013-12-05 16:48:06 -08001098 mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
1099 mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
Amith Yamasanif47e51e2015-04-17 10:02:15 -07001100 mAppUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
Julia Reynolds2a128742016-11-28 14:29:25 -05001101 mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
Julia Reynolds73ed76b2017-04-04 17:04:38 -04001102 mCompanionManager = companionManager;
San Mehat3ee13172010-02-04 20:54:43 -08001103
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05001104 mHandler = new WorkerHandler(looper);
Chris Wrenf9536642014-04-17 10:01:54 -04001105 mRankingThread.start();
Chris Wren54bbef42014-07-09 18:37:56 -04001106 String[] extractorNames;
1107 try {
1108 extractorNames = resources.getStringArray(R.array.config_notificationSignalExtractors);
1109 } catch (Resources.NotFoundException e) {
1110 extractorNames = new String[0];
1111 }
Geoffrey Pitschd5bcf212017-06-01 15:45:35 -04001112 mUsageStats = usageStats;
Chris Wren51017d02015-12-15 15:34:46 -05001113 mRankingHandler = new RankingHandlerWorker(mRankingThread.getLooper());
Chris Wren54bbef42014-07-09 18:37:56 -04001114 mRankingHelper = new RankingHelper(getContext(),
Julia Reynolds85769912016-10-25 09:08:57 -04001115 getContext().getPackageManager(),
Chris Wren51017d02015-12-15 15:34:46 -05001116 mRankingHandler,
Chris Wren5eab2b72015-06-16 13:56:22 -04001117 mUsageStats,
Chris Wren54bbef42014-07-09 18:37:56 -04001118 extractorNames);
John Spurlockb2278d62015-04-07 12:47:12 -04001119 mConditionProviders = new ConditionProviders(getContext(), mHandler, mUserProfiles);
1120 mZenModeHelper = new ZenModeHelper(getContext(), mHandler.getLooper(), mConditionProviders);
John Spurlock1c923a32014-04-27 16:42:29 -04001121 mZenModeHelper.addCallback(new ZenModeHelper.Callback() {
John Spurlock056c5192014-04-20 21:52:01 -04001122 @Override
1123 public void onConfigChanged() {
1124 savePolicyFile();
1125 }
John Spurlockd8afe3c2014-08-01 14:04:07 -04001126
1127 @Override
1128 void onZenModeChanged() {
John Spurlock80774932015-05-07 17:38:50 -04001129 sendRegisteredOnlyBroadcast(NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED);
Jason Monka9927322015-12-13 16:22:37 -05001130 getContext().sendBroadcastAsUser(
Jason Monk63506742015-12-16 12:06:51 -05001131 new Intent(NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED_INTERNAL)
1132 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT),
Jason Monka9927322015-12-13 16:22:37 -05001133 UserHandle.ALL, android.Manifest.permission.MANAGE_NOTIFICATIONS);
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05001134 synchronized (mNotificationLock) {
Christoph Studer85a384b2014-08-27 20:16:15 +02001135 updateInterruptionFilterLocked();
John Spurlockd8afe3c2014-08-01 14:04:07 -04001136 }
1137 }
John Spurlock1fc476d2015-04-14 16:05:20 -04001138
1139 @Override
1140 void onPolicyChanged() {
John Spurlock80774932015-05-07 17:38:50 -04001141 sendRegisteredOnlyBroadcast(NotificationManager.ACTION_NOTIFICATION_POLICY_CHANGED);
1142 }
John Spurlock056c5192014-04-20 21:52:01 -04001143 });
Julia Reynoldsa78cdff2017-04-26 10:19:25 -04001144 mSnoozeHelper = snoozeHelper;
Julia Reynolds8f488d32016-10-14 10:59:01 -04001145 mGroupHelper = new GroupHelper(new GroupHelper.Callback() {
1146 @Override
1147 public void addAutoGroup(String key) {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05001148 synchronized (mNotificationLock) {
Julia Reynolds8f488d32016-10-14 10:59:01 -04001149 addAutogroupKeyLocked(key);
1150 }
Julia Reynolds22f02b32016-12-01 15:05:13 -05001151 mRankingHandler.requestSort(false);
Julia Reynolds8f488d32016-10-14 10:59:01 -04001152 }
1153
1154 @Override
1155 public void removeAutoGroup(String key) {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05001156 synchronized (mNotificationLock) {
Julia Reynolds8f488d32016-10-14 10:59:01 -04001157 removeAutogroupKeyLocked(key);
1158 }
Julia Reynolds22f02b32016-12-01 15:05:13 -05001159 mRankingHandler.requestSort(false);
Julia Reynolds8f488d32016-10-14 10:59:01 -04001160 }
1161
1162 @Override
1163 public void addAutoGroupSummary(int userId, String pkg, String triggeringKey) {
1164 createAutoGroupSummary(userId, pkg, triggeringKey);
1165 }
1166
1167 @Override
1168 public void removeAutoGroupSummary(int userId, String pkg) {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05001169 synchronized (mNotificationLock) {
Julia Reynolds8f488d32016-10-14 10:59:01 -04001170 clearAutogroupSummaryLocked(userId, pkg);
1171 }
1172 }
1173 });
Julia Reynolds72f1cbb2016-09-19 14:57:31 -04001174
John Spurlock056c5192014-04-20 21:52:01 -04001175 final File systemDir = new File(Environment.getDataDirectory(), "system");
1176 mPolicyFile = new AtomicFile(new File(systemDir, "notification_policy.xml"));
Daniel Sandler4a900ac2013-01-30 14:04:10 -05001177
Julia Reynolds5fe2eae2017-05-22 08:45:27 -04001178 loadPolicyFile();
Daniel Sandler0da673f2012-04-11 12:33:16 -04001179
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05001180 // This is a ManagedServices object that keeps track of the listeners.
1181 mListeners = notificationListeners;
Chris Wren0efdb882016-03-01 17:17:47 -05001182
Julia Reynolds77b2cc92016-11-08 14:41:09 -05001183 // This is a MangedServices object that keeps track of the assistant.
1184 mNotificationAssistants = new NotificationAssistants();
Chris Wren0efdb882016-03-01 17:17:47 -05001185
Adam Lesinski182f73f2013-12-05 16:48:06 -08001186 mStatusBar = getLocalService(StatusBarManagerInternal.class);
Wei Liu97e56662016-03-04 10:52:33 -08001187 if (mStatusBar != null) {
1188 mStatusBar.setNotificationDelegate(mNotificationDelegate);
1189 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001190
Geoffrey Pitsch03533712017-01-05 10:30:07 -05001191 mNotificationLight = lightsManager.getLight(LightsManager.LIGHT_ID_NOTIFICATIONS);
1192 mAttentionLight = lightsManager.getLight(LightsManager.LIGHT_ID_ATTENTION);
Mike Lockwood3cb67a32009-11-27 14:25:58 -05001193
Daniel Sandleredbb3802012-11-13 20:49:47 -08001194 mFallbackVibrationPattern = getLongArray(resources,
Scott Greenwald9a05b312013-06-28 00:37:54 -04001195 R.array.config_notificationFallbackVibePattern,
Daniel Sandleredbb3802012-11-13 20:49:47 -08001196 VIBRATE_PATTERN_MAXLEN,
1197 DEFAULT_VIBRATE_PATTERN);
1198
Chris Wren5116a822014-06-04 15:59:50 -04001199 mUseAttentionLight = resources.getBoolean(R.bool.config_useAttentionLight);
1200
Joe Onorato39f5b6a2009-07-23 12:29:19 -04001201 // Don't start allowing notifications until the setup wizard has run once.
1202 // After that, including subsequent boots, init with notifications turned on.
1203 // This works on the first boot because the setup wizard will toggle this
1204 // flag at least once and we'll go back to 0 after that.
Adam Lesinski182f73f2013-12-05 16:48:06 -08001205 if (0 == Settings.Global.getInt(getContext().getContentResolver(),
Jeff Brownbf6f6f92012-09-25 15:03:20 -07001206 Settings.Global.DEVICE_PROVISIONED, 0)) {
John Spurlockd8afe3c2014-08-01 14:04:07 -04001207 mDisableNotificationEffects = true;
Joe Onorato39f5b6a2009-07-23 12:29:19 -04001208 }
John Spurlockb2278d62015-04-07 12:47:12 -04001209 mZenModeHelper.initZenMode();
John Spurlockf3701772015-02-12 13:29:37 -05001210 mInterruptionFilter = mZenModeHelper.getZenModeListenerInterruptionFilter();
Joe Onorato39f5b6a2009-07-23 12:29:19 -04001211
John Spurlockb408e8e2014-04-23 21:12:45 -04001212 mUserProfiles.updateCache(getContext());
John Spurlock32fe4c62014-10-02 12:16:02 -04001213 listenForCallState();
Kenny Guya263e4e2014-03-03 18:24:03 +00001214
Geoffrey Pitschccbfbc72017-07-18 10:35:22 -04001215 mSettingsObserver = new SettingsObserver(mHandler);
1216
1217 mArchive = new Archive(resources.getInteger(
1218 R.integer.config_notificationServiceArchiveSize));
1219
1220 mIsTelevision = mPackageManagerClient.hasSystemFeature(FEATURE_LEANBACK)
1221 || mPackageManagerClient.hasSystemFeature(FEATURE_TELEVISION);
1222 }
1223
1224 @Override
1225 public void onStart() {
1226 SnoozeHelper snoozeHelper = new SnoozeHelper(getContext(), new SnoozeHelper.Callback() {
1227 @Override
1228 public void repost(int userId, NotificationRecord r) {
1229 try {
1230 if (DBG) {
1231 Slog.d(TAG, "Reposting " + r.getKey());
1232 }
1233 enqueueNotificationInternal(r.sbn.getPackageName(), r.sbn.getOpPkg(),
1234 r.sbn.getUid(), r.sbn.getInitialPid(), r.sbn.getTag(), r.sbn.getId(),
1235 r.sbn.getNotification(), userId);
1236 } catch (Exception e) {
1237 Slog.e(TAG, "Cannot un-snooze notification", e);
1238 }
1239 }
1240 }, mUserProfiles);
1241
1242 init(Looper.myLooper(), AppGlobals.getPackageManager(), getContext().getPackageManager(),
1243 getLocalService(LightsManager.class), new NotificationListeners(),
1244 null, snoozeHelper, new NotificationUsageStats(getContext()));
1245
Mike Lockwood35e16bf2010-11-30 19:53:36 -05001246 // register for various Intents
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001247 IntentFilter filter = new IntentFilter();
Mike Lockwoodc22404a2009-12-02 11:15:02 -05001248 filter.addAction(Intent.ACTION_SCREEN_ON);
1249 filter.addAction(Intent.ACTION_SCREEN_OFF);
Daniel Sandlere96ffb12010-03-11 13:38:06 -05001250 filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
Mike Lockwood63b5ad92011-08-30 09:55:30 -04001251 filter.addAction(Intent.ACTION_USER_PRESENT);
Dianne Hackborn80a4af22012-08-27 19:18:31 -07001252 filter.addAction(Intent.ACTION_USER_STOPPED);
Daniel Sandler4b749ef2013-03-18 21:53:04 -04001253 filter.addAction(Intent.ACTION_USER_SWITCHED);
Kenny Guy3a7c4a52014-03-03 18:24:03 +00001254 filter.addAction(Intent.ACTION_USER_ADDED);
John Spurlock21258a32015-05-27 18:22:55 -04001255 filter.addAction(Intent.ACTION_USER_REMOVED);
Julia Reynoldsa3dcaff2016-02-03 15:04:05 -05001256 filter.addAction(Intent.ACTION_USER_UNLOCKED);
Rubin Xue95057a2016-04-01 16:49:25 +01001257 filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001258 getContext().registerReceiver(mIntentReceiver, filter);
Kenny Guy70058402014-10-28 20:45:06 +00001259
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08001260 IntentFilter pkgFilter = new IntentFilter();
Chris Wren3da73022013-05-10 14:41:21 -04001261 pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08001262 pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
Daniel Sandleraac0eb02011-08-06 22:51:56 -04001263 pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08001264 pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
1265 pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
1266 pkgFilter.addDataScheme("package");
Kenny Guy70058402014-10-28 20:45:06 +00001267 getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, pkgFilter, null,
1268 null);
1269
Andrei Stingaceanu0122f6512016-01-22 15:33:03 +00001270 IntentFilter suspendedPkgFilter = new IntentFilter();
1271 suspendedPkgFilter.addAction(Intent.ACTION_PACKAGES_SUSPENDED);
1272 getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL,
1273 suspendedPkgFilter, null, null);
1274
Suchi Amalapurapub56ae202010-02-04 22:51:07 -08001275 IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
Kenny Guy70058402014-10-28 20:45:06 +00001276 getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, sdFilter, null,
1277 null);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001278
Julia Reynolds2a128742016-11-28 14:29:25 -05001279 IntentFilter timeoutFilter = new IntentFilter(ACTION_NOTIFICATION_TIMEOUT);
1280 timeoutFilter.addDataScheme(SCHEME_TIMEOUT);
1281 getContext().registerReceiver(mNotificationTimeoutReceiver, timeoutFilter);
1282
Adam Lesinski182f73f2013-12-05 16:48:06 -08001283 publishBinderService(Context.NOTIFICATION_SERVICE, mService);
1284 publishLocalService(NotificationManagerInternal.class, mInternalService);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001285 }
1286
John Spurlocke7a835b2015-05-13 10:47:05 -04001287 private void sendRegisteredOnlyBroadcast(String action) {
1288 getContext().sendBroadcastAsUser(new Intent(action)
1289 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), UserHandle.ALL, null);
1290 }
1291
Adam Lesinski182f73f2013-12-05 16:48:06 -08001292 @Override
1293 public void onBootPhase(int phase) {
1294 if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
1295 // no beeping until we're basically done booting
1296 mSystemReady = true;
Jeff Sharkey098d5802012-04-26 17:30:34 -07001297
Adam Lesinski182f73f2013-12-05 16:48:06 -08001298 // Grab our optional AudioService
1299 mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
John Spurlockcdb57ae2015-02-11 19:04:11 -05001300 mAudioManagerInternal = getLocalService(AudioManagerInternal.class);
Svetoslav Ganovaa076532016-08-01 19:16:43 -07001301 mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
John Spurlock661f2cf2014-11-17 10:29:10 -05001302 mZenModeHelper.onSystemReady();
Adam Lesinskia6db4ab2014-03-24 12:31:45 -07001303 } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
1304 // This observer will force an update when observe is called, causing us to
1305 // bind to listener services.
1306 mSettingsObserver.observe();
John Spurlockb408e8e2014-04-23 21:12:45 -04001307 mListeners.onBootPhaseAppsCanStart();
Julia Reynolds77b2cc92016-11-08 14:41:09 -05001308 mNotificationAssistants.onBootPhaseAppsCanStart();
John Spurlock7340fc82014-04-24 18:50:12 -04001309 mConditionProviders.onBootPhaseAppsCanStart();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001310 }
1311 }
1312
Julia Reynolds88860ce2017-06-01 16:55:49 -04001313 @GuardedBy("mNotificationLock")
John Spurlockd8afe3c2014-08-01 14:04:07 -04001314 private void updateListenerHintsLocked() {
Bryce Lee7219ada2016-04-08 10:54:23 -07001315 final int hints = calculateHints();
John Spurlockd8afe3c2014-08-01 14:04:07 -04001316 if (hints == mListenerHints) return;
Bryce Lee7219ada2016-04-08 10:54:23 -07001317 ZenLog.traceListenerHintsChanged(mListenerHints, hints, mEffectsSuppressors.size());
John Spurlockd8afe3c2014-08-01 14:04:07 -04001318 mListenerHints = hints;
1319 scheduleListenerHintsChanged(hints);
John Spurlock1fa865f2014-07-21 14:56:39 -04001320 }
1321
Julia Reynolds88860ce2017-06-01 16:55:49 -04001322 @GuardedBy("mNotificationLock")
John Spurlockb4782522014-08-22 14:54:46 -04001323 private void updateEffectsSuppressorLocked() {
Bryce Lee7219ada2016-04-08 10:54:23 -07001324 final long updatedSuppressedEffects = calculateSuppressedEffects();
1325 if (updatedSuppressedEffects == mZenModeHelper.getSuppressedEffects()) return;
1326 final List<ComponentName> suppressors = getSuppressors();
1327 ZenLog.traceEffectsSuppressorChanged(mEffectsSuppressors, suppressors, updatedSuppressedEffects);
1328 mEffectsSuppressors = suppressors;
1329 mZenModeHelper.setSuppressedEffects(updatedSuppressedEffects);
John Spurlocke7a835b2015-05-13 10:47:05 -04001330 sendRegisteredOnlyBroadcast(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED);
John Spurlockb4782522014-08-22 14:54:46 -04001331 }
1332
Julia Reynolds73ed76b2017-04-04 17:04:38 -04001333 private void updateNotificationChannelInt(String pkg, int uid, NotificationChannel channel,
1334 boolean fromListener) {
Julia Reynolds924eed12017-01-19 09:52:07 -05001335 if (channel.getImportance() == NotificationManager.IMPORTANCE_NONE) {
1336 // cancel
1337 cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channel.getId(), 0, 0, true,
1338 UserHandle.getUserId(Binder.getCallingUid()), REASON_CHANNEL_BANNED,
1339 null);
1340 }
Julia Reynolds60315332017-04-04 14:29:07 -04001341 mRankingHelper.updateNotificationChannel(pkg, uid, channel);
Julia Reynolds924eed12017-01-19 09:52:07 -05001342
Julia Reynolds73ed76b2017-04-04 17:04:38 -04001343 final NotificationChannel modifiedChannel =
1344 mRankingHelper.getNotificationChannel(pkg, uid, channel.getId(), false);
1345
1346 if (!fromListener) {
1347 mListeners.notifyNotificationChannelChanged(
Julia Reynoldsf27d6b22017-04-13 15:48:16 -04001348 pkg, UserHandle.getUserHandleForUid(uid),
1349 modifiedChannel, NOTIFICATION_CHANNEL_OR_GROUP_UPDATED);
Julia Reynolds73ed76b2017-04-04 17:04:38 -04001350 }
1351
Julia Reynoldsa917a112017-03-21 11:09:14 -04001352 synchronized (mNotificationLock) {
Julia Reynolds924eed12017-01-19 09:52:07 -05001353 final int N = mNotificationList.size();
1354 for (int i = N - 1; i >= 0; --i) {
1355 NotificationRecord r = mNotificationList.get(i);
Julia Reynoldsa917a112017-03-21 11:09:14 -04001356 if (r.sbn.getPackageName().equals(pkg)
1357 && r.sbn.getUid() == uid
1358 && channel.getId() != null
1359 && channel.getId().equals(r.getChannel().getId())) {
Julia Reynolds73ed76b2017-04-04 17:04:38 -04001360 r.updateNotificationChannel(modifiedChannel);
Julia Reynolds924eed12017-01-19 09:52:07 -05001361 }
1362 }
1363 }
1364 mRankingHandler.requestSort(true);
1365 savePolicyFile();
1366 }
1367
Bryce Lee7219ada2016-04-08 10:54:23 -07001368 private ArrayList<ComponentName> getSuppressors() {
1369 ArrayList<ComponentName> names = new ArrayList<ComponentName>();
1370 for (int i = mListenersDisablingEffects.size() - 1; i >= 0; --i) {
1371 ArraySet<ManagedServiceInfo> serviceInfoList = mListenersDisablingEffects.valueAt(i);
1372
1373 for (ManagedServiceInfo info : serviceInfoList) {
1374 names.add(info.component);
1375 }
1376 }
1377
1378 return names;
1379 }
1380
1381 private boolean removeDisabledHints(ManagedServiceInfo info) {
1382 return removeDisabledHints(info, 0);
1383 }
1384
1385 private boolean removeDisabledHints(ManagedServiceInfo info, int hints) {
1386 boolean removed = false;
1387
1388 for (int i = mListenersDisablingEffects.size() - 1; i >= 0; --i) {
1389 final int hint = mListenersDisablingEffects.keyAt(i);
1390 final ArraySet<ManagedServiceInfo> listeners =
1391 mListenersDisablingEffects.valueAt(i);
1392
1393 if (hints == 0 || (hint & hints) == hint) {
1394 removed = removed || listeners.remove(info);
1395 }
1396 }
1397
1398 return removed;
1399 }
1400
1401 private void addDisabledHints(ManagedServiceInfo info, int hints) {
1402 if ((hints & HINT_HOST_DISABLE_EFFECTS) != 0) {
1403 addDisabledHint(info, HINT_HOST_DISABLE_EFFECTS);
1404 }
1405
1406 if ((hints & HINT_HOST_DISABLE_NOTIFICATION_EFFECTS) != 0) {
1407 addDisabledHint(info, HINT_HOST_DISABLE_NOTIFICATION_EFFECTS);
1408 }
1409
1410 if ((hints & HINT_HOST_DISABLE_CALL_EFFECTS) != 0) {
1411 addDisabledHint(info, HINT_HOST_DISABLE_CALL_EFFECTS);
1412 }
1413 }
1414
1415 private void addDisabledHint(ManagedServiceInfo info, int hint) {
1416 if (mListenersDisablingEffects.indexOfKey(hint) < 0) {
1417 mListenersDisablingEffects.put(hint, new ArraySet<ManagedServiceInfo>());
1418 }
1419
1420 ArraySet<ManagedServiceInfo> hintListeners = mListenersDisablingEffects.get(hint);
1421 hintListeners.add(info);
1422 }
1423
1424 private int calculateHints() {
1425 int hints = 0;
1426 for (int i = mListenersDisablingEffects.size() - 1; i >= 0; --i) {
1427 int hint = mListenersDisablingEffects.keyAt(i);
1428 ArraySet<ManagedServiceInfo> serviceInfoList = mListenersDisablingEffects.valueAt(i);
1429
1430 if (!serviceInfoList.isEmpty()) {
1431 hints |= hint;
1432 }
1433 }
1434
1435 return hints;
1436 }
1437
1438 private long calculateSuppressedEffects() {
1439 int hints = calculateHints();
1440 long suppressedEffects = 0;
1441
1442 if ((hints & HINT_HOST_DISABLE_EFFECTS) != 0) {
1443 suppressedEffects |= ZenModeHelper.SUPPRESSED_EFFECT_ALL;
1444 }
1445
1446 if ((hints & HINT_HOST_DISABLE_NOTIFICATION_EFFECTS) != 0) {
1447 suppressedEffects |= ZenModeHelper.SUPPRESSED_EFFECT_NOTIFICATIONS;
1448 }
1449
1450 if ((hints & HINT_HOST_DISABLE_CALL_EFFECTS) != 0) {
1451 suppressedEffects |= ZenModeHelper.SUPPRESSED_EFFECT_CALLS;
1452 }
1453
1454 return suppressedEffects;
1455 }
1456
Julia Reynolds88860ce2017-06-01 16:55:49 -04001457 @GuardedBy("mNotificationLock")
Christoph Studer85a384b2014-08-27 20:16:15 +02001458 private void updateInterruptionFilterLocked() {
1459 int interruptionFilter = mZenModeHelper.getZenModeListenerInterruptionFilter();
1460 if (interruptionFilter == mInterruptionFilter) return;
1461 mInterruptionFilter = interruptionFilter;
1462 scheduleInterruptionFilterChanged(interruptionFilter);
1463 }
1464
Geoffrey Pitsche75a66e2016-11-22 11:12:11 -05001465 @VisibleForTesting
1466 INotificationManager getBinderService() {
1467 return INotificationManager.Stub.asInterface(mService);
1468 }
1469
Geoffrey Pitsch415e4542017-04-10 13:12:58 -04001470 @VisibleForTesting
1471 NotificationManagerInternal getInternalService() {
1472 return mInternalService;
1473 }
1474
Adam Lesinski182f73f2013-12-05 16:48:06 -08001475 private final IBinder mService = new INotificationManager.Stub() {
1476 // Toasts
1477 // ============================================================================
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001478
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001479 @Override
Adam Lesinski182f73f2013-12-05 16:48:06 -08001480 public void enqueueToast(String pkg, ITransientNotification callback, int duration)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001481 {
Adam Lesinski182f73f2013-12-05 16:48:06 -08001482 if (DBG) {
1483 Slog.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback
1484 + " duration=" + duration);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001485 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001486
1487 if (pkg == null || callback == null) {
1488 Slog.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback);
1489 return ;
1490 }
1491
Geoffrey Pitsch27684152017-05-02 11:41:31 -04001492 final boolean isSystemToast = isCallerSystemOrPhone() || ("android".equals(pkg));
Andrei Stingaceanu355b2322016-02-12 16:43:51 +00001493 final boolean isPackageSuspended =
1494 isPackageSuspendedForUser(pkg, Binder.getCallingUid());
Adam Lesinski182f73f2013-12-05 16:48:06 -08001495
Geoffrey Pitsch2486f892017-05-22 10:53:44 -04001496 if (ENABLE_BLOCKED_TOASTS && !isSystemToast &&
Julia Reynolds5fe2eae2017-05-22 08:45:27 -04001497 (!areNotificationsEnabledForPackage(pkg, Binder.getCallingUid())
1498 || isPackageSuspended)) {
Geoffrey Pitsch2486f892017-05-22 10:53:44 -04001499 Slog.e(TAG, "Suppressing toast from package " + pkg
1500 + (isPackageSuspended
1501 ? " due to package suspended by administrator."
1502 : " by user request."));
1503 return;
Adam Lesinski182f73f2013-12-05 16:48:06 -08001504 }
1505
1506 synchronized (mToastQueue) {
1507 int callingPid = Binder.getCallingPid();
1508 long callingId = Binder.clearCallingIdentity();
1509 try {
1510 ToastRecord record;
1511 int index = indexOfToastLocked(pkg, callback);
1512 // If it's already in the queue, we update it in place, we don't
1513 // move it to the end of the queue.
1514 if (index >= 0) {
1515 record = mToastQueue.get(index);
1516 record.update(duration);
1517 } else {
1518 // Limit the number of toasts that any given package except the android
1519 // package can enqueue. Prevents DOS attacks and deals with leaks.
1520 if (!isSystemToast) {
1521 int count = 0;
1522 final int N = mToastQueue.size();
1523 for (int i=0; i<N; i++) {
1524 final ToastRecord r = mToastQueue.get(i);
1525 if (r.pkg.equals(pkg)) {
1526 count++;
1527 if (count >= MAX_PACKAGE_NOTIFICATIONS) {
1528 Slog.e(TAG, "Package has already posted " + count
1529 + " toasts. Not showing more. Package=" + pkg);
1530 return;
1531 }
1532 }
1533 }
1534 }
1535
Svetoslav Ganovaa076532016-08-01 19:16:43 -07001536 Binder token = new Binder();
Wale Ogunwaleac2561e2016-11-01 15:43:46 -07001537 mWindowManagerInternal.addWindowToken(token, TYPE_TOAST, DEFAULT_DISPLAY);
Svetoslav Ganovaa076532016-08-01 19:16:43 -07001538 record = new ToastRecord(callingPid, pkg, callback, duration, token);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001539 mToastQueue.add(record);
1540 index = mToastQueue.size() - 1;
Svetoslav Ganovaa076532016-08-01 19:16:43 -07001541 keepProcessAliveIfNeededLocked(callingPid);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001542 }
1543 // If it's at index 0, it's the current toast. It doesn't matter if it's
1544 // new or just been updated. Call back and tell it to show itself.
1545 // If the callback fails, this will remove it from the list, so don't
1546 // assume that it's valid after this.
1547 if (index == 0) {
1548 showNextToastLocked();
1549 }
1550 } finally {
1551 Binder.restoreCallingIdentity(callingId);
1552 }
1553 }
1554 }
1555
1556 @Override
1557 public void cancelToast(String pkg, ITransientNotification callback) {
1558 Slog.i(TAG, "cancelToast pkg=" + pkg + " callback=" + callback);
1559
1560 if (pkg == null || callback == null) {
1561 Slog.e(TAG, "Not cancelling notification. pkg=" + pkg + " callback=" + callback);
1562 return ;
1563 }
1564
1565 synchronized (mToastQueue) {
1566 long callingId = Binder.clearCallingIdentity();
1567 try {
1568 int index = indexOfToastLocked(pkg, callback);
1569 if (index >= 0) {
1570 cancelToastLocked(index);
1571 } else {
1572 Slog.w(TAG, "Toast already cancelled. pkg=" + pkg
1573 + " callback=" + callback);
1574 }
1575 } finally {
1576 Binder.restoreCallingIdentity(callingId);
1577 }
1578 }
1579 }
1580
1581 @Override
Christoph Studer8fd7f1e2014-04-11 17:35:05 -04001582 public void enqueueNotificationWithTag(String pkg, String opPkg, String tag, int id,
Julia Reynoldsfea6f7b2017-04-19 13:50:12 -04001583 Notification notification, int userId) throws RemoteException {
Christoph Studer8fd7f1e2014-04-11 17:35:05 -04001584 enqueueNotificationInternal(pkg, opPkg, Binder.getCallingUid(),
Julia Reynoldsfea6f7b2017-04-19 13:50:12 -04001585 Binder.getCallingPid(), tag, id, notification, userId);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001586 }
1587
1588 @Override
1589 public void cancelNotificationWithTag(String pkg, String tag, int id, int userId) {
John Spurlock7340fc82014-04-24 18:50:12 -04001590 checkCallerIsSystemOrSameApp(pkg);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001591 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1592 Binder.getCallingUid(), userId, true, false, "cancelNotificationWithTag", pkg);
Julia Reynoldse46bb372016-03-17 11:05:58 -04001593 // Don't allow client applications to cancel foreground service notis or autobundled
1594 // summaries.
Geoffrey Pitsch27684152017-05-02 11:41:31 -04001595 final int mustNotHaveFlags = isCallingUidSystem() ? 0 :
1596 (Notification.FLAG_FOREGROUND_SERVICE | Notification.FLAG_AUTOGROUP_SUMMARY);
John Spurlocke6a7d932014-03-13 12:29:00 -04001597 cancelNotification(Binder.getCallingUid(), Binder.getCallingPid(), pkg, tag, id, 0,
Geoffrey Pitsch27684152017-05-02 11:41:31 -04001598 mustNotHaveFlags, false, userId, REASON_APP_CANCEL, null);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001599 }
1600
1601 @Override
1602 public void cancelAllNotifications(String pkg, int userId) {
John Spurlock7340fc82014-04-24 18:50:12 -04001603 checkCallerIsSystemOrSameApp(pkg);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001604
1605 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1606 Binder.getCallingUid(), userId, true, false, "cancelAllNotifications", pkg);
1607
1608 // Calling from user space, don't allow the canceling of actively
1609 // running foreground services.
John Spurlocke6a7d932014-03-13 12:29:00 -04001610 cancelAllNotificationsInt(Binder.getCallingUid(), Binder.getCallingPid(),
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001611 pkg, null, 0, Notification.FLAG_FOREGROUND_SERVICE, true, userId,
Julia Reynoldsef37f282016-02-12 09:11:27 -05001612 REASON_APP_CANCEL_ALL, null);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001613 }
1614
1615 @Override
1616 public void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) {
John Spurlock7340fc82014-04-24 18:50:12 -04001617 checkCallerIsSystem();
Adam Lesinski182f73f2013-12-05 16:48:06 -08001618
Chris Wrenacf424a2016-03-15 12:48:55 -04001619 mRankingHelper.setEnabled(pkg, uid, enabled);
Julia Reynolds5fe2eae2017-05-22 08:45:27 -04001620 // Now, cancel any outstanding notifications that are part of a just-disabled app
Julia Reynolds4da79702017-06-01 11:06:10 -04001621 if (!enabled) {
Julia Reynolds5fe2eae2017-05-22 08:45:27 -04001622 cancelAllNotificationsInt(MY_UID, MY_PID, pkg, null, 0, 0, true,
1623 UserHandle.getUserId(uid), REASON_PACKAGE_BANNED, null);
1624 }
Chris Wrenacf424a2016-03-15 12:48:55 -04001625 savePolicyFile();
Adam Lesinski182f73f2013-12-05 16:48:06 -08001626 }
1627
1628 /**
1629 * Use this when you just want to know if notifications are OK for this package.
1630 */
1631 @Override
Julia Reynolds81afbcd2016-02-09 14:54:08 -05001632 public boolean areNotificationsEnabled(String pkg) {
1633 return areNotificationsEnabledForPackage(pkg, Binder.getCallingUid());
1634 }
1635
1636 /**
1637 * Use this when you just want to know if notifications are OK for this package.
1638 */
1639 @Override
Adam Lesinski182f73f2013-12-05 16:48:06 -08001640 public boolean areNotificationsEnabledForPackage(String pkg, int uid) {
Julia Reynolds81afbcd2016-02-09 14:54:08 -05001641 checkCallerIsSystemOrSameApp(pkg);
Julia Reynolds5fe2eae2017-05-22 08:45:27 -04001642
1643 return mRankingHelper.getImportance(pkg, uid) != IMPORTANCE_NONE;
Adam Lesinski182f73f2013-12-05 16:48:06 -08001644 }
1645
Chris Wren54bbef42014-07-09 18:37:56 -04001646 @Override
Julia Reynoldsef37f282016-02-12 09:11:27 -05001647 public int getPackageImportance(String pkg) {
Julia Reynolds81afbcd2016-02-09 14:54:08 -05001648 checkCallerIsSystemOrSameApp(pkg);
Julia Reynoldsef37f282016-02-12 09:11:27 -05001649 return mRankingHelper.getImportance(pkg, Binder.getCallingUid());
Julia Reynolds81afbcd2016-02-09 14:54:08 -05001650 }
1651
1652 @Override
Julia Reynolds924eed12017-01-19 09:52:07 -05001653 public boolean canShowBadge(String pkg, int uid) {
1654 checkCallerIsSystem();
1655 return mRankingHelper.canShowBadge(pkg, uid);
1656 }
1657
1658 @Override
1659 public void setShowBadge(String pkg, int uid, boolean showBadge) {
1660 checkCallerIsSystem();
1661 mRankingHelper.setShowBadge(pkg, uid, showBadge);
1662 savePolicyFile();
1663 }
1664
1665 @Override
Julia Reynolds59e152e2017-01-25 17:42:53 -05001666 public void createNotificationChannelGroups(String pkg,
1667 ParceledListSlice channelGroupList) throws RemoteException {
1668 checkCallerIsSystemOrSameApp(pkg);
1669 List<NotificationChannelGroup> groups = channelGroupList.getList();
1670 final int groupSize = groups.size();
1671 for (int i = 0; i < groupSize; i++) {
1672 final NotificationChannelGroup group = groups.get(i);
1673 Preconditions.checkNotNull(group, "group in list is null");
1674 mRankingHelper.createNotificationChannelGroup(pkg, Binder.getCallingUid(), group,
1675 true /* fromTargetApp */);
Julia Reynoldsf27d6b22017-04-13 15:48:16 -04001676 mListeners.notifyNotificationChannelGroupChanged(pkg,
1677 UserHandle.of(UserHandle.getCallingUserId()), group,
Julia Reynolds73ed76b2017-04-04 17:04:38 -04001678 NOTIFICATION_CHANNEL_OR_GROUP_ADDED);
Julia Reynolds59e152e2017-01-25 17:42:53 -05001679 }
1680 savePolicyFile();
1681 }
1682
Geoffrey Pitsch3560f842017-03-22 16:42:43 -04001683 private void createNotificationChannelsImpl(String pkg, int uid,
1684 ParceledListSlice channelsList) {
Geoffrey Pitsch03533712017-01-05 10:30:07 -05001685 List<NotificationChannel> channels = channelsList.getList();
1686 final int channelsSize = channels.size();
1687 for (int i = 0; i < channelsSize; i++) {
1688 final NotificationChannel channel = channels.get(i);
1689 Preconditions.checkNotNull(channel, "channel in list is null");
Geoffrey Pitsch3560f842017-03-22 16:42:43 -04001690 mRankingHelper.createNotificationChannel(pkg, uid, channel,
Geoffrey Pitsch03533712017-01-05 10:30:07 -05001691 true /* fromTargetApp */);
Julia Reynolds73ed76b2017-04-04 17:04:38 -04001692 mListeners.notifyNotificationChannelChanged(pkg,
Julia Reynoldsf27d6b22017-04-13 15:48:16 -04001693 UserHandle.getUserHandleForUid(uid),
Julia Reynolds73ed76b2017-04-04 17:04:38 -04001694 mRankingHelper.getNotificationChannel(pkg, uid, channel.getId(), false),
1695 NOTIFICATION_CHANNEL_OR_GROUP_ADDED);
Geoffrey Pitsch03533712017-01-05 10:30:07 -05001696 }
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001697 savePolicyFile();
1698 }
1699
1700 @Override
Geoffrey Pitsch3560f842017-03-22 16:42:43 -04001701 public void createNotificationChannels(String pkg,
1702 ParceledListSlice channelsList) throws RemoteException {
1703 checkCallerIsSystemOrSameApp(pkg);
1704 createNotificationChannelsImpl(pkg, Binder.getCallingUid(), channelsList);
1705 }
1706
1707 @Override
1708 public void createNotificationChannelsForPackage(String pkg, int uid,
1709 ParceledListSlice channelsList) throws RemoteException {
1710 checkCallerIsSystem();
1711 createNotificationChannelsImpl(pkg, uid, channelsList);
1712 }
1713
1714 @Override
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001715 public NotificationChannel getNotificationChannel(String pkg, String channelId) {
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001716 checkCallerIsSystemOrSameApp(pkg);
Julia Reynolds4036e8d2017-01-13 09:50:05 -05001717 return mRankingHelper.getNotificationChannel(
1718 pkg, Binder.getCallingUid(), channelId, false /* includeDeleted */);
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001719 }
1720
1721 @Override
1722 public NotificationChannel getNotificationChannelForPackage(String pkg, int uid,
Julia Reynolds4036e8d2017-01-13 09:50:05 -05001723 String channelId, boolean includeDeleted) {
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001724 checkCallerIsSystem();
Julia Reynolds9bfba592017-03-15 14:03:55 -04001725 return mRankingHelper.getNotificationChannel(pkg, uid, channelId, includeDeleted);
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001726 }
1727
1728 @Override
1729 public void deleteNotificationChannel(String pkg, String channelId) {
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001730 checkCallerIsSystemOrSameApp(pkg);
Julia Reynolds73ed76b2017-04-04 17:04:38 -04001731 final int callingUid = Binder.getCallingUid();
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001732 if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(channelId)) {
1733 throw new IllegalArgumentException("Cannot delete default channel");
1734 }
1735 cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channelId, 0, 0, true,
Julia Reynolds73ed76b2017-04-04 17:04:38 -04001736 UserHandle.getUserId(callingUid), REASON_CHANNEL_BANNED, null);
1737 mRankingHelper.deleteNotificationChannel(pkg, callingUid, channelId);
1738 mListeners.notifyNotificationChannelChanged(pkg,
Julia Reynoldsf27d6b22017-04-13 15:48:16 -04001739 UserHandle.getUserHandleForUid(callingUid),
Julia Reynolds73ed76b2017-04-04 17:04:38 -04001740 mRankingHelper.getNotificationChannel(pkg, callingUid, channelId, true),
1741 NOTIFICATION_CHANNEL_OR_GROUP_DELETED);
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001742 savePolicyFile();
1743 }
1744
1745 @Override
Julia Reynolds9bfba592017-03-15 14:03:55 -04001746 public ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroups(
1747 String pkg) {
1748 checkCallerIsSystemOrSameApp(pkg);
1749 return new ParceledListSlice<>(new ArrayList(
1750 mRankingHelper.getNotificationChannelGroups(pkg, Binder.getCallingUid())));
1751 }
1752
1753 @Override
Julia Reynolds73ed76b2017-04-04 17:04:38 -04001754 public void deleteNotificationChannelGroup(String pkg, String groupId) {
Julia Reynolds9bfba592017-03-15 14:03:55 -04001755 checkCallerIsSystemOrSameApp(pkg);
1756
Julia Reynolds73ed76b2017-04-04 17:04:38 -04001757 final int callingUid = Binder.getCallingUid();
1758 NotificationChannelGroup groupToDelete =
1759 mRankingHelper.getNotificationChannelGroup(groupId, pkg, callingUid);
1760 if (groupToDelete != null) {
1761 List<NotificationChannel> deletedChannels =
1762 mRankingHelper.deleteNotificationChannelGroup(pkg, callingUid, groupId);
1763 for (int i = 0; i < deletedChannels.size(); i++) {
1764 final NotificationChannel deletedChannel = deletedChannels.get(i);
1765 cancelAllNotificationsInt(MY_UID, MY_PID, pkg, deletedChannel.getId(), 0, 0,
1766 true,
1767 UserHandle.getUserId(Binder.getCallingUid()), REASON_CHANNEL_BANNED,
1768 null);
Julia Reynoldsf27d6b22017-04-13 15:48:16 -04001769 mListeners.notifyNotificationChannelChanged(pkg,
1770 UserHandle.getUserHandleForUid(callingUid),
1771 deletedChannel,
Julia Reynolds73ed76b2017-04-04 17:04:38 -04001772 NOTIFICATION_CHANNEL_OR_GROUP_DELETED);
1773 }
1774 mListeners.notifyNotificationChannelGroupChanged(
Julia Reynoldsf27d6b22017-04-13 15:48:16 -04001775 pkg, UserHandle.getUserHandleForUid(callingUid), groupToDelete,
1776 NOTIFICATION_CHANNEL_OR_GROUP_DELETED);
Julia Reynolds73ed76b2017-04-04 17:04:38 -04001777 savePolicyFile();
Julia Reynolds9bfba592017-03-15 14:03:55 -04001778 }
Julia Reynolds9bfba592017-03-15 14:03:55 -04001779 }
1780
1781 @Override
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001782 public void updateNotificationChannelForPackage(String pkg, int uid,
1783 NotificationChannel channel) {
Geoffrey Pitsch4dd50062016-12-06 16:41:22 -05001784 enforceSystemOrSystemUI("Caller not system or systemui");
Julia Reynolds924eed12017-01-19 09:52:07 -05001785 Preconditions.checkNotNull(channel);
Julia Reynolds73ed76b2017-04-04 17:04:38 -04001786 updateNotificationChannelInt(pkg, uid, channel, false);
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001787 }
1788
1789 @Override
1790 public ParceledListSlice<NotificationChannel> getNotificationChannelsForPackage(String pkg,
Julia Reynolds4036e8d2017-01-13 09:50:05 -05001791 int uid, boolean includeDeleted) {
Geoffrey Pitschdf44b602017-02-03 13:31:50 -05001792 enforceSystemOrSystemUI("getNotificationChannelsForPackage");
Julia Reynolds4036e8d2017-01-13 09:50:05 -05001793 return mRankingHelper.getNotificationChannels(pkg, uid, includeDeleted);
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001794 }
1795
1796 @Override
Geoffrey Pitschdf44b602017-02-03 13:31:50 -05001797 public int getNumNotificationChannelsForPackage(String pkg, int uid,
1798 boolean includeDeleted) {
1799 enforceSystemOrSystemUI("getNumNotificationChannelsForPackage");
1800 return mRankingHelper.getNotificationChannels(pkg, uid, includeDeleted)
1801 .getList().size();
1802 }
1803
1804 @Override
Julia Reynolds17717f52017-05-09 11:46:06 -04001805 public boolean onlyHasDefaultChannel(String pkg, int uid) {
1806 enforceSystemOrSystemUI("onlyHasDefaultChannel");
1807 return mRankingHelper.onlyHasDefaultChannel(pkg, uid);
1808 }
1809
1810 @Override
Julia Reynolds41103f42017-03-15 11:36:35 -04001811 public int getDeletedChannelCount(String pkg, int uid) {
1812 enforceSystemOrSystemUI("getDeletedChannelCount");
1813 return mRankingHelper.getDeletedChannelCount(pkg, uid);
1814 }
1815
1816 @Override
Julia Reynolds59e152e2017-01-25 17:42:53 -05001817 public ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroupsForPackage(
1818 String pkg, int uid, boolean includeDeleted) {
1819 checkCallerIsSystem();
1820 return mRankingHelper.getNotificationChannelGroups(pkg, uid, includeDeleted);
1821 }
1822
1823 @Override
Geoffrey Pitschdf44b602017-02-03 13:31:50 -05001824 public NotificationChannelGroup getNotificationChannelGroupForPackage(
1825 String groupId, String pkg, int uid) {
1826 enforceSystemOrSystemUI("getNotificationChannelGroupForPackage");
1827 return mRankingHelper.getNotificationChannelGroup(groupId, pkg, uid);
1828 }
1829
1830 @Override
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001831 public ParceledListSlice<NotificationChannel> getNotificationChannels(String pkg) {
1832 checkCallerIsSystemOrSameApp(pkg);
Julia Reynolds4036e8d2017-01-13 09:50:05 -05001833 return mRankingHelper.getNotificationChannels(
1834 pkg, Binder.getCallingUid(), false /* includeDeleted */);
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001835 }
1836
Julia Reynolds4036e8d2017-01-13 09:50:05 -05001837 @Override
Julia Reynolds5355e852017-02-07 14:54:13 -05001838 public void clearData(String packageName, int uid, boolean fromApp) throws RemoteException {
Julia Reynolds4036e8d2017-01-13 09:50:05 -05001839 checkCallerIsSystem();
1840
1841 // Cancel posted notifications
1842 cancelAllNotificationsInt(MY_UID, MY_PID, packageName, null, 0, 0, true,
1843 UserHandle.getUserId(Binder.getCallingUid()), REASON_CHANNEL_BANNED, null);
1844
1845 // Listener & assistant
1846 mListeners.onPackagesChanged(true, new String[] {packageName});
1847 mNotificationAssistants.onPackagesChanged(true, new String[] {packageName});
1848
1849 // Zen
1850 mConditionProviders.onPackagesChanged(true, new String[] {packageName});
1851
1852 // Reset notification preferences
Julia Reynolds5355e852017-02-07 14:54:13 -05001853 if (!fromApp) {
1854 mRankingHelper.onPackagesChanged(true, UserHandle.getCallingUserId(),
1855 new String[]{packageName}, new int[]{uid});
1856 }
Julia Reynolds4036e8d2017-01-13 09:50:05 -05001857
1858 savePolicyFile();
1859 }
1860
1861
Adam Lesinski182f73f2013-12-05 16:48:06 -08001862 /**
1863 * System-only API for getting a list of current (i.e. not cleared) notifications.
1864 *
1865 * Requires ACCESS_NOTIFICATIONS which is signature|system.
Chris Wrenf9536642014-04-17 10:01:54 -04001866 * @returns A list of all the notifications, in natural order.
Adam Lesinski182f73f2013-12-05 16:48:06 -08001867 */
1868 @Override
1869 public StatusBarNotification[] getActiveNotifications(String callingPkg) {
1870 // enforce() will ensure the calling uid has the correct permission
1871 getContext().enforceCallingOrSelfPermission(
1872 android.Manifest.permission.ACCESS_NOTIFICATIONS,
1873 "NotificationManagerService.getActiveNotifications");
1874
1875 StatusBarNotification[] tmp = null;
1876 int uid = Binder.getCallingUid();
1877
1878 // noteOp will check to make sure the callingPkg matches the uid
1879 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
1880 == AppOpsManager.MODE_ALLOWED) {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05001881 synchronized (mNotificationLock) {
Adam Lesinski182f73f2013-12-05 16:48:06 -08001882 tmp = new StatusBarNotification[mNotificationList.size()];
1883 final int N = mNotificationList.size();
1884 for (int i=0; i<N; i++) {
1885 tmp[i] = mNotificationList.get(i).sbn;
1886 }
1887 }
1888 }
1889 return tmp;
1890 }
1891
1892 /**
Dan Sandler994349c2015-04-15 11:02:54 -04001893 * Public API for getting a list of current notifications for the calling package/uid.
1894 *
Julia Reynolds573c6532017-01-24 17:44:38 -05001895 * Note that since notification posting is done asynchronously, this will not return
1896 * notifications that are in the process of being posted.
1897 *
Dan Sandler994349c2015-04-15 11:02:54 -04001898 * @returns A list of all the package's notifications, in natural order.
1899 */
1900 @Override
1901 public ParceledListSlice<StatusBarNotification> getAppActiveNotifications(String pkg,
1902 int incomingUserId) {
1903 checkCallerIsSystemOrSameApp(pkg);
1904 int userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1905 Binder.getCallingUid(), incomingUserId, true, false,
1906 "getAppActiveNotifications", pkg);
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05001907 synchronized (mNotificationLock) {
Julia Reynoldsfeb73412017-04-18 09:28:22 -04001908 final ArrayMap<String, StatusBarNotification> map
1909 = new ArrayMap<>(mNotificationList.size() + mEnqueuedNotifications.size());
Erik Wolsheimer2242b4d2015-11-24 13:22:04 -08001910 final int N = mNotificationList.size();
Dan Sandler994349c2015-04-15 11:02:54 -04001911 for (int i = 0; i < N; i++) {
Chris Wren6676dab2016-12-21 18:26:27 -05001912 StatusBarNotification sbn = sanitizeSbn(pkg, userId,
1913 mNotificationList.get(i).sbn);
1914 if (sbn != null) {
1915 map.put(sbn.getKey(), sbn);
1916 }
1917 }
1918 for(NotificationRecord snoozed: mSnoozeHelper.getSnoozed(userId, pkg)) {
1919 StatusBarNotification sbn = sanitizeSbn(pkg, userId, snoozed.sbn);
1920 if (sbn != null) {
1921 map.put(sbn.getKey(), sbn);
1922 }
1923 }
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05001924 final int M = mEnqueuedNotifications.size();
1925 for (int i = 0; i < M; i++) {
Chris Wren6676dab2016-12-21 18:26:27 -05001926 StatusBarNotification sbn = sanitizeSbn(pkg, userId,
1927 mEnqueuedNotifications.get(i).sbn);
1928 if (sbn != null) {
1929 map.put(sbn.getKey(), sbn); // pending update overwrites existing post here
Dan Sandler994349c2015-04-15 11:02:54 -04001930 }
1931 }
Julia Reynoldsfeb73412017-04-18 09:28:22 -04001932 final ArrayList<StatusBarNotification> list = new ArrayList<>(map.size());
1933 list.addAll(map.values());
1934 return new ParceledListSlice<StatusBarNotification>(list);
Dan Sandler994349c2015-04-15 11:02:54 -04001935 }
Dan Sandler994349c2015-04-15 11:02:54 -04001936 }
1937
Chris Wren6676dab2016-12-21 18:26:27 -05001938 private StatusBarNotification sanitizeSbn(String pkg, int userId,
1939 StatusBarNotification sbn) {
1940 if (sbn.getPackageName().equals(pkg) && sbn.getUserId() == userId
1941 && (sbn.getNotification().flags
1942 & Notification.FLAG_AUTOGROUP_SUMMARY) == 0) {
1943 // We could pass back a cloneLight() but clients might get confused and
1944 // try to send this thing back to notify() again, which would not work
1945 // very well.
1946 return new StatusBarNotification(
1947 sbn.getPackageName(),
1948 sbn.getOpPkg(),
Chris Wren6676dab2016-12-21 18:26:27 -05001949 sbn.getId(), sbn.getTag(), sbn.getUid(), sbn.getInitialPid(),
1950 sbn.getNotification().clone(),
1951 sbn.getUser(), sbn.getOverrideGroupKey(), sbn.getPostTime());
1952 }
1953 return null;
1954 }
1955
Dan Sandler994349c2015-04-15 11:02:54 -04001956 /**
Adam Lesinski182f73f2013-12-05 16:48:06 -08001957 * System-only API for getting a list of recent (cleared, no longer shown) notifications.
1958 *
1959 * Requires ACCESS_NOTIFICATIONS which is signature|system.
1960 */
1961 @Override
1962 public StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count) {
1963 // enforce() will ensure the calling uid has the correct permission
1964 getContext().enforceCallingOrSelfPermission(
1965 android.Manifest.permission.ACCESS_NOTIFICATIONS,
1966 "NotificationManagerService.getHistoricalNotifications");
1967
1968 StatusBarNotification[] tmp = null;
1969 int uid = Binder.getCallingUid();
1970
1971 // noteOp will check to make sure the callingPkg matches the uid
1972 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
1973 == AppOpsManager.MODE_ALLOWED) {
1974 synchronized (mArchive) {
1975 tmp = mArchive.getArray(count);
1976 }
1977 }
1978 return tmp;
1979 }
1980
1981 /**
1982 * Register a listener binder directly with the notification manager.
1983 *
1984 * Only works with system callers. Apps should extend
1985 * {@link android.service.notification.NotificationListenerService}.
1986 */
1987 @Override
1988 public void registerListener(final INotificationListener listener,
Chris Wren0efdb882016-03-01 17:17:47 -05001989 final ComponentName component, final int userid) {
Christoph Studer3e144d32014-05-22 16:48:40 +02001990 enforceSystemOrSystemUI("INotificationManager.registerListener");
Chris Wren0efdb882016-03-01 17:17:47 -05001991 mListeners.registerService(listener, component, userid);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001992 }
1993
1994 /**
1995 * Remove a listener binder directly
1996 */
1997 @Override
Chris Wrene0ba7eb2016-03-04 17:30:43 -05001998 public void unregisterListener(INotificationListener token, int userid) {
Chris Wrenb7c81092016-03-10 11:41:10 -05001999 mListeners.unregisterService(token, userid);
Adam Lesinski182f73f2013-12-05 16:48:06 -08002000 }
2001
2002 /**
2003 * Allow an INotificationListener to simulate a "clear all" operation.
2004 *
2005 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onClearAllNotifications}
2006 *
2007 * @param token The binder for the listener, to check that the caller is allowed
2008 */
2009 @Override
John Spurlocka4294292014-03-24 18:02:32 -04002010 public void cancelNotificationsFromListener(INotificationListener token, String[] keys) {
John Spurlocke6a7d932014-03-13 12:29:00 -04002011 final int callingUid = Binder.getCallingUid();
2012 final int callingPid = Binder.getCallingPid();
Adam Lesinski182f73f2013-12-05 16:48:06 -08002013 long identity = Binder.clearCallingIdentity();
2014 try {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05002015 synchronized (mNotificationLock) {
John Spurlock7340fc82014-04-24 18:50:12 -04002016 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
John Spurlocka4294292014-03-24 18:02:32 -04002017 if (keys != null) {
2018 final int N = keys.length;
2019 for (int i = 0; i < N; i++) {
2020 NotificationRecord r = mNotificationsByKey.get(keys[i]);
Griff Hazen335e1f02014-09-11 14:49:31 -07002021 if (r == null) continue;
Kenny Guya263e4e2014-03-03 18:24:03 +00002022 final int userId = r.sbn.getUserId();
2023 if (userId != info.userid && userId != UserHandle.USER_ALL &&
John Spurlockb408e8e2014-04-23 21:12:45 -04002024 !mUserProfiles.isCurrentProfile(userId)) {
Kenny Guya263e4e2014-03-03 18:24:03 +00002025 throw new SecurityException("Disallowed call from listener: "
John Spurlock7340fc82014-04-24 18:50:12 -04002026 + info.service);
Kenny Guya263e4e2014-03-03 18:24:03 +00002027 }
Griff Hazen335e1f02014-09-11 14:49:31 -07002028 cancelNotificationFromListenerLocked(info, callingUid, callingPid,
2029 r.sbn.getPackageName(), r.sbn.getTag(), r.sbn.getId(),
2030 userId);
John Spurlocka4294292014-03-24 18:02:32 -04002031 }
2032 } else {
2033 cancelAllLocked(callingUid, callingPid, info.userid,
Kenny Guya263e4e2014-03-03 18:24:03 +00002034 REASON_LISTENER_CANCEL_ALL, info, info.supportsProfiles());
John Spurlocka4294292014-03-24 18:02:32 -04002035 }
Adam Lesinskie8240262014-03-26 16:01:00 -07002036 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08002037 } finally {
2038 Binder.restoreCallingIdentity(identity);
2039 }
2040 }
2041
Chris Wrenab41eec2016-01-04 18:01:27 -05002042 /**
2043 * Handle request from an approved listener to re-enable itself.
2044 *
2045 * @param component The componenet to be re-enabled, caller must match package.
2046 */
2047 @Override
2048 public void requestBindListener(ComponentName component) {
2049 checkCallerIsSystemOrSameApp(component.getPackageName());
2050 long identity = Binder.clearCallingIdentity();
2051 try {
Julia Reynoldse46bb372016-03-17 11:05:58 -04002052 ManagedServices manager =
Julia Reynolds77b2cc92016-11-08 14:41:09 -05002053 mNotificationAssistants.isComponentEnabledForCurrentProfiles(component)
2054 ? mNotificationAssistants
Chris Wrenab41eec2016-01-04 18:01:27 -05002055 : mListeners;
2056 manager.setComponentState(component, true);
2057 } finally {
2058 Binder.restoreCallingIdentity(identity);
2059 }
2060 }
2061
2062 @Override
2063 public void requestUnbindListener(INotificationListener token) {
2064 long identity = Binder.clearCallingIdentity();
2065 try {
2066 // allow bound services to disable themselves
Julia Reynoldsfeb73412017-04-18 09:28:22 -04002067 synchronized (mNotificationLock) {
2068 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2069 info.getOwner().setComponentState(info.component, false);
2070 }
Chris Wrenab41eec2016-01-04 18:01:27 -05002071 } finally {
2072 Binder.restoreCallingIdentity(identity);
2073 }
2074 }
2075
Amith Yamasanif47e51e2015-04-17 10:02:15 -07002076 @Override
2077 public void setNotificationsShownFromListener(INotificationListener token, String[] keys) {
Amith Yamasanif47e51e2015-04-17 10:02:15 -07002078 long identity = Binder.clearCallingIdentity();
2079 try {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05002080 synchronized (mNotificationLock) {
Amith Yamasanif47e51e2015-04-17 10:02:15 -07002081 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2082 if (keys != null) {
2083 final int N = keys.length;
2084 for (int i = 0; i < N; i++) {
2085 NotificationRecord r = mNotificationsByKey.get(keys[i]);
2086 if (r == null) continue;
2087 final int userId = r.sbn.getUserId();
2088 if (userId != info.userid && userId != UserHandle.USER_ALL &&
2089 !mUserProfiles.isCurrentProfile(userId)) {
2090 throw new SecurityException("Disallowed call from listener: "
2091 + info.service);
2092 }
2093 if (!r.isSeen()) {
2094 if (DBG) Slog.d(TAG, "Marking notification as seen " + keys[i]);
2095 mAppUsageStats.reportEvent(r.sbn.getPackageName(),
Xiaohui Chenddbe4ca2015-08-13 16:20:56 -07002096 userId == UserHandle.USER_ALL ? UserHandle.USER_SYSTEM
Amith Yamasanif47e51e2015-04-17 10:02:15 -07002097 : userId,
Adam Lesinskic8e87292015-06-10 15:33:45 -07002098 UsageEvents.Event.USER_INTERACTION);
Amith Yamasanif47e51e2015-04-17 10:02:15 -07002099 r.setSeen();
2100 }
2101 }
2102 }
2103 }
2104 } finally {
2105 Binder.restoreCallingIdentity(identity);
2106 }
2107 }
2108
Julia Reynolds72f1cbb2016-09-19 14:57:31 -04002109 /**
2110 * Allow an INotificationListener to simulate clearing (dismissing) a single notification.
2111 *
2112 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onNotificationClear}
2113 *
Julia Reynolds79672302017-01-12 08:30:16 -05002114 * @param info The binder for the listener, to check that the caller is allowed
Julia Reynolds72f1cbb2016-09-19 14:57:31 -04002115 */
Julia Reynolds88860ce2017-06-01 16:55:49 -04002116 @GuardedBy("mNotificationLock")
John Spurlock7340fc82014-04-24 18:50:12 -04002117 private void cancelNotificationFromListenerLocked(ManagedServiceInfo info,
Kenny Guya263e4e2014-03-03 18:24:03 +00002118 int callingUid, int callingPid, String pkg, String tag, int id, int userId) {
John Spurlocka4294292014-03-24 18:02:32 -04002119 cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
2120 Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
2121 true,
Kenny Guya263e4e2014-03-03 18:24:03 +00002122 userId, REASON_LISTENER_CANCEL, info);
John Spurlocka4294292014-03-24 18:02:32 -04002123 }
2124
Adam Lesinski182f73f2013-12-05 16:48:06 -08002125 /**
Julia Reynolds79672302017-01-12 08:30:16 -05002126 * Allow an INotificationListener to snooze a single notification until a context.
2127 *
2128 * @param token The binder for the listener, to check that the caller is allowed
2129 */
2130 @Override
2131 public void snoozeNotificationUntilContextFromListener(INotificationListener token,
2132 String key, String snoozeCriterionId) {
2133 long identity = Binder.clearCallingIdentity();
2134 try {
Julia Reynoldsfeb73412017-04-18 09:28:22 -04002135 synchronized (mNotificationLock) {
2136 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2137 snoozeNotificationInt(key, SNOOZE_UNTIL_UNSPECIFIED, snoozeCriterionId, info);
2138 }
Julia Reynolds79672302017-01-12 08:30:16 -05002139 } finally {
2140 Binder.restoreCallingIdentity(identity);
2141 }
2142 }
2143
2144 /**
2145 * Allow an INotificationListener to snooze a single notification until a time.
Julia Reynolds72f1cbb2016-09-19 14:57:31 -04002146 *
2147 * @param token The binder for the listener, to check that the caller is allowed
2148 */
2149 @Override
Julia Reynoldsb6c1f992016-11-22 09:26:46 -05002150 public void snoozeNotificationUntilFromListener(INotificationListener token, String key,
Julia Reynolds50989772017-02-23 14:32:16 -05002151 long duration) {
Julia Reynolds72f1cbb2016-09-19 14:57:31 -04002152 long identity = Binder.clearCallingIdentity();
2153 try {
Julia Reynoldsfeb73412017-04-18 09:28:22 -04002154 synchronized (mNotificationLock) {
2155 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2156 snoozeNotificationInt(key, duration, null, info);
2157 }
Julia Reynolds72f1cbb2016-09-19 14:57:31 -04002158 } finally {
2159 Binder.restoreCallingIdentity(identity);
2160 }
2161 }
2162
2163 /**
Julia Reynoldscf63ff12017-01-24 13:55:48 -05002164 * Allows the notification assistant to un-snooze a single notification.
Julia Reynoldsb6c1f992016-11-22 09:26:46 -05002165 *
Julia Reynoldscf63ff12017-01-24 13:55:48 -05002166 * @param token The binder for the assistant, to check that the caller is allowed
Julia Reynoldsb6c1f992016-11-22 09:26:46 -05002167 */
2168 @Override
Julia Reynoldscf63ff12017-01-24 13:55:48 -05002169 public void unsnoozeNotificationFromAssistant(INotificationListener token, String key) {
Julia Reynoldsb6c1f992016-11-22 09:26:46 -05002170 long identity = Binder.clearCallingIdentity();
2171 try {
Julia Reynoldsfeb73412017-04-18 09:28:22 -04002172 synchronized (mNotificationLock) {
2173 final ManagedServiceInfo info =
2174 mNotificationAssistants.checkServiceTokenLocked(token);
2175 unsnoozeNotificationInt(key, info);
2176 }
Julia Reynoldsb6c1f992016-11-22 09:26:46 -05002177 } finally {
2178 Binder.restoreCallingIdentity(identity);
2179 }
2180 }
2181
2182 /**
Adam Lesinski182f73f2013-12-05 16:48:06 -08002183 * Allow an INotificationListener to simulate clearing (dismissing) a single notification.
2184 *
2185 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onNotificationClear}
2186 *
2187 * @param token The binder for the listener, to check that the caller is allowed
2188 */
2189 @Override
2190 public void cancelNotificationFromListener(INotificationListener token, String pkg,
2191 String tag, int id) {
John Spurlocke6a7d932014-03-13 12:29:00 -04002192 final int callingUid = Binder.getCallingUid();
2193 final int callingPid = Binder.getCallingPid();
Adam Lesinski182f73f2013-12-05 16:48:06 -08002194 long identity = Binder.clearCallingIdentity();
2195 try {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05002196 synchronized (mNotificationLock) {
John Spurlock7340fc82014-04-24 18:50:12 -04002197 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
Kenny Guya263e4e2014-03-03 18:24:03 +00002198 if (info.supportsProfiles()) {
2199 Log.e(TAG, "Ignoring deprecated cancelNotification(pkg, tag, id) "
2200 + "from " + info.component
2201 + " use cancelNotification(key) instead.");
2202 } else {
2203 cancelNotificationFromListenerLocked(info, callingUid, callingPid,
2204 pkg, tag, id, info.userid);
2205 }
Adam Lesinskie8240262014-03-26 16:01:00 -07002206 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08002207 } finally {
2208 Binder.restoreCallingIdentity(identity);
2209 }
2210 }
2211
2212 /**
2213 * Allow an INotificationListener to request the list of outstanding notifications seen by
2214 * the current user. Useful when starting up, after which point the listener callbacks
2215 * should be used.
2216 *
2217 * @param token The binder for the listener, to check that the caller is allowed
Dan Sandlerea75fdd2014-08-12 12:29:19 -04002218 * @param keys An array of notification keys to fetch, or null to fetch everything
Chris Wrenf9536642014-04-17 10:01:54 -04002219 * @returns The return value will contain the notifications specified in keys, in that
2220 * order, or if keys is null, all the notifications, in natural order.
Adam Lesinski182f73f2013-12-05 16:48:06 -08002221 */
2222 @Override
Christoph Studercee44ba2014-05-20 18:36:43 +02002223 public ParceledListSlice<StatusBarNotification> getActiveNotificationsFromListener(
Christoph Studerb82bc782014-08-20 14:29:43 +02002224 INotificationListener token, String[] keys, int trim) {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05002225 synchronized (mNotificationLock) {
John Spurlock7340fc82014-04-24 18:50:12 -04002226 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
Dan Sandlerea75fdd2014-08-12 12:29:19 -04002227 final boolean getKeys = keys != null;
2228 final int N = getKeys ? keys.length : mNotificationList.size();
Christoph Studerb82bc782014-08-20 14:29:43 +02002229 final ArrayList<StatusBarNotification> list
2230 = new ArrayList<StatusBarNotification>(N);
Christoph Studercee44ba2014-05-20 18:36:43 +02002231 for (int i=0; i<N; i++) {
Dan Sandlerea75fdd2014-08-12 12:29:19 -04002232 final NotificationRecord r = getKeys
2233 ? mNotificationsByKey.get(keys[i])
2234 : mNotificationList.get(i);
Christoph Studerb82bc782014-08-20 14:29:43 +02002235 if (r == null) continue;
2236 StatusBarNotification sbn = r.sbn;
2237 if (!isVisibleToListener(sbn, info)) continue;
2238 StatusBarNotification sbnToSend =
2239 (trim == TRIM_FULL) ? sbn : sbn.cloneLight();
2240 list.add(sbnToSend);
Adam Lesinski182f73f2013-12-05 16:48:06 -08002241 }
Christoph Studercee44ba2014-05-20 18:36:43 +02002242 return new ParceledListSlice<StatusBarNotification>(list);
Adam Lesinski182f73f2013-12-05 16:48:06 -08002243 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08002244 }
2245
Julia Reynoldscf63ff12017-01-24 13:55:48 -05002246 /**
2247 * Allow an INotificationListener to request the list of outstanding snoozed notifications
2248 * seen by the current user. Useful when starting up, after which point the listener
2249 * callbacks should be used.
2250 *
2251 * @param token The binder for the listener, to check that the caller is allowed
2252 * @returns The return value will contain the notifications specified in keys, in that
2253 * order, or if keys is null, all the notifications, in natural order.
2254 */
2255 @Override
2256 public ParceledListSlice<StatusBarNotification> getSnoozedNotificationsFromListener(
2257 INotificationListener token, int trim) {
2258 synchronized (mNotificationLock) {
2259 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2260 List<NotificationRecord> snoozedRecords = mSnoozeHelper.getSnoozed();
2261 final int N = snoozedRecords.size();
2262 final ArrayList<StatusBarNotification> list = new ArrayList<>(N);
2263 for (int i=0; i < N; i++) {
2264 final NotificationRecord r = snoozedRecords.get(i);
2265 if (r == null) continue;
2266 StatusBarNotification sbn = r.sbn;
2267 if (!isVisibleToListener(sbn, info)) continue;
2268 StatusBarNotification sbnToSend =
2269 (trim == TRIM_FULL) ? sbn : sbn.cloneLight();
2270 list.add(sbnToSend);
2271 }
2272 return new ParceledListSlice<>(list);
2273 }
2274 }
2275
Adam Lesinski182f73f2013-12-05 16:48:06 -08002276 @Override
John Spurlockd8afe3c2014-08-01 14:04:07 -04002277 public void requestHintsFromListener(INotificationListener token, int hints) {
2278 final long identity = Binder.clearCallingIdentity();
2279 try {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05002280 synchronized (mNotificationLock) {
John Spurlockd8afe3c2014-08-01 14:04:07 -04002281 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
Bryce Lee7219ada2016-04-08 10:54:23 -07002282 final int disableEffectsMask = HINT_HOST_DISABLE_EFFECTS
2283 | HINT_HOST_DISABLE_NOTIFICATION_EFFECTS
2284 | HINT_HOST_DISABLE_CALL_EFFECTS;
2285 final boolean disableEffects = (hints & disableEffectsMask) != 0;
John Spurlockd8afe3c2014-08-01 14:04:07 -04002286 if (disableEffects) {
Bryce Lee7219ada2016-04-08 10:54:23 -07002287 addDisabledHints(info, hints);
John Spurlockd8afe3c2014-08-01 14:04:07 -04002288 } else {
Bryce Lee7219ada2016-04-08 10:54:23 -07002289 removeDisabledHints(info, hints);
John Spurlockd8afe3c2014-08-01 14:04:07 -04002290 }
John Spurlockd8afe3c2014-08-01 14:04:07 -04002291 updateListenerHintsLocked();
John Spurlockb4782522014-08-22 14:54:46 -04002292 updateEffectsSuppressorLocked();
John Spurlock1fa865f2014-07-21 14:56:39 -04002293 }
John Spurlockd8afe3c2014-08-01 14:04:07 -04002294 } finally {
2295 Binder.restoreCallingIdentity(identity);
John Spurlock1fa865f2014-07-21 14:56:39 -04002296 }
2297 }
2298
2299 @Override
John Spurlockd8afe3c2014-08-01 14:04:07 -04002300 public int getHintsFromListener(INotificationListener token) {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05002301 synchronized (mNotificationLock) {
John Spurlockd8afe3c2014-08-01 14:04:07 -04002302 return mListenerHints;
John Spurlock1fa865f2014-07-21 14:56:39 -04002303 }
2304 }
2305
2306 @Override
Christoph Studer85a384b2014-08-27 20:16:15 +02002307 public void requestInterruptionFilterFromListener(INotificationListener token,
2308 int interruptionFilter) throws RemoteException {
2309 final long identity = Binder.clearCallingIdentity();
2310 try {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05002311 synchronized (mNotificationLock) {
John Spurlock661f2cf2014-11-17 10:29:10 -05002312 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2313 mZenModeHelper.requestFromListener(info.component, interruptionFilter);
Christoph Studer85a384b2014-08-27 20:16:15 +02002314 updateInterruptionFilterLocked();
2315 }
2316 } finally {
2317 Binder.restoreCallingIdentity(identity);
2318 }
2319 }
2320
2321 @Override
2322 public int getInterruptionFilterFromListener(INotificationListener token)
2323 throws RemoteException {
2324 synchronized (mNotificationLight) {
2325 return mInterruptionFilter;
2326 }
2327 }
2328
2329 @Override
Christoph Studerb82bc782014-08-20 14:29:43 +02002330 public void setOnNotificationPostedTrimFromListener(INotificationListener token, int trim)
2331 throws RemoteException {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05002332 synchronized (mNotificationLock) {
Christoph Studerb82bc782014-08-20 14:29:43 +02002333 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2334 if (info == null) return;
2335 mListeners.setOnNotificationPostedTrimLocked(info, trim);
2336 }
2337 }
2338
2339 @Override
John Spurlockb2278d62015-04-07 12:47:12 -04002340 public int getZenMode() {
2341 return mZenModeHelper.getZenMode();
2342 }
2343
2344 @Override
John Spurlock056c5192014-04-20 21:52:01 -04002345 public ZenModeConfig getZenModeConfig() {
Julia Reynoldsbb983d202017-01-06 09:54:20 -05002346 enforceSystemOrSystemUI("INotificationManager.getZenModeConfig");
John Spurlock056c5192014-04-20 21:52:01 -04002347 return mZenModeHelper.getConfig();
2348 }
2349
2350 @Override
John Spurlockb2278d62015-04-07 12:47:12 -04002351 public void setZenMode(int mode, Uri conditionId, String reason) throws RemoteException {
Julia Reynoldsbb983d202017-01-06 09:54:20 -05002352 enforceSystemOrSystemUI("INotificationManager.setZenMode");
John Spurlockcdb57ae2015-02-11 19:04:11 -05002353 final long identity = Binder.clearCallingIdentity();
2354 try {
Julia Reynolds44ad6ff2016-07-06 09:47:45 -04002355 mZenModeHelper.setManualZenMode(mode, conditionId, null, reason);
John Spurlockcdb57ae2015-02-11 19:04:11 -05002356 } finally {
2357 Binder.restoreCallingIdentity(identity);
2358 }
2359 }
2360
2361 @Override
Julia Reynolds361e82d32016-02-26 18:19:49 -05002362 public List<ZenModeConfig.ZenRule> getZenRules() throws RemoteException {
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04002363 enforcePolicyAccess(Binder.getCallingUid(), "getAutomaticZenRules");
Julia Reynolds361e82d32016-02-26 18:19:49 -05002364 return mZenModeHelper.getZenRules();
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04002365 }
2366
2367 @Override
Julia Reynolds4fe98d62015-10-06 16:23:41 -04002368 public AutomaticZenRule getAutomaticZenRule(String id) throws RemoteException {
2369 Preconditions.checkNotNull(id, "Id is null");
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04002370 enforcePolicyAccess(Binder.getCallingUid(), "getAutomaticZenRule");
Julia Reynolds4fe98d62015-10-06 16:23:41 -04002371 return mZenModeHelper.getAutomaticZenRule(id);
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04002372 }
2373
2374 @Override
Julia Reynolds361e82d32016-02-26 18:19:49 -05002375 public String addAutomaticZenRule(AutomaticZenRule automaticZenRule)
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04002376 throws RemoteException {
2377 Preconditions.checkNotNull(automaticZenRule, "automaticZenRule is null");
2378 Preconditions.checkNotNull(automaticZenRule.getName(), "Name is null");
2379 Preconditions.checkNotNull(automaticZenRule.getOwner(), "Owner is null");
2380 Preconditions.checkNotNull(automaticZenRule.getConditionId(), "ConditionId is null");
Julia Reynolds4fe98d62015-10-06 16:23:41 -04002381 enforcePolicyAccess(Binder.getCallingUid(), "addAutomaticZenRule");
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04002382
Julia Reynolds4fe98d62015-10-06 16:23:41 -04002383 return mZenModeHelper.addAutomaticZenRule(automaticZenRule,
2384 "addAutomaticZenRule");
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04002385 }
2386
2387 @Override
Julia Reynolds361e82d32016-02-26 18:19:49 -05002388 public boolean updateAutomaticZenRule(String id, AutomaticZenRule automaticZenRule)
Julia Reynolds4fe98d62015-10-06 16:23:41 -04002389 throws RemoteException {
2390 Preconditions.checkNotNull(automaticZenRule, "automaticZenRule is null");
2391 Preconditions.checkNotNull(automaticZenRule.getName(), "Name is null");
2392 Preconditions.checkNotNull(automaticZenRule.getOwner(), "Owner is null");
2393 Preconditions.checkNotNull(automaticZenRule.getConditionId(), "ConditionId is null");
2394 enforcePolicyAccess(Binder.getCallingUid(), "updateAutomaticZenRule");
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04002395
Julia Reynolds361e82d32016-02-26 18:19:49 -05002396 return mZenModeHelper.updateAutomaticZenRule(id, automaticZenRule,
Julia Reynolds4fe98d62015-10-06 16:23:41 -04002397 "updateAutomaticZenRule");
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04002398 }
2399
2400 @Override
Julia Reynolds4fe98d62015-10-06 16:23:41 -04002401 public boolean removeAutomaticZenRule(String id) throws RemoteException {
2402 Preconditions.checkNotNull(id, "Id is null");
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04002403 // Verify that they can modify zen rules.
2404 enforcePolicyAccess(Binder.getCallingUid(), "removeAutomaticZenRule");
2405
Julia Reynolds4fe98d62015-10-06 16:23:41 -04002406 return mZenModeHelper.removeAutomaticZenRule(id, "removeAutomaticZenRule");
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04002407 }
2408
2409 @Override
Julia Reynoldsc8e54e82015-11-30 16:43:05 -05002410 public boolean removeAutomaticZenRules(String packageName) throws RemoteException {
2411 Preconditions.checkNotNull(packageName, "Package name is null");
2412 enforceSystemOrSystemUI("removeAutomaticZenRules");
2413
2414 return mZenModeHelper.removeAutomaticZenRules(packageName, "removeAutomaticZenRules");
2415 }
2416
2417 @Override
Julia Reynolds43b70cd2016-01-14 15:05:34 -05002418 public int getRuleInstanceCount(ComponentName owner) throws RemoteException {
2419 Preconditions.checkNotNull(owner, "Owner is null");
2420 enforceSystemOrSystemUI("getRuleInstanceCount");
2421
2422 return mZenModeHelper.getCurrentInstanceCount(owner);
2423 }
2424
2425 @Override
John Spurlock80774932015-05-07 17:38:50 -04002426 public void setInterruptionFilter(String pkg, int filter) throws RemoteException {
2427 enforcePolicyAccess(pkg, "setInterruptionFilter");
2428 final int zen = NotificationManager.zenModeFromInterruptionFilter(filter, -1);
2429 if (zen == -1) throw new IllegalArgumentException("Invalid filter: " + filter);
2430 final long identity = Binder.clearCallingIdentity();
2431 try {
Julia Reynolds44ad6ff2016-07-06 09:47:45 -04002432 mZenModeHelper.setManualZenMode(zen, null, pkg, "setInterruptionFilter");
John Spurlock80774932015-05-07 17:38:50 -04002433 } finally {
2434 Binder.restoreCallingIdentity(identity);
2435 }
2436 }
2437
2438 @Override
John Spurlocka7d92b12015-05-13 14:48:02 -04002439 public void notifyConditions(final String pkg, IConditionProvider provider,
2440 final Condition[] conditions) {
John Spurlocke77bb362014-04-26 10:24:59 -04002441 final ManagedServiceInfo info = mConditionProviders.checkServiceToken(provider);
2442 checkCallerIsSystemOrSameApp(pkg);
John Spurlocka7d92b12015-05-13 14:48:02 -04002443 mHandler.post(new Runnable() {
2444 @Override
2445 public void run() {
2446 mConditionProviders.notifyConditions(pkg, info, conditions);
2447 }
2448 });
John Spurlocke77bb362014-04-26 10:24:59 -04002449 }
2450
Julia Reynolds38e6ca42016-08-08 08:38:09 -04002451 @Override
2452 public void requestUnbindProvider(IConditionProvider provider) {
2453 long identity = Binder.clearCallingIdentity();
2454 try {
2455 // allow bound services to disable themselves
2456 final ManagedServiceInfo info = mConditionProviders.checkServiceToken(provider);
2457 info.getOwner().setComponentState(info.component, false);
2458 } finally {
2459 Binder.restoreCallingIdentity(identity);
2460 }
2461 }
2462
2463 @Override
2464 public void requestBindProvider(ComponentName component) {
2465 checkCallerIsSystemOrSameApp(component.getPackageName());
2466 long identity = Binder.clearCallingIdentity();
2467 try {
2468 mConditionProviders.setComponentState(component, true);
2469 } finally {
2470 Binder.restoreCallingIdentity(identity);
2471 }
2472 }
2473
John Spurlocke77bb362014-04-26 10:24:59 -04002474 private void enforceSystemOrSystemUI(String message) {
Geoffrey Pitsch27684152017-05-02 11:41:31 -04002475 if (isCallerSystemOrPhone()) return;
John Spurlocke77bb362014-04-26 10:24:59 -04002476 getContext().enforceCallingPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
2477 message);
John Spurlock7340fc82014-04-24 18:50:12 -04002478 }
2479
Julia Reynolds48034f82016-03-09 10:15:16 -05002480 private void enforceSystemOrSystemUIOrSamePackage(String pkg, String message) {
2481 try {
2482 checkCallerIsSystemOrSameApp(pkg);
2483 } catch (SecurityException e) {
2484 getContext().enforceCallingPermission(
2485 android.Manifest.permission.STATUS_BAR_SERVICE,
2486 message);
2487 }
2488 }
2489
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04002490 private void enforcePolicyAccess(int uid, String method) {
2491 if (PackageManager.PERMISSION_GRANTED == getContext().checkCallingPermission(
2492 android.Manifest.permission.MANAGE_NOTIFICATIONS)) {
2493 return;
2494 }
2495 boolean accessAllowed = false;
2496 String[] packages = getContext().getPackageManager().getPackagesForUid(uid);
2497 final int packageCount = packages.length;
2498 for (int i = 0; i < packageCount; i++) {
2499 if (checkPolicyAccess(packages[i])) {
2500 accessAllowed = true;
2501 }
2502 }
2503 if (!accessAllowed) {
2504 Slog.w(TAG, "Notification policy access denied calling " + method);
2505 throw new SecurityException("Notification policy access denied");
2506 }
2507 }
2508
John Spurlock80774932015-05-07 17:38:50 -04002509 private void enforcePolicyAccess(String pkg, String method) {
Julia Reynolds6ee26172015-09-28 11:34:48 -04002510 if (PackageManager.PERMISSION_GRANTED == getContext().checkCallingPermission(
2511 android.Manifest.permission.MANAGE_NOTIFICATIONS)) {
2512 return;
2513 }
Julia Reynolds0cd1b782016-06-29 08:43:00 -04002514 checkCallerIsSameApp(pkg);
John Spurlock80774932015-05-07 17:38:50 -04002515 if (!checkPolicyAccess(pkg)) {
2516 Slog.w(TAG, "Notification policy access denied calling " + method);
2517 throw new SecurityException("Notification policy access denied");
John Spurlock1fc476d2015-04-14 16:05:20 -04002518 }
2519 }
2520
John Spurlock80774932015-05-07 17:38:50 -04002521 private boolean checkPackagePolicyAccess(String pkg) {
John Spurlock7c74f782015-06-04 13:01:42 -04002522 return mPolicyAccess.isPackageGranted(pkg);
John Spurlock80774932015-05-07 17:38:50 -04002523 }
2524
2525 private boolean checkPolicyAccess(String pkg) {
Julia Reynolds0867b3a2016-03-30 17:29:54 -04002526 try {
2527 int uid = getContext().getPackageManager().getPackageUidAsUser(
2528 pkg, UserHandle.getCallingUserId());
2529 if (PackageManager.PERMISSION_GRANTED == ActivityManager.checkComponentPermission(
2530 android.Manifest.permission.MANAGE_NOTIFICATIONS, uid,
2531 -1, true)) {
2532 return true;
2533 }
2534 } catch (NameNotFoundException e) {
2535 return false;
Julia Reynoldsa2d01022016-03-18 15:03:43 -04002536 }
John Spurlock80774932015-05-07 17:38:50 -04002537 return checkPackagePolicyAccess(pkg) || mListeners.isComponentEnabledForPackage(pkg);
John Spurlock1fc476d2015-04-14 16:05:20 -04002538 }
2539
John Spurlock7340fc82014-04-24 18:50:12 -04002540 @Override
Adam Lesinski182f73f2013-12-05 16:48:06 -08002541 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Jeff Sharkey6df866a2017-03-31 14:08:23 -06002542 if (!DumpUtils.checkDumpAndUsageStatsPermission(getContext(), TAG, pw)) return;
Chris Wrene4b38802015-07-07 15:54:19 -04002543 final DumpFilter filter = DumpFilter.parseFromArguments(args);
2544 if (filter != null && filter.stats) {
2545 dumpJson(pw, filter);
Julia Reynoldsc9842c12017-02-07 12:46:41 -05002546 } else if (filter != null && filter.proto) {
2547 dumpProto(fd, filter);
Chris Wrene4b38802015-07-07 15:54:19 -04002548 } else {
2549 dumpImpl(pw, filter);
2550 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08002551 }
John Spurlockb4782522014-08-22 14:54:46 -04002552
2553 @Override
2554 public ComponentName getEffectsSuppressor() {
Bryce Leeba3d8952016-04-12 12:39:15 -07002555 return !mEffectsSuppressors.isEmpty() ? mEffectsSuppressors.get(0) : null;
John Spurlockb4782522014-08-22 14:54:46 -04002556 }
John Spurlock2b122f42014-08-27 16:29:47 -04002557
2558 @Override
2559 public boolean matchesCallFilter(Bundle extras) {
2560 enforceSystemOrSystemUI("INotificationManager.matchesCallFilter");
Christoph Studer12aeda82014-09-23 19:08:56 +02002561 return mZenModeHelper.matchesCallFilter(
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07002562 Binder.getCallingUserHandle(),
Christoph Studer12aeda82014-09-23 19:08:56 +02002563 extras,
2564 mRankingHelper.findExtractor(ValidateNotificationPeople.class),
2565 MATCHES_CALL_FILTER_CONTACTS_TIMEOUT_MS,
2566 MATCHES_CALL_FILTER_TIMEOUT_AFFINITY);
John Spurlock2b122f42014-08-27 16:29:47 -04002567 }
John Spurlock530052a2014-11-30 16:26:19 -05002568
2569 @Override
2570 public boolean isSystemConditionProviderEnabled(String path) {
Julia Reynoldsbb983d202017-01-06 09:54:20 -05002571 enforceSystemOrSystemUI("INotificationManager.isSystemConditionProviderEnabled");
John Spurlockb2278d62015-04-07 12:47:12 -04002572 return mConditionProviders.isSystemProviderEnabled(path);
John Spurlock530052a2014-11-30 16:26:19 -05002573 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08002574
Christopher Tatef9767d62015-04-08 14:35:43 -07002575 // Backup/restore interface
2576 @Override
2577 public byte[] getBackupPayload(int user) {
John Spurlock35ef0a62015-05-28 11:24:10 -04002578 if (DBG) Slog.d(TAG, "getBackupPayload u=" + user);
Xiaohui Chenddbe4ca2015-08-13 16:20:56 -07002579 //TODO: http://b/22388012
2580 if (user != UserHandle.USER_SYSTEM) {
John Spurlock35ef0a62015-05-28 11:24:10 -04002581 Slog.w(TAG, "getBackupPayload: cannot backup policy for user " + user);
2582 return null;
2583 }
songjinshi9bf22712017-02-04 10:47:45 +08002584 synchronized(mPolicyFile) {
2585 final ByteArrayOutputStream baos = new ByteArrayOutputStream();
2586 try {
2587 writePolicyXml(baos, true /*forBackup*/);
2588 return baos.toByteArray();
2589 } catch (IOException e) {
2590 Slog.w(TAG, "getBackupPayload: error writing payload for user " + user, e);
2591 }
John Spurlock35ef0a62015-05-28 11:24:10 -04002592 }
Christopher Tatef9767d62015-04-08 14:35:43 -07002593 return null;
2594 }
2595
2596 @Override
2597 public void applyRestore(byte[] payload, int user) {
John Spurlock35ef0a62015-05-28 11:24:10 -04002598 if (DBG) Slog.d(TAG, "applyRestore u=" + user + " payload="
2599 + (payload != null ? new String(payload, StandardCharsets.UTF_8) : null));
2600 if (payload == null) {
2601 Slog.w(TAG, "applyRestore: no payload to restore for user " + user);
2602 return;
2603 }
Xiaohui Chenddbe4ca2015-08-13 16:20:56 -07002604 //TODO: http://b/22388012
2605 if (user != UserHandle.USER_SYSTEM) {
John Spurlock35ef0a62015-05-28 11:24:10 -04002606 Slog.w(TAG, "applyRestore: cannot restore policy for user " + user);
2607 return;
2608 }
songjinshi9bf22712017-02-04 10:47:45 +08002609 synchronized(mPolicyFile) {
2610 final ByteArrayInputStream bais = new ByteArrayInputStream(payload);
2611 try {
2612 readPolicyXml(bais, true /*forRestore*/);
2613 savePolicyFile();
2614 } catch (NumberFormatException | XmlPullParserException | IOException e) {
2615 Slog.w(TAG, "applyRestore: error reading payload", e);
2616 }
John Spurlock35ef0a62015-05-28 11:24:10 -04002617 }
Christopher Tatef9767d62015-04-08 14:35:43 -07002618 }
2619
John Spurlock1fc476d2015-04-14 16:05:20 -04002620 @Override
John Spurlock80774932015-05-07 17:38:50 -04002621 public boolean isNotificationPolicyAccessGranted(String pkg) {
2622 return checkPolicyAccess(pkg);
John Spurlock1fc476d2015-04-14 16:05:20 -04002623 }
2624
2625 @Override
Julia Reynolds48034f82016-03-09 10:15:16 -05002626 public boolean isNotificationPolicyAccessGrantedForPackage(String pkg) {;
2627 enforceSystemOrSystemUIOrSamePackage(pkg,
2628 "request policy access status for another package");
Julia Reynoldsa2d01022016-03-18 15:03:43 -04002629 return checkPolicyAccess(pkg);
John Spurlock80774932015-05-07 17:38:50 -04002630 }
2631
2632 @Override
2633 public String[] getPackagesRequestingNotificationPolicyAccess()
2634 throws RemoteException {
2635 enforceSystemOrSystemUI("request policy access packages");
2636 final long identity = Binder.clearCallingIdentity();
2637 try {
John Spurlock7c74f782015-06-04 13:01:42 -04002638 return mPolicyAccess.getRequestingPackages();
John Spurlock80774932015-05-07 17:38:50 -04002639 } finally {
2640 Binder.restoreCallingIdentity(identity);
2641 }
2642 }
2643
2644 @Override
2645 public void setNotificationPolicyAccessGranted(String pkg, boolean granted)
2646 throws RemoteException {
2647 enforceSystemOrSystemUI("grant notification policy access");
2648 final long identity = Binder.clearCallingIdentity();
2649 try {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05002650 synchronized (mNotificationLock) {
John Spurlock80774932015-05-07 17:38:50 -04002651 mPolicyAccess.put(pkg, granted);
2652 }
2653 } finally {
2654 Binder.restoreCallingIdentity(identity);
2655 }
2656 }
2657
2658 @Override
2659 public Policy getNotificationPolicy(String pkg) {
2660 enforcePolicyAccess(pkg, "getNotificationPolicy");
John Spurlock1fc476d2015-04-14 16:05:20 -04002661 final long identity = Binder.clearCallingIdentity();
2662 try {
2663 return mZenModeHelper.getNotificationPolicy();
2664 } finally {
2665 Binder.restoreCallingIdentity(identity);
2666 }
2667 }
2668
2669 @Override
John Spurlock80774932015-05-07 17:38:50 -04002670 public void setNotificationPolicy(String pkg, Policy policy) {
2671 enforcePolicyAccess(pkg, "setNotificationPolicy");
John Spurlock1fc476d2015-04-14 16:05:20 -04002672 final long identity = Binder.clearCallingIdentity();
2673 try {
2674 mZenModeHelper.setNotificationPolicy(policy);
2675 } finally {
2676 Binder.restoreCallingIdentity(identity);
2677 }
2678 }
Chris Wren51017d02015-12-15 15:34:46 -05002679
2680 @Override
Julia Reynolds4b82f6d2017-01-04 10:47:41 -05002681 public void applyEnqueuedAdjustmentFromAssistant(INotificationListener token,
2682 Adjustment adjustment) throws RemoteException {
2683 final long identity = Binder.clearCallingIdentity();
2684 try {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05002685 synchronized (mNotificationLock) {
Julia Reynolds4b82f6d2017-01-04 10:47:41 -05002686 mNotificationAssistants.checkServiceTokenLocked(token);
2687 int N = mEnqueuedNotifications.size();
2688 for (int i = 0; i < N; i++) {
2689 final NotificationRecord n = mEnqueuedNotifications.get(i);
2690 if (Objects.equals(adjustment.getKey(), n.getKey())
2691 && Objects.equals(adjustment.getUser(), n.getUserId())) {
2692 applyAdjustment(n, adjustment);
2693 break;
2694 }
2695 }
2696 }
2697 } finally {
2698 Binder.restoreCallingIdentity(identity);
2699 }
2700 }
2701
2702 @Override
Julia Reynolds52e64d02016-12-09 15:36:12 -05002703 public void applyAdjustmentFromAssistant(INotificationListener token,
Julia Reynoldse46bb372016-03-17 11:05:58 -04002704 Adjustment adjustment) throws RemoteException {
Chris Wren51017d02015-12-15 15:34:46 -05002705 final long identity = Binder.clearCallingIdentity();
2706 try {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05002707 synchronized (mNotificationLock) {
Julia Reynolds77b2cc92016-11-08 14:41:09 -05002708 mNotificationAssistants.checkServiceTokenLocked(token);
Julia Reynolds4b82f6d2017-01-04 10:47:41 -05002709 NotificationRecord n = mNotificationsByKey.get(adjustment.getKey());
2710 applyAdjustment(n, adjustment);
Chris Wren51017d02015-12-15 15:34:46 -05002711 }
Julia Reynolds22f02b32016-12-01 15:05:13 -05002712 mRankingHandler.requestSort(true);
Julia Reynoldse46bb372016-03-17 11:05:58 -04002713 } finally {
2714 Binder.restoreCallingIdentity(identity);
2715 }
2716 }
2717
2718 @Override
Julia Reynolds52e64d02016-12-09 15:36:12 -05002719 public void applyAdjustmentsFromAssistant(INotificationListener token,
Julia Reynoldse46bb372016-03-17 11:05:58 -04002720 List<Adjustment> adjustments) throws RemoteException {
2721
2722 final long identity = Binder.clearCallingIdentity();
2723 try {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05002724 synchronized (mNotificationLock) {
Julia Reynolds77b2cc92016-11-08 14:41:09 -05002725 mNotificationAssistants.checkServiceTokenLocked(token);
Julia Reynoldse46bb372016-03-17 11:05:58 -04002726 for (Adjustment adjustment : adjustments) {
Julia Reynolds4b82f6d2017-01-04 10:47:41 -05002727 NotificationRecord n = mNotificationsByKey.get(adjustment.getKey());
2728 applyAdjustment(n, adjustment);
Julia Reynoldse46bb372016-03-17 11:05:58 -04002729 }
2730 }
Julia Reynolds22f02b32016-12-01 15:05:13 -05002731 mRankingHandler.requestSort(true);
Chris Wren51017d02015-12-15 15:34:46 -05002732 } finally {
2733 Binder.restoreCallingIdentity(identity);
2734 }
2735 }
Julia Reynolds73ed76b2017-04-04 17:04:38 -04002736
2737 @Override
2738 public void updateNotificationChannelFromPrivilegedListener(INotificationListener token,
Julia Reynoldsf27d6b22017-04-13 15:48:16 -04002739 String pkg, UserHandle user, NotificationChannel channel) throws RemoteException {
Julia Reynolds73ed76b2017-04-04 17:04:38 -04002740 Preconditions.checkNotNull(channel);
Julia Reynoldsf27d6b22017-04-13 15:48:16 -04002741 Preconditions.checkNotNull(pkg);
2742 Preconditions.checkNotNull(user);
Julia Reynolds73ed76b2017-04-04 17:04:38 -04002743
Julia Reynoldsf27d6b22017-04-13 15:48:16 -04002744 verifyPrivilegedListener(token, user);
2745 updateNotificationChannelInt(pkg, getUidForPackageAndUser(pkg, user), channel, true);
Julia Reynolds73ed76b2017-04-04 17:04:38 -04002746 }
2747
2748 @Override
2749 public ParceledListSlice<NotificationChannel> getNotificationChannelsFromPrivilegedListener(
Julia Reynoldsf27d6b22017-04-13 15:48:16 -04002750 INotificationListener token, String pkg, UserHandle user) throws RemoteException {
2751 Preconditions.checkNotNull(pkg);
2752 Preconditions.checkNotNull(user);
2753 verifyPrivilegedListener(token, user);
Julia Reynolds73ed76b2017-04-04 17:04:38 -04002754
Julia Reynoldsf27d6b22017-04-13 15:48:16 -04002755 return mRankingHelper.getNotificationChannels(pkg, getUidForPackageAndUser(pkg, user),
2756 false /* includeDeleted */);
Julia Reynolds73ed76b2017-04-04 17:04:38 -04002757 }
2758
2759 @Override
2760 public ParceledListSlice<NotificationChannelGroup>
2761 getNotificationChannelGroupsFromPrivilegedListener(
Julia Reynoldsf27d6b22017-04-13 15:48:16 -04002762 INotificationListener token, String pkg, UserHandle user) throws RemoteException {
2763 Preconditions.checkNotNull(pkg);
2764 Preconditions.checkNotNull(user);
2765 verifyPrivilegedListener(token, user);
2766
2767 List<NotificationChannelGroup> groups = new ArrayList<>();
2768 groups.addAll(mRankingHelper.getNotificationChannelGroups(
2769 pkg, getUidForPackageAndUser(pkg, user)));
2770 return new ParceledListSlice<>(groups);
2771 }
2772
2773 private void verifyPrivilegedListener(INotificationListener token, UserHandle user) {
Julia Reynoldsfeb73412017-04-18 09:28:22 -04002774 ManagedServiceInfo info;
2775 synchronized (mNotificationLock) {
2776 info = mListeners.checkServiceTokenLocked(token);
2777 }
Julia Reynoldsda781472017-04-12 09:41:16 -04002778 if (!hasCompanionDevice(info)) {
2779 throw new SecurityException(info + " does not have access");
2780 }
Julia Reynoldsf27d6b22017-04-13 15:48:16 -04002781 if (!info.enabledAndUserMatches(user.getIdentifier())) {
2782 throw new SecurityException(info + " does not have access");
2783 }
2784 }
Julia Reynolds73ed76b2017-04-04 17:04:38 -04002785
Julia Reynoldsf27d6b22017-04-13 15:48:16 -04002786 private int getUidForPackageAndUser(String pkg, UserHandle user) throws RemoteException {
2787 int uid = 0;
2788 long identity = Binder.clearCallingIdentity();
2789 try {
2790 uid = mPackageManager.getPackageUid(pkg, 0, user.getIdentifier());
2791 } finally {
2792 Binder.restoreCallingIdentity(identity);
2793 }
2794 return uid;
Julia Reynolds73ed76b2017-04-04 17:04:38 -04002795 }
John Spurlock1fc476d2015-04-14 16:05:20 -04002796 };
John Spurlocka4294292014-03-24 18:02:32 -04002797
Julia Reynolds4b82f6d2017-01-04 10:47:41 -05002798 private void applyAdjustment(NotificationRecord n, Adjustment adjustment) {
Julia Reynoldse46bb372016-03-17 11:05:58 -04002799 if (n == null) {
2800 return;
2801 }
Julia Reynoldse46bb372016-03-17 11:05:58 -04002802 if (adjustment.getSignals() != null) {
2803 Bundle.setDefusable(adjustment.getSignals(), true);
Julia Reynolds22f02b32016-12-01 15:05:13 -05002804 final ArrayList<String> people =
2805 adjustment.getSignals().getStringArrayList(Adjustment.KEY_PEOPLE);
2806 final ArrayList<SnoozeCriterion> snoozeCriterionList =
2807 adjustment.getSignals().getParcelableArrayList(Adjustment.KEY_SNOOZE_CRITERIA);
Julia Reynolds22f02b32016-12-01 15:05:13 -05002808 n.setPeopleOverride(people);
2809 n.setSnoozeCriteria(snoozeCriterionList);
Julia Reynoldse46bb372016-03-17 11:05:58 -04002810 }
2811 }
2812
Julia Reynolds88860ce2017-06-01 16:55:49 -04002813 @GuardedBy("mNotificationLock")
Julia Reynolds8f488d32016-10-14 10:59:01 -04002814 private void addAutogroupKeyLocked(String key) {
2815 NotificationRecord n = mNotificationsByKey.get(key);
2816 if (n == null) {
2817 return;
2818 }
Chris Wrenb3921792017-06-01 13:34:46 -04002819 n.setOverrideGroupKey(GroupHelper.AUTOGROUP_KEY);
Julia Reynolds8f488d32016-10-14 10:59:01 -04002820 EventLogTags.writeNotificationAutogrouped(key);
2821 }
2822
Julia Reynolds88860ce2017-06-01 16:55:49 -04002823 @GuardedBy("mNotificationLock")
Julia Reynolds8f488d32016-10-14 10:59:01 -04002824 private void removeAutogroupKeyLocked(String key) {
2825 NotificationRecord n = mNotificationsByKey.get(key);
2826 if (n == null) {
2827 return;
2828 }
Chris Wrenb3921792017-06-01 13:34:46 -04002829 n.setOverrideGroupKey(null);
Julia Reynolds8f488d32016-10-14 10:59:01 -04002830 EventLogTags.writeNotificationUnautogrouped(key);
2831 }
2832
2833 // Clears the 'fake' auto-group summary.
Julia Reynolds88860ce2017-06-01 16:55:49 -04002834 @GuardedBy("mNotificationLock")
Julia Reynolds8f488d32016-10-14 10:59:01 -04002835 private void clearAutogroupSummaryLocked(int userId, String pkg) {
2836 ArrayMap<String, String> summaries = mAutobundledSummaries.get(userId);
2837 if (summaries != null && summaries.containsKey(pkg)) {
2838 // Clear summary.
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05002839 final NotificationRecord removed = findNotificationByKeyLocked(summaries.remove(pkg));
Julia Reynolds8f488d32016-10-14 10:59:01 -04002840 if (removed != null) {
Julia Reynolds0839c022017-06-15 15:24:01 -04002841 boolean wasPosted = removeFromNotificationListsLocked(removed);
2842 cancelNotificationLocked(removed, false, REASON_UNAUTOBUNDLED, wasPosted);
Julia Reynoldse46bb372016-03-17 11:05:58 -04002843 }
2844 }
2845 }
2846
2847 // Posts a 'fake' summary for a package that has exceeded the solo-notification limit.
Julia Reynolds8f488d32016-10-14 10:59:01 -04002848 private void createAutoGroupSummary(int userId, String pkg, String triggeringKey) {
2849 NotificationRecord summaryRecord = null;
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05002850 synchronized (mNotificationLock) {
Julia Reynolds8f488d32016-10-14 10:59:01 -04002851 NotificationRecord notificationRecord = mNotificationsByKey.get(triggeringKey);
2852 if (notificationRecord == null) {
2853 // The notification could have been cancelled again already. A successive
2854 // adjustment will post a summary if needed.
2855 return;
Julia Reynoldse46bb372016-03-17 11:05:58 -04002856 }
Julia Reynolds8f488d32016-10-14 10:59:01 -04002857 final StatusBarNotification adjustedSbn = notificationRecord.sbn;
2858 userId = adjustedSbn.getUser().getIdentifier();
2859 ArrayMap<String, String> summaries = mAutobundledSummaries.get(userId);
2860 if (summaries == null) {
2861 summaries = new ArrayMap<>();
2862 }
2863 mAutobundledSummaries.put(userId, summaries);
2864 if (!summaries.containsKey(pkg)) {
2865 // Add summary
2866 final ApplicationInfo appInfo =
2867 adjustedSbn.getNotification().extras.getParcelable(
2868 Notification.EXTRA_BUILDER_APPLICATION_INFO);
2869 final Bundle extras = new Bundle();
2870 extras.putParcelable(Notification.EXTRA_BUILDER_APPLICATION_INFO, appInfo);
Geoffrey Pitschaf759c52017-02-15 09:35:38 -05002871 final String channelId = notificationRecord.getChannel().getId();
Julia Reynolds8f488d32016-10-14 10:59:01 -04002872 final Notification summaryNotification =
Geoffrey Pitschaf759c52017-02-15 09:35:38 -05002873 new Notification.Builder(getContext(), channelId)
2874 .setSmallIcon(adjustedSbn.getNotification().getSmallIcon())
Julia Reynolds8f488d32016-10-14 10:59:01 -04002875 .setGroupSummary(true)
Julia Reynolds9d5786e2017-04-28 10:26:32 -04002876 .setGroupAlertBehavior(Notification.GROUP_ALERT_CHILDREN)
Julia Reynolds8f488d32016-10-14 10:59:01 -04002877 .setGroup(GroupHelper.AUTOGROUP_KEY)
2878 .setFlag(Notification.FLAG_AUTOGROUP_SUMMARY, true)
2879 .setFlag(Notification.FLAG_GROUP_SUMMARY, true)
2880 .setColor(adjustedSbn.getNotification().color)
2881 .setLocalOnly(true)
2882 .build();
2883 summaryNotification.extras.putAll(extras);
2884 Intent appIntent = getContext().getPackageManager().getLaunchIntentForPackage(pkg);
2885 if (appIntent != null) {
2886 summaryNotification.contentIntent = PendingIntent.getActivityAsUser(
2887 getContext(), 0, appIntent, 0, null, UserHandle.of(userId));
2888 }
2889 final StatusBarNotification summarySbn =
2890 new StatusBarNotification(adjustedSbn.getPackageName(),
Julia Reynolds423b9fc2016-11-09 09:51:08 -05002891 adjustedSbn.getOpPkg(),
Julia Reynolds423b9fc2016-11-09 09:51:08 -05002892 Integer.MAX_VALUE,
Julia Reynolds8f488d32016-10-14 10:59:01 -04002893 GroupHelper.AUTOGROUP_KEY, adjustedSbn.getUid(),
2894 adjustedSbn.getInitialPid(), summaryNotification,
2895 adjustedSbn.getUser(), GroupHelper.AUTOGROUP_KEY,
2896 System.currentTimeMillis());
Julia Reynolds924eed12017-01-19 09:52:07 -05002897 summaryRecord = new NotificationRecord(getContext(), summarySbn,
Geoffrey Pitscha22f6442017-05-05 16:47:38 +00002898 notificationRecord.getChannel());
Julia Reynolds8f488d32016-10-14 10:59:01 -04002899 summaries.put(pkg, summarySbn.getKey());
2900 }
2901 }
Julia Reynoldsd94054f2017-02-01 11:11:06 -05002902 if (summaryRecord != null && checkDisqualifyingFeatures(userId, MY_UID,
2903 summaryRecord.sbn.getId(), summaryRecord.sbn.getTag(), summaryRecord)) {
Julia Reynolds8f488d32016-10-14 10:59:01 -04002904 mHandler.post(new EnqueueNotificationRunnable(userId, summaryRecord));
Julia Reynoldse46bb372016-03-17 11:05:58 -04002905 }
2906 }
2907
John Spurlock32fe4c62014-10-02 12:16:02 -04002908 private String disableNotificationEffects(NotificationRecord record) {
2909 if (mDisableNotificationEffects) {
2910 return "booleanState";
2911 }
2912 if ((mListenerHints & HINT_HOST_DISABLE_EFFECTS) != 0) {
2913 return "listenerHints";
2914 }
2915 if (mCallState != TelephonyManager.CALL_STATE_IDLE && !mZenModeHelper.isCall(record)) {
2916 return "callState";
2917 }
2918 return null;
Chris Wrene4b38802015-07-07 15:54:19 -04002919 };
2920
2921 private void dumpJson(PrintWriter pw, DumpFilter filter) {
2922 JSONObject dump = new JSONObject();
2923 try {
2924 dump.put("service", "Notification Manager");
Chris Wrenacf424a2016-03-15 12:48:55 -04002925 dump.put("bans", mRankingHelper.dumpBansJson(filter));
2926 dump.put("ranking", mRankingHelper.dumpJson(filter));
Chris Wrene4b38802015-07-07 15:54:19 -04002927 dump.put("stats", mUsageStats.dumpJson(filter));
Julia Reynoldsd373d782017-03-03 13:32:57 -05002928 dump.put("channels", mRankingHelper.dumpChannelsJson(filter));
Chris Wrene4b38802015-07-07 15:54:19 -04002929 } catch (JSONException e) {
2930 e.printStackTrace();
2931 }
2932 pw.println(dump);
John Spurlock1fa865f2014-07-21 14:56:39 -04002933 }
2934
Julia Reynoldsc9842c12017-02-07 12:46:41 -05002935 private void dumpProto(FileDescriptor fd, DumpFilter filter) {
2936 final ProtoOutputStream proto = new ProtoOutputStream(fd);
2937 synchronized (mNotificationLock) {
2938 long records = proto.start(NotificationServiceDumpProto.RECORDS);
2939 int N = mNotificationList.size();
2940 if (N > 0) {
2941 for (int i = 0; i < N; i++) {
2942 final NotificationRecord nr = mNotificationList.get(i);
2943 if (filter.filtered && !filter.matches(nr.sbn)) continue;
2944 nr.dump(proto, filter.redact);
2945 proto.write(NotificationRecordProto.STATE, NotificationServiceProto.POSTED);
2946 }
2947 }
2948 N = mEnqueuedNotifications.size();
2949 if (N > 0) {
2950 for (int i = 0; i < N; i++) {
2951 final NotificationRecord nr = mEnqueuedNotifications.get(i);
2952 if (filter.filtered && !filter.matches(nr.sbn)) continue;
2953 nr.dump(proto, filter.redact);
2954 proto.write(NotificationRecordProto.STATE, NotificationServiceProto.ENQUEUED);
2955 }
2956 }
Julia Reynolds520df6e2017-02-13 09:05:10 -05002957 List<NotificationRecord> snoozed = mSnoozeHelper.getSnoozed();
2958 N = snoozed.size();
2959 if (N > 0) {
2960 for (int i = 0; i < N; i++) {
2961 final NotificationRecord nr = snoozed.get(i);
2962 if (filter.filtered && !filter.matches(nr.sbn)) continue;
2963 nr.dump(proto, filter.redact);
2964 proto.write(NotificationRecordProto.STATE, NotificationServiceProto.SNOOZED);
2965 }
2966 }
Julia Reynoldsc9842c12017-02-07 12:46:41 -05002967 proto.end(records);
2968 }
Julia Reynolds520df6e2017-02-13 09:05:10 -05002969
2970 long zenLog = proto.start(NotificationServiceDumpProto.ZEN);
2971 mZenModeHelper.dump(proto);
2972 for (ComponentName suppressor : mEffectsSuppressors) {
2973 proto.write(ZenModeProto.SUPPRESSORS, suppressor.toString());
2974 }
2975 proto.end(zenLog);
2976
Julia Reynoldsc9842c12017-02-07 12:46:41 -05002977 proto.flush();
2978 }
2979
John Spurlock25e2d242014-06-27 13:58:23 -04002980 void dumpImpl(PrintWriter pw, DumpFilter filter) {
2981 pw.print("Current Notification Manager state");
Dan Sandlera1770312015-07-10 13:59:29 -04002982 if (filter.filtered) {
John Spurlock50806fc2014-07-15 10:22:02 -04002983 pw.print(" (filtered to "); pw.print(filter); pw.print(")");
John Spurlock25e2d242014-06-27 13:58:23 -04002984 }
2985 pw.println(':');
Adam Lesinski182f73f2013-12-05 16:48:06 -08002986 int N;
Julia Reynoldse6b53e62015-07-31 09:25:10 -04002987 final boolean zenOnly = filter.filtered && filter.zen;
Adam Lesinski182f73f2013-12-05 16:48:06 -08002988
John Spurlock50806fc2014-07-15 10:22:02 -04002989 if (!zenOnly) {
2990 synchronized (mToastQueue) {
2991 N = mToastQueue.size();
2992 if (N > 0) {
2993 pw.println(" Toast Queue:");
2994 for (int i=0; i<N; i++) {
2995 mToastQueue.get(i).dump(pw, " ", filter);
2996 }
2997 pw.println(" ");
Adam Lesinski182f73f2013-12-05 16:48:06 -08002998 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08002999 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08003000 }
3001
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05003002 synchronized (mNotificationLock) {
John Spurlock50806fc2014-07-15 10:22:02 -04003003 if (!zenOnly) {
3004 N = mNotificationList.size();
John Spurlock25e2d242014-06-27 13:58:23 -04003005 if (N > 0) {
John Spurlock50806fc2014-07-15 10:22:02 -04003006 pw.println(" Notification List:");
John Spurlock25e2d242014-06-27 13:58:23 -04003007 for (int i=0; i<N; i++) {
John Spurlock50806fc2014-07-15 10:22:02 -04003008 final NotificationRecord nr = mNotificationList.get(i);
Julia Reynoldse6b53e62015-07-31 09:25:10 -04003009 if (filter.filtered && !filter.matches(nr.sbn)) continue;
Dan Sandlera1770312015-07-10 13:59:29 -04003010 nr.dump(pw, " ", getContext(), filter.redact);
John Spurlock25e2d242014-06-27 13:58:23 -04003011 }
3012 pw.println(" ");
Adam Lesinski182f73f2013-12-05 16:48:06 -08003013 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08003014
Julia Reynoldse6b53e62015-07-31 09:25:10 -04003015 if (!filter.filtered) {
John Spurlock50806fc2014-07-15 10:22:02 -04003016 N = mLights.size();
3017 if (N > 0) {
3018 pw.println(" Lights List:");
3019 for (int i=0; i<N; i++) {
Chris Wren6054e612014-11-25 17:16:46 -05003020 if (i == N - 1) {
3021 pw.print(" > ");
3022 } else {
3023 pw.print(" ");
3024 }
3025 pw.println(mLights.get(i));
John Spurlock50806fc2014-07-15 10:22:02 -04003026 }
3027 pw.println(" ");
3028 }
John Spurlockcb566aa2014-08-03 22:58:28 -04003029 pw.println(" mUseAttentionLight=" + mUseAttentionLight);
3030 pw.println(" mNotificationPulseEnabled=" + mNotificationPulseEnabled);
Chris Wren6054e612014-11-25 17:16:46 -05003031 pw.println(" mSoundNotificationKey=" + mSoundNotificationKey);
3032 pw.println(" mVibrateNotificationKey=" + mVibrateNotificationKey);
John Spurlockd8afe3c2014-08-01 14:04:07 -04003033 pw.println(" mDisableNotificationEffects=" + mDisableNotificationEffects);
John Spurlock32fe4c62014-10-02 12:16:02 -04003034 pw.println(" mCallState=" + callStateToString(mCallState));
John Spurlock50806fc2014-07-15 10:22:02 -04003035 pw.println(" mSystemReady=" + mSystemReady);
Chris Wren763a9bb2016-05-31 17:14:12 -04003036 pw.println(" mMaxPackageEnqueueRate=" + mMaxPackageEnqueueRate);
John Spurlock50806fc2014-07-15 10:22:02 -04003037 }
3038 pw.println(" mArchive=" + mArchive.toString());
3039 Iterator<StatusBarNotification> iter = mArchive.descendingIterator();
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05003040 int j=0;
John Spurlock50806fc2014-07-15 10:22:02 -04003041 while (iter.hasNext()) {
3042 final StatusBarNotification sbn = iter.next();
3043 if (filter != null && !filter.matches(sbn)) continue;
3044 pw.println(" " + sbn);
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05003045 if (++j >= 5) {
John Spurlock50806fc2014-07-15 10:22:02 -04003046 if (iter.hasNext()) pw.println(" ...");
3047 break;
3048 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08003049 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08003050
Julia Reynolds4b82f6d2017-01-04 10:47:41 -05003051 if (!zenOnly) {
3052 N = mEnqueuedNotifications.size();
3053 if (N > 0) {
3054 pw.println(" Enqueued Notification List:");
3055 for (int i = 0; i < N; i++) {
3056 final NotificationRecord nr = mEnqueuedNotifications.get(i);
3057 if (filter.filtered && !filter.matches(nr.sbn)) continue;
3058 nr.dump(pw, " ", getContext(), filter.redact);
3059 }
3060 pw.println(" ");
3061 }
Julia Reynolds520df6e2017-02-13 09:05:10 -05003062
3063 mSnoozeHelper.dump(pw, filter);
Julia Reynolds4b82f6d2017-01-04 10:47:41 -05003064 }
3065 }
3066
John Spurlock50806fc2014-07-15 10:22:02 -04003067 if (!zenOnly) {
John Spurlock50806fc2014-07-15 10:22:02 -04003068 pw.println("\n Ranking Config:");
3069 mRankingHelper.dump(pw, " ", filter);
Chris Wren54bbef42014-07-09 18:37:56 -04003070
John Spurlock50806fc2014-07-15 10:22:02 -04003071 pw.println("\n Notification listeners:");
3072 mListeners.dump(pw, filter);
John Spurlockd8afe3c2014-08-01 14:04:07 -04003073 pw.print(" mListenerHints: "); pw.println(mListenerHints);
3074 pw.print(" mListenersDisablingEffects: (");
3075 N = mListenersDisablingEffects.size();
John Spurlock1fa865f2014-07-21 14:56:39 -04003076 for (int i = 0; i < N; i++) {
Bryce Lee7219ada2016-04-08 10:54:23 -07003077 final int hint = mListenersDisablingEffects.keyAt(i);
3078 if (i > 0) pw.print(';');
3079 pw.print("hint[" + hint + "]:");
3080
3081 final ArraySet<ManagedServiceInfo> listeners =
3082 mListenersDisablingEffects.valueAt(i);
3083 final int listenerSize = listeners.size();
3084
3085 for (int j = 0; j < listenerSize; j++) {
3086 if (i > 0) pw.print(',');
3087 final ManagedServiceInfo listener = listeners.valueAt(i);
3088 pw.print(listener.component);
3089 }
John Spurlock1fa865f2014-07-21 14:56:39 -04003090 }
3091 pw.println(')');
Julia Reynolds77b2cc92016-11-08 14:41:09 -05003092 pw.println("\n Notification assistant services:");
3093 mNotificationAssistants.dump(pw, filter);
John Spurlock50806fc2014-07-15 10:22:02 -04003094 }
Julia Reynolds72f1cbb2016-09-19 14:57:31 -04003095
Julia Reynolds520df6e2017-02-13 09:05:10 -05003096 if (!filter.filtered || zenOnly) {
3097 pw.println("\n Zen Mode:");
3098 pw.print(" mInterruptionFilter="); pw.println(mInterruptionFilter);
3099 mZenModeHelper.dump(pw, " ");
3100
3101 pw.println("\n Zen Log:");
3102 ZenLog.dump(pw, " ");
Julia Reynolds72f1cbb2016-09-19 14:57:31 -04003103 }
3104
John Spurlock80774932015-05-07 17:38:50 -04003105 pw.println("\n Policy access:");
3106 pw.print(" mPolicyAccess: "); pw.println(mPolicyAccess);
John Spurlocke77bb362014-04-26 10:24:59 -04003107
3108 pw.println("\n Condition providers:");
John Spurlock25e2d242014-06-27 13:58:23 -04003109 mConditionProviders.dump(pw, filter);
Christoph Studer265c1052014-07-23 17:14:33 +02003110
3111 pw.println("\n Group summaries:");
3112 for (Entry<String, NotificationRecord> entry : mSummaryByGroupKey.entrySet()) {
3113 NotificationRecord r = entry.getValue();
3114 pw.println(" " + entry.getKey() + " -> " + r.getKey());
3115 if (mNotificationsByKey.get(r.getKey()) != r) {
3116 pw.println("!!!!!!LEAK: Record not found in mNotificationsByKey.");
Dan Sandlera1770312015-07-10 13:59:29 -04003117 r.dump(pw, " ", getContext(), filter.redact);
Christoph Studer265c1052014-07-23 17:14:33 +02003118 }
3119 }
Julia Reynolds520df6e2017-02-13 09:05:10 -05003120
3121 if (!zenOnly) {
3122 pw.println("\n Usage Stats:");
3123 mUsageStats.dump(pw, " ", filter);
3124 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003125 }
3126 }
3127
Adam Lesinski182f73f2013-12-05 16:48:06 -08003128 /**
3129 * The private API only accessible to the system process.
3130 */
3131 private final NotificationManagerInternal mInternalService = new NotificationManagerInternal() {
3132 @Override
Christoph Studer8fd7f1e2014-04-11 17:35:05 -04003133 public void enqueueNotification(String pkg, String opPkg, int callingUid, int callingPid,
Julia Reynoldsfea6f7b2017-04-19 13:50:12 -04003134 String tag, int id, Notification notification, int userId) {
Christoph Studer8fd7f1e2014-04-11 17:35:05 -04003135 enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, notification,
Julia Reynoldsfea6f7b2017-04-19 13:50:12 -04003136 userId);
Adam Lesinski182f73f2013-12-05 16:48:06 -08003137 }
Christoph Studer365e4c32014-09-18 20:35:36 +02003138
3139 @Override
3140 public void removeForegroundServiceFlagFromNotification(String pkg, int notificationId,
3141 int userId) {
3142 checkCallerIsSystem();
Geoffrey Pitsch87237d72017-04-13 13:44:09 -04003143 mHandler.post(new Runnable() {
3144 @Override
3145 public void run() {
3146 synchronized (mNotificationLock) {
Julia Reynoldsfeb73412017-04-18 09:28:22 -04003147 removeForegroundServiceFlagByListLocked(
3148 mEnqueuedNotifications, pkg, notificationId, userId);
3149 removeForegroundServiceFlagByListLocked(
3150 mNotificationList, pkg, notificationId, userId);
Julia Reynolds8f488d32016-10-14 10:59:01 -04003151 }
Geoffrey Pitsch87237d72017-04-13 13:44:09 -04003152 }
3153 });
3154 }
3155
Julia Reynolds88860ce2017-06-01 16:55:49 -04003156 @GuardedBy("mNotificationLock")
Geoffrey Pitsch87237d72017-04-13 13:44:09 -04003157 private void removeForegroundServiceFlagByListLocked(
Julia Reynolds88860ce2017-06-01 16:55:49 -04003158 ArrayList<NotificationRecord> notificationList, String pkg, int notificationId,
3159 int userId) {
3160 NotificationRecord r = findNotificationByListLocked(
3161 notificationList, pkg, null, notificationId, userId);
Geoffrey Pitsch87237d72017-04-13 13:44:09 -04003162 if (r == null) {
3163 return;
Christoph Studer365e4c32014-09-18 20:35:36 +02003164 }
Geoffrey Pitsch87237d72017-04-13 13:44:09 -04003165 StatusBarNotification sbn = r.sbn;
3166 // NoMan adds flags FLAG_NO_CLEAR and FLAG_ONGOING_EVENT when it sees
3167 // FLAG_FOREGROUND_SERVICE. Hence it's not enough to remove
3168 // FLAG_FOREGROUND_SERVICE, we have to revert to the flags we received
3169 // initially *and* force remove FLAG_FOREGROUND_SERVICE.
3170 sbn.getNotification().flags =
3171 (r.mOriginalFlags & ~Notification.FLAG_FOREGROUND_SERVICE);
3172 mRankingHelper.sort(mNotificationList);
3173 mListeners.notifyPostedLocked(sbn, sbn /* oldSbn */);
3174 mGroupHelper.onNotificationPosted(sbn);
Christoph Studer365e4c32014-09-18 20:35:36 +02003175 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08003176 };
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003177
Christoph Studer8fd7f1e2014-04-11 17:35:05 -04003178 void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,
Scott Greenwald9b05c612013-06-25 23:44:05 -04003179 final int callingPid, final String tag, final int id, final Notification notification,
Julia Reynoldsfea6f7b2017-04-19 13:50:12 -04003180 int incomingUserId) {
Daniel Sandler0da673f2012-04-11 12:33:16 -04003181 if (DBG) {
Adam Lesinski182f73f2013-12-05 16:48:06 -08003182 Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id
3183 + " notification=" + notification);
Daniel Sandler0da673f2012-04-11 12:33:16 -04003184 }
John Spurlock7340fc82014-04-24 18:50:12 -04003185 checkCallerIsSystemOrSameApp(pkg);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003186
Scott Greenwald9b05c612013-06-25 23:44:05 -04003187 final int userId = ActivityManager.handleIncomingUser(callingPid,
3188 callingUid, incomingUserId, true, false, "enqueueNotification", pkg);
Jeff Sharkey65c4a2b2012-09-25 17:22:27 -07003189 final UserHandle user = new UserHandle(userId);
Dianne Hackborn41203752012-08-31 14:05:51 -07003190
Julia Reynoldsd94054f2017-02-01 11:11:06 -05003191 if (pkg == null || notification == null) {
3192 throw new IllegalArgumentException("null not allowed: pkg=" + pkg
3193 + " id=" + id + " notification=" + notification);
3194 }
Svetoslav Ganov2a67ee82017-02-16 18:57:57 -08003195
3196 // The system can post notifications for any package, let us resolve that.
3197 final int notificationUid = resolveNotificationUid(opPkg, callingUid, userId);
3198
Julia Reynoldse46bb372016-03-17 11:05:58 -04003199 // Fix the notification as best we can.
3200 try {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05003201 final ApplicationInfo ai = mPackageManagerClient.getApplicationInfoAsUser(
Jeff Sharkey012bc7b2016-04-11 16:30:27 -06003202 pkg, PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
3203 (userId == UserHandle.USER_ALL) ? UserHandle.USER_SYSTEM : userId);
Julia Reynoldse071abd2017-03-22 10:52:11 -04003204 Notification.addFieldsFromContext(ai, notification);
Julia Reynoldsd71c5a92017-06-30 13:34:01 -04003205
Dan Sandler708a1ca2017-08-11 20:30:24 -04003206 boolean canColorize = PERMISSION_GRANTED == mPackageManagerClient.checkPermission(
3207 "com.android.permission.USE_COLORIZED_NOTIFICATIONS", pkg);
3208 if (canColorize) {
Julia Reynoldsd71c5a92017-06-30 13:34:01 -04003209 notification.flags |= Notification.FLAG_CAN_COLORIZE;
3210 } else {
3211 notification.flags &= ~Notification.FLAG_CAN_COLORIZE;
3212 }
3213
Julia Reynoldse46bb372016-03-17 11:05:58 -04003214 } catch (NameNotFoundException e) {
3215 Slog.e(TAG, "Cannot create a context for sending app", e);
3216 return;
3217 }
3218
Chris Wren888b7a82016-06-17 15:47:19 -04003219 mUsageStats.registerEnqueuedByApp(pkg);
3220
Julia Reynoldsd94054f2017-02-01 11:11:06 -05003221 // setup local book-keeping
Julia Reynoldsbad42972017-04-25 13:52:49 -04003222 String channelId = notification.getChannelId();
3223 if (mIsTelevision && (new Notification.TvExtender(notification)).getChannelId() != null) {
3224 channelId = (new Notification.TvExtender(notification)).getChannelId();
Julia Reynolds5f20e9f2017-01-30 08:54:53 -05003225 }
Geoffrey Pitsch1f17e022017-01-03 16:44:20 -05003226 final NotificationChannel channel = mRankingHelper.getNotificationChannel(pkg,
Svetoslav Ganov2a67ee82017-02-16 18:57:57 -08003227 notificationUid, channelId, false /* includeDeleted */);
Geoffrey Pitsch1f17e022017-01-03 16:44:20 -05003228 if (channel == null) {
Geoffrey Pitsch96cac7f2017-03-23 17:57:12 -04003229 final String noChannelStr = "No Channel found for "
3230 + "pkg=" + pkg
3231 + ", channelId=" + channelId
Julia Reynoldsf26eb912017-05-22 15:47:06 -04003232 + ", id=" + id
3233 + ", tag=" + tag
Geoffrey Pitsch96cac7f2017-03-23 17:57:12 -04003234 + ", opPkg=" + opPkg
3235 + ", callingUid=" + callingUid
3236 + ", userId=" + userId
3237 + ", incomingUserId=" + incomingUserId
3238 + ", notificationUid=" + notificationUid
3239 + ", notification=" + notification;
Geoffrey Pitsch96cac7f2017-03-23 17:57:12 -04003240 Log.e(TAG, noChannelStr);
Geoffrey Pitsch4c6eef22017-04-19 10:26:45 -04003241 doChannelWarningToast("Developer warning for package \"" + pkg + "\"\n" +
Geoffrey Pitschcadb5dc2017-04-11 11:35:02 -04003242 "Failed to post notification on channel \"" + channelId + "\"\n" +
Geoffrey Pitsch86c11e602017-04-17 15:28:40 -04003243 "See log for more details");
Geoffrey Pitsch96cac7f2017-03-23 17:57:12 -04003244 return;
Geoffrey Pitsch1f17e022017-01-03 16:44:20 -05003245 }
Geoffrey Pitsch86c11e602017-04-17 15:28:40 -04003246
Chris Wrena61f1792016-08-04 11:24:42 -04003247 final StatusBarNotification n = new StatusBarNotification(
Svetoslav Ganov2a67ee82017-02-16 18:57:57 -08003248 pkg, opPkg, id, tag, notificationUid, callingPid, notification,
Julia Reynolds423b9fc2016-11-09 09:51:08 -05003249 user, null, System.currentTimeMillis());
Geoffrey Pitscha22f6442017-05-05 16:47:38 +00003250 final NotificationRecord r = new NotificationRecord(getContext(), n, channel);
Chris Wrena61f1792016-08-04 11:24:42 -04003251
Julia Reynolds4da79702017-06-01 11:06:10 -04003252 if (!checkDisqualifyingFeatures(userId, notificationUid, id, tag, r)) {
Julia Reynoldsd94054f2017-02-01 11:11:06 -05003253 return;
Joe Onoratobd73d012010-06-04 11:44:54 -07003254 }
3255
Felipe Lemedd85da62016-06-28 11:29:54 -07003256 // Whitelist pending intents.
3257 if (notification.allPendingIntents != null) {
3258 final int intentCount = notification.allPendingIntents.size();
3259 if (intentCount > 0) {
3260 final ActivityManagerInternal am = LocalServices
3261 .getService(ActivityManagerInternal.class);
3262 final long duration = LocalServices.getService(
3263 DeviceIdleController.LocalService.class).getNotificationWhitelistDuration();
3264 for (int i = 0; i < intentCount; i++) {
3265 PendingIntent pendingIntent = notification.allPendingIntents.valueAt(i);
3266 if (pendingIntent != null) {
Dianne Hackborn98305522017-05-05 17:53:53 -07003267 am.setPendingIntentWhitelistDuration(pendingIntent.getTarget(),
3268 WHITELIST_TOKEN, duration);
Felipe Lemedd85da62016-06-28 11:29:54 -07003269 }
3270 }
3271 }
3272 }
Felipe Lemea1b79bf2016-05-24 13:06:54 -07003273
Chris Wren47633422016-01-22 09:56:59 -05003274 mHandler.post(new EnqueueNotificationRunnable(userId, r));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003275 }
3276
Geoffrey Pitsch4c6eef22017-04-19 10:26:45 -04003277 private void doChannelWarningToast(CharSequence toastText) {
Geoffrey Pitsch507822d2017-05-11 12:57:22 -04003278 final int defaultWarningEnabled = Build.IS_DEBUGGABLE ? 1 : 0;
Geoffrey Pitsch2486f892017-05-22 10:53:44 -04003279 final boolean warningEnabled = Settings.Global.getInt(getContext().getContentResolver(),
Geoffrey Pitsch507822d2017-05-11 12:57:22 -04003280 Settings.Global.SHOW_NOTIFICATION_CHANNEL_WARNINGS, defaultWarningEnabled) != 0;
3281 if (warningEnabled) {
Geoffrey Pitsch2486f892017-05-22 10:53:44 -04003282 Toast toast = Toast.makeText(getContext(), mHandler.getLooper(), toastText,
Geoffrey Pitsch5bdddbd2017-05-26 10:50:05 -04003283 Toast.LENGTH_SHORT);
Geoffrey Pitschd34c1872017-05-04 16:02:15 -04003284 toast.show();
Geoffrey Pitsch86c11e602017-04-17 15:28:40 -04003285 }
3286 }
3287
Svetoslav Ganov2a67ee82017-02-16 18:57:57 -08003288 private int resolveNotificationUid(String opPackageName, int callingUid, int userId) {
3289 // The system can post notifications on behalf of any package it wants
Geoffrey Pitsch27684152017-05-02 11:41:31 -04003290 if (isCallerSystemOrPhone() && opPackageName != null && !"android".equals(opPackageName)) {
Svetoslav Ganov2a67ee82017-02-16 18:57:57 -08003291 try {
3292 return getContext().getPackageManager()
3293 .getPackageUidAsUser(opPackageName, userId);
3294 } catch (NameNotFoundException e) {
3295 /* ignore */
3296 }
3297 }
3298 return callingUid;
3299 }
3300
Julia Reynoldsd94054f2017-02-01 11:11:06 -05003301 /**
3302 * Checks if a notification can be posted. checks rate limiter, snooze helper, and blocking.
3303 *
3304 * Has side effects.
3305 */
3306 private boolean checkDisqualifyingFeatures(int userId, int callingUid, int id, String tag,
3307 NotificationRecord r) {
3308 final String pkg = r.sbn.getPackageName();
Sailesh Nepale8bde702017-07-21 11:44:04 -07003309 final String dialerPackage =
3310 getContext().getSystemService(TelecomManager.class).getSystemDialerPackage();
Geoffrey Pitsch27684152017-05-02 11:41:31 -04003311 final boolean isSystemNotification =
Sailesh Nepale8bde702017-07-21 11:44:04 -07003312 isUidSystemOrPhone(callingUid) || ("android".equals(pkg))
3313 || TextUtils.equals(pkg, dialerPackage);
Julia Reynoldsd94054f2017-02-01 11:11:06 -05003314 final boolean isNotificationFromListener = mListeners.isListenerPackage(pkg);
3315
3316 // Limit the number of notifications that any given package except the android
3317 // package or a registered listener can enqueue. Prevents DOS attacks and deals with leaks.
3318 if (!isSystemNotification && !isNotificationFromListener) {
3319 synchronized (mNotificationLock) {
Julia Reynolds6ad0aec2017-07-05 08:47:03 -04003320 if (mNotificationsByKey.get(r.sbn.getKey()) == null && isCallerInstantApp(pkg)) {
3321 // Ephemeral apps have some special constraints for notifications.
3322 // They are not allowed to create new notifications however they are allowed to
3323 // update notifications created by the system (e.g. a foreground service
3324 // notification).
3325 throw new SecurityException("Instant app " + pkg
3326 + " cannot create notifications");
3327 }
3328
3329 // rate limit updates that aren't completed progress notifications
3330 if (mNotificationsByKey.get(r.sbn.getKey()) != null
3331 && !r.getNotification().hasCompletedProgress()) {
3332
Julia Reynoldsd94054f2017-02-01 11:11:06 -05003333 final float appEnqueueRate = mUsageStats.getAppEnqueueRate(pkg);
3334 if (appEnqueueRate > mMaxPackageEnqueueRate) {
3335 mUsageStats.registerOverRateQuota(pkg);
3336 final long now = SystemClock.elapsedRealtime();
3337 if ((now - mLastOverRateLogTime) > MIN_PACKAGE_OVERRATE_LOG_INTERVAL) {
3338 Slog.e(TAG, "Package enqueue rate is " + appEnqueueRate
3339 + ". Shedding events. package=" + pkg);
3340 mLastOverRateLogTime = now;
3341 }
3342 return false;
3343 }
3344 }
3345
Julia Reynolds6ad0aec2017-07-05 08:47:03 -04003346 // limit the number of outstanding notificationrecords an app can have
3347 int count = getNotificationCountLocked(pkg, userId, id, tag);
3348 if (count >= MAX_PACKAGE_NOTIFICATIONS) {
3349 mUsageStats.registerOverCountQuota(pkg);
3350 Slog.e(TAG, "Package has already posted or enqueued " + count
3351 + " notifications. Not showing more. package=" + pkg);
3352 return false;
Julia Reynoldsd94054f2017-02-01 11:11:06 -05003353 }
3354 }
3355 }
3356
3357 // snoozed apps
3358 if (mSnoozeHelper.isSnoozed(userId, pkg, r.getKey())) {
Julia Reynolds520df6e2017-02-13 09:05:10 -05003359 MetricsLogger.action(r.getLogMaker()
3360 .setType(MetricsProto.MetricsEvent.TYPE_UPDATE)
3361 .setCategory(MetricsProto.MetricsEvent.NOTIFICATION_SNOOZED));
Julia Reynoldsd94054f2017-02-01 11:11:06 -05003362 if (DBG) {
3363 Slog.d(TAG, "Ignored enqueue for snoozed notification " + r.getKey());
3364 }
3365 mSnoozeHelper.update(userId, r);
3366 savePolicyFile();
3367 return false;
3368 }
3369
3370
3371 // blocked apps
3372 if (isBlocked(r, mUsageStats)) {
3373 return false;
3374 }
3375
3376 return true;
3377 }
3378
Julia Reynolds6ad0aec2017-07-05 08:47:03 -04003379 protected int getNotificationCountLocked(String pkg, int userId, int excludedId,
3380 String excludedTag) {
3381 int count = 0;
3382 final int N = mNotificationList.size();
3383 for (int i = 0; i < N; i++) {
3384 final NotificationRecord existing = mNotificationList.get(i);
3385 if (existing.sbn.getPackageName().equals(pkg)
3386 && existing.sbn.getUserId() == userId) {
3387 if (existing.sbn.getId() == excludedId
3388 && TextUtils.equals(existing.sbn.getTag(), excludedTag)) {
3389 continue;
3390 }
3391 count++;
3392 }
3393 }
3394 final int M = mEnqueuedNotifications.size();
3395 for (int i = 0; i < M; i++) {
3396 final NotificationRecord existing = mEnqueuedNotifications.get(i);
3397 if (existing.sbn.getPackageName().equals(pkg)
3398 && existing.sbn.getUserId() == userId) {
3399 count++;
3400 }
3401 }
3402 return count;
3403 }
3404
Julia Reynoldsd94054f2017-02-01 11:11:06 -05003405 protected boolean isBlocked(NotificationRecord r, NotificationUsageStats usageStats) {
3406 final String pkg = r.sbn.getPackageName();
3407 final int callingUid = r.sbn.getUid();
3408
3409 final boolean isPackageSuspended = isPackageSuspendedForUser(pkg, callingUid);
3410 if (isPackageSuspended) {
3411 Slog.e(TAG, "Suppressing notification from package due to package "
3412 + "suspended by administrator.");
3413 usageStats.registerSuspendedByAdmin(r);
3414 return isPackageSuspended;
3415 }
3416
Julia Reynolds4da79702017-06-01 11:06:10 -04003417 final boolean isBlocked =
3418 mRankingHelper.getImportance(pkg, callingUid) == NotificationManager.IMPORTANCE_NONE
Julia Reynolds5fe2eae2017-05-22 08:45:27 -04003419 || r.getChannel().getImportance() == NotificationManager.IMPORTANCE_NONE;
Julia Reynoldsd94054f2017-02-01 11:11:06 -05003420 if (isBlocked) {
3421 Slog.e(TAG, "Suppressing notification from package by user request.");
3422 usageStats.registerBlocked(r);
3423 }
3424 return isBlocked;
3425 }
3426
Julia Reynoldsa78cdff2017-04-26 10:19:25 -04003427 protected class SnoozeNotificationRunnable implements Runnable {
3428 private final String mKey;
3429 private final long mDuration;
3430 private final String mSnoozeCriterionId;
3431
3432 SnoozeNotificationRunnable(String key, long duration, String snoozeCriterionId) {
3433 mKey = key;
3434 mDuration = duration;
3435 mSnoozeCriterionId = snoozeCriterionId;
3436 }
3437
3438 @Override
3439 public void run() {
3440 synchronized (mNotificationLock) {
3441 final NotificationRecord r = findNotificationByKeyLocked(mKey);
3442 if (r != null) {
3443 snoozeLocked(r);
3444 }
3445 }
3446 }
3447
Julia Reynolds88860ce2017-06-01 16:55:49 -04003448 @GuardedBy("mNotificationLock")
Julia Reynoldsa78cdff2017-04-26 10:19:25 -04003449 void snoozeLocked(NotificationRecord r) {
3450 if (r.sbn.isGroup()) {
3451 final List<NotificationRecord> groupNotifications = findGroupNotificationsLocked(
3452 r.sbn.getPackageName(), r.sbn.getGroupKey(), r.sbn.getUserId());
3453 if (r.getNotification().isGroupSummary()) {
3454 // snooze summary and all children
3455 for (int i = 0; i < groupNotifications.size(); i++) {
3456 snoozeNotificationLocked(groupNotifications.get(i));
3457 }
3458 } else {
3459 // if there is a valid summary for this group, and we are snoozing the only
3460 // child, also snooze the summary
3461 if (mSummaryByGroupKey.containsKey(r.sbn.getGroupKey())) {
3462 if (groupNotifications.size() != 2) {
3463 snoozeNotificationLocked(r);
3464 } else {
3465 // snooze summary and the one child
3466 for (int i = 0; i < groupNotifications.size(); i++) {
3467 snoozeNotificationLocked(groupNotifications.get(i));
3468 }
3469 }
3470 } else {
3471 snoozeNotificationLocked(r);
3472 }
3473 }
3474 } else {
3475 // just snooze the one notification
3476 snoozeNotificationLocked(r);
3477 }
3478 }
3479
Julia Reynolds88860ce2017-06-01 16:55:49 -04003480 @GuardedBy("mNotificationLock")
Julia Reynoldsa78cdff2017-04-26 10:19:25 -04003481 void snoozeNotificationLocked(NotificationRecord r) {
3482 MetricsLogger.action(r.getLogMaker()
3483 .setCategory(MetricsEvent.NOTIFICATION_SNOOZED)
3484 .setType(MetricsEvent.TYPE_CLOSE)
3485 .addTaggedData(MetricsEvent.NOTIFICATION_SNOOZED_CRITERIA,
3486 mSnoozeCriterionId == null ? 0 : 1));
Julia Reynolds0839c022017-06-15 15:24:01 -04003487 boolean wasPosted = removeFromNotificationListsLocked(r);
3488 cancelNotificationLocked(r, false, REASON_SNOOZED, wasPosted);
Julia Reynoldsa78cdff2017-04-26 10:19:25 -04003489 updateLightsLocked();
3490 if (mSnoozeCriterionId != null) {
3491 mNotificationAssistants.notifyAssistantSnoozedLocked(r.sbn, mSnoozeCriterionId);
3492 mSnoozeHelper.snooze(r);
3493 } else {
3494 mSnoozeHelper.snooze(r, mDuration);
3495 }
3496 savePolicyFile();
3497 }
3498 }
3499
Julia Reynoldsbaff4002016-12-15 11:34:26 -05003500 protected class EnqueueNotificationRunnable implements Runnable {
Chris Wren47633422016-01-22 09:56:59 -05003501 private final NotificationRecord r;
3502 private final int userId;
3503
3504 EnqueueNotificationRunnable(int userId, NotificationRecord r) {
3505 this.userId = userId;
3506 this.r = r;
3507 };
3508
3509 @Override
3510 public void run() {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05003511 synchronized (mNotificationLock) {
Julia Reynolds573c6532017-01-24 17:44:38 -05003512 mEnqueuedNotifications.add(r);
Julia Reynolds2a128742016-11-28 14:29:25 -05003513 scheduleTimeoutLocked(r);
Julia Reynolds573c6532017-01-24 17:44:38 -05003514
Julia Reynolds4b82f6d2017-01-04 10:47:41 -05003515 final StatusBarNotification n = r.sbn;
3516 if (DBG) Slog.d(TAG, "EnqueueNotificationRunnable.run for: " + n.getKey());
3517 NotificationRecord old = mNotificationsByKey.get(n.getKey());
3518 if (old != null) {
3519 // Retain ranking information from previous record
3520 r.copyRankingInformation(old);
3521 }
3522
3523 final int callingUid = n.getUid();
3524 final int callingPid = n.getInitialPid();
3525 final Notification notification = n.getNotification();
3526 final String pkg = n.getPackageName();
3527 final int id = n.getId();
3528 final String tag = n.getTag();
3529
3530 // Handle grouped notifications and bail out early if we
3531 // can to avoid extracting signals.
3532 handleGroupedNotificationLocked(r, old, callingUid, callingPid);
3533
Julia Reynoldsa78cdff2017-04-26 10:19:25 -04003534 // if this is a group child, unsnooze parent summary
3535 if (n.isGroup() && notification.isGroupChild()) {
3536 mSnoozeHelper.repostGroupSummary(pkg, r.getUserId(), n.getGroupKey());
3537 }
3538
Julia Reynolds4b82f6d2017-01-04 10:47:41 -05003539 // This conditional is a dirty hack to limit the logging done on
3540 // behalf of the download manager without affecting other apps.
3541 if (!pkg.equals("com.android.providers.downloads")
3542 || Log.isLoggable("DownloadManager", Log.VERBOSE)) {
3543 int enqueueStatus = EVENTLOG_ENQUEUE_STATUS_NEW;
Chris Wren6676dab2016-12-21 18:26:27 -05003544 if (old != null) {
Julia Reynolds4b82f6d2017-01-04 10:47:41 -05003545 enqueueStatus = EVENTLOG_ENQUEUE_STATUS_UPDATE;
Chris Wren6676dab2016-12-21 18:26:27 -05003546 }
Julia Reynolds4b82f6d2017-01-04 10:47:41 -05003547 EventLogTags.writeNotificationEnqueue(callingUid, callingPid,
3548 pkg, id, tag, userId, notification.toString(),
3549 enqueueStatus);
3550 }
Chris Wren6676dab2016-12-21 18:26:27 -05003551
Julia Reynolds4b82f6d2017-01-04 10:47:41 -05003552 mRankingHelper.extractSignals(r);
Chris Wren6676dab2016-12-21 18:26:27 -05003553
Julia Reynolds4b82f6d2017-01-04 10:47:41 -05003554 // tell the assistant service about the notification
3555 if (mNotificationAssistants.isEnabled()) {
3556 mNotificationAssistants.onNotificationEnqueued(r);
Julia Reynoldsd94054f2017-02-01 11:11:06 -05003557 mHandler.postDelayed(new PostNotificationRunnable(r.getKey()),
Julia Reynolds4b82f6d2017-01-04 10:47:41 -05003558 DELAY_FOR_ASSISTANT_TIME);
3559 } else {
Julia Reynoldsd94054f2017-02-01 11:11:06 -05003560 mHandler.post(new PostNotificationRunnable(r.getKey()));
Julia Reynolds4b82f6d2017-01-04 10:47:41 -05003561 }
3562 }
3563 }
Julia Reynolds4b82f6d2017-01-04 10:47:41 -05003564 }
3565
3566 protected class PostNotificationRunnable implements Runnable {
3567 private final String key;
Julia Reynolds4b82f6d2017-01-04 10:47:41 -05003568
Julia Reynoldsd94054f2017-02-01 11:11:06 -05003569 PostNotificationRunnable(String key) {
Julia Reynolds4b82f6d2017-01-04 10:47:41 -05003570 this.key = key;
3571 }
3572
3573 @Override
3574 public void run() {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05003575 synchronized (mNotificationLock) {
3576 try {
3577 NotificationRecord r = null;
Julia Reynolds4b82f6d2017-01-04 10:47:41 -05003578 int N = mEnqueuedNotifications.size();
3579 for (int i = 0; i < N; i++) {
3580 final NotificationRecord enqueued = mEnqueuedNotifications.get(i);
3581 if (Objects.equals(key, enqueued.getKey())) {
3582 r = enqueued;
3583 break;
Chris Wren6676dab2016-12-21 18:26:27 -05003584 }
Chris Wren6676dab2016-12-21 18:26:27 -05003585 }
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05003586 if (r == null) {
3587 Slog.i(TAG, "Cannot find enqueued record for key: " + key);
3588 return;
3589 }
Julia Reynolds4b82f6d2017-01-04 10:47:41 -05003590 NotificationRecord old = mNotificationsByKey.get(key);
3591 final StatusBarNotification n = r.sbn;
3592 final Notification notification = n.getNotification();
Chris Wren6676dab2016-12-21 18:26:27 -05003593 int index = indexOfNotificationLocked(n.getKey());
3594 if (index < 0) {
3595 mNotificationList.add(r);
3596 mUsageStats.registerPostedByApp(r);
3597 } else {
3598 old = mNotificationList.get(index);
3599 mNotificationList.set(index, r);
3600 mUsageStats.registerUpdatedByApp(r, old);
3601 // Make sure we don't lose the foreground service state.
3602 notification.flags |=
3603 old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;
3604 r.isUpdate = true;
3605 }
3606
3607 mNotificationsByKey.put(n.getKey(), r);
3608
3609 // Ensure if this is a foreground service that the proper additional
3610 // flags are set.
3611 if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
3612 notification.flags |= Notification.FLAG_ONGOING_EVENT
3613 | Notification.FLAG_NO_CLEAR;
3614 }
3615
3616 applyZenModeLocked(r);
3617 mRankingHelper.sort(mNotificationList);
3618
3619 if (notification.getSmallIcon() != null) {
3620 StatusBarNotification oldSbn = (old != null) ? old.sbn : null;
3621 mListeners.notifyPostedLocked(n, oldSbn);
Julia Reynolds8f488d32016-10-14 10:59:01 -04003622 mHandler.post(new Runnable() {
3623 @Override
3624 public void run() {
Chris Wren6676dab2016-12-21 18:26:27 -05003625 mGroupHelper.onNotificationPosted(n);
Julia Reynolds8f488d32016-10-14 10:59:01 -04003626 }
3627 });
Chris Wren6676dab2016-12-21 18:26:27 -05003628 } else {
3629 Slog.e(TAG, "Not posting notification without small icon: " + notification);
3630 if (old != null && !old.isCanceled) {
3631 mListeners.notifyRemovedLocked(n,
Julia Reynoldsf619bc52017-03-17 08:32:23 -04003632 NotificationListenerService.REASON_ERROR);
Chris Wren6676dab2016-12-21 18:26:27 -05003633 mHandler.post(new Runnable() {
3634 @Override
3635 public void run() {
3636 mGroupHelper.onNotificationRemoved(n);
3637 }
3638 });
3639 }
3640 // ATTENTION: in a future release we will bail out here
3641 // so that we do not play sounds, show lights, etc. for invalid
3642 // notifications
3643 Slog.e(TAG, "WARNING: In a future release this will crash the app: "
3644 + n.getPackageName());
Chris Wren47633422016-01-22 09:56:59 -05003645 }
Chris Wren47633422016-01-22 09:56:59 -05003646
Chris Wren6676dab2016-12-21 18:26:27 -05003647 buzzBeepBlinkLocked(r);
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05003648 } finally {
Julia Reynolds4b82f6d2017-01-04 10:47:41 -05003649 int N = mEnqueuedNotifications.size();
3650 for (int i = 0; i < N; i++) {
3651 final NotificationRecord enqueued = mEnqueuedNotifications.get(i);
3652 if (Objects.equals(key, enqueued.getKey())) {
3653 mEnqueuedNotifications.remove(i);
3654 break;
3655 }
3656 }
Chris Wren6676dab2016-12-21 18:26:27 -05003657 }
Chris Wren47633422016-01-22 09:56:59 -05003658 }
3659 }
3660 }
3661
Christoph Studer265c1052014-07-23 17:14:33 +02003662 /**
3663 * Ensures that grouped notification receive their special treatment.
3664 *
3665 * <p>Cancels group children if the new notification causes a group to lose
3666 * its summary.</p>
3667 *
3668 * <p>Updates mSummaryByGroupKey.</p>
3669 */
Julia Reynolds88860ce2017-06-01 16:55:49 -04003670 @GuardedBy("mNotificationLock")
Christoph Studer265c1052014-07-23 17:14:33 +02003671 private void handleGroupedNotificationLocked(NotificationRecord r, NotificationRecord old,
3672 int callingUid, int callingPid) {
3673 StatusBarNotification sbn = r.sbn;
3674 Notification n = sbn.getNotification();
Selim Cinek5b03ce92016-05-18 15:16:58 -07003675 if (n.isGroupSummary() && !sbn.isAppGroup()) {
3676 // notifications without a group shouldn't be a summary, otherwise autobundling can
3677 // lead to bugs
3678 n.flags &= ~Notification.FLAG_GROUP_SUMMARY;
3679 }
3680
Christoph Studer265c1052014-07-23 17:14:33 +02003681 String group = sbn.getGroupKey();
3682 boolean isSummary = n.isGroupSummary();
3683
3684 Notification oldN = old != null ? old.sbn.getNotification() : null;
3685 String oldGroup = old != null ? old.sbn.getGroupKey() : null;
3686 boolean oldIsSummary = old != null && oldN.isGroupSummary();
3687
3688 if (oldIsSummary) {
3689 NotificationRecord removedSummary = mSummaryByGroupKey.remove(oldGroup);
3690 if (removedSummary != old) {
3691 String removedKey =
3692 removedSummary != null ? removedSummary.getKey() : "<null>";
3693 Slog.w(TAG, "Removed summary didn't match old notification: old=" + old.getKey() +
3694 ", removed=" + removedKey);
3695 }
3696 }
3697 if (isSummary) {
3698 mSummaryByGroupKey.put(group, r);
3699 }
3700
3701 // Clear out group children of the old notification if the update
3702 // causes the group summary to go away. This happens when the old
3703 // notification was a summary and the new one isn't, or when the old
3704 // notification was a summary and its group key changed.
3705 if (oldIsSummary && (!isSummary || !oldGroup.equals(group))) {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05003706 cancelGroupChildrenLocked(old, callingUid, callingPid, null, false /* sendDelete */);
Christoph Studer265c1052014-07-23 17:14:33 +02003707 }
3708 }
3709
Chris Wren93bb8b82016-03-29 14:35:05 -04003710 @VisibleForTesting
Julia Reynolds88860ce2017-06-01 16:55:49 -04003711 @GuardedBy("mNotificationLock")
Julia Reynolds2a128742016-11-28 14:29:25 -05003712 void scheduleTimeoutLocked(NotificationRecord record) {
Julia Reynoldsbad42972017-04-25 13:52:49 -04003713 if (record.getNotification().getTimeoutAfter() > 0) {
Julia Reynolds2a128742016-11-28 14:29:25 -05003714 final PendingIntent pi = PendingIntent.getBroadcast(getContext(),
3715 REQUEST_CODE_TIMEOUT,
3716 new Intent(ACTION_NOTIFICATION_TIMEOUT)
3717 .setData(new Uri.Builder().scheme(SCHEME_TIMEOUT)
3718 .appendPath(record.getKey()).build())
3719 .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
3720 .putExtra(EXTRA_KEY, record.getKey()),
3721 PendingIntent.FLAG_UPDATE_CURRENT);
Julia Reynolds50989772017-02-23 14:32:16 -05003722 mAlarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP,
Julia Reynoldsbad42972017-04-25 13:52:49 -04003723 SystemClock.elapsedRealtime() + record.getNotification().getTimeoutAfter(), pi);
Julia Reynolds2a128742016-11-28 14:29:25 -05003724 }
3725 }
3726
3727 @VisibleForTesting
Julia Reynolds88860ce2017-06-01 16:55:49 -04003728 @GuardedBy("mNotificationLock")
Chris Wren93bb8b82016-03-29 14:35:05 -04003729 void buzzBeepBlinkLocked(NotificationRecord record) {
Chris Wren82ba59d2015-06-05 11:23:44 -04003730 boolean buzz = false;
3731 boolean beep = false;
3732 boolean blink = false;
3733
Chris Wrena3446562014-06-03 18:11:47 -04003734 final Notification notification = record.sbn.getNotification();
Chris Wren93bb8b82016-03-29 14:35:05 -04003735 final String key = record.getKey();
Chris Wrena3446562014-06-03 18:11:47 -04003736
3737 // Should this notification make noise, vibe, or use the LED?
Julia Reynolds85769912016-10-25 09:08:57 -04003738 final boolean aboveThreshold =
3739 record.getImportance() >= NotificationManager.IMPORTANCE_DEFAULT;
Chris Wren93bb8b82016-03-29 14:35:05 -04003740
3741 // Remember if this notification already owns the notification channels.
3742 boolean wasBeep = key != null && key.equals(mSoundNotificationKey);
3743 boolean wasBuzz = key != null && key.equals(mVibrateNotificationKey);
Chris Wren93bb8b82016-03-29 14:35:05 -04003744 // These are set inside the conditional if the notification is allowed to make noise.
3745 boolean hasValidVibrate = false;
3746 boolean hasValidSound = false;
Chris Wrena3446562014-06-03 18:11:47 -04003747
Dan Sandler8ce1be42017-08-01 20:55:59 -04003748 if (isNotificationForCurrentUser(record)) {
3749 // If the notification icon will appear in the status bar, AND it hasn't been blocked
3750 // by do-not-disturb, it should generate an accessibility event
3751 if (!record.isUpdate
3752 && !record.isIntercepted()
3753 && record.getImportance() > IMPORTANCE_MIN) {
Chris Wren93bb8b82016-03-29 14:35:05 -04003754 sendAccessibilityEvent(notification, record.sbn.getPackageName());
Julia Reynolds7c96b582017-05-25 12:35:36 -04003755 }
Dan Sandler8ce1be42017-08-01 20:55:59 -04003756 if (aboveThreshold && mSystemReady && mAudioManager != null) {
3757 // this notification wants to make noise & is allowed to make noise
Julia Reynolds7c96b582017-05-25 12:35:36 -04003758 Uri soundUri = record.getSound();
3759 hasValidSound = soundUri != null && !Uri.EMPTY.equals(soundUri);
Julia Reynolds5f8e0b882017-06-19 08:16:04 -04003760
Julia Reynolds7c96b582017-05-25 12:35:36 -04003761 long[] vibration = record.getVibration();
3762 // Demote sound to vibration if vibration missing & phone in vibration mode.
3763 if (vibration == null
3764 && hasValidSound
3765 && (mAudioManager.getRingerModeInternal()
3766 == AudioManager.RINGER_MODE_VIBRATE)) {
3767 vibration = mFallbackVibrationPattern;
Chris Wren93bb8b82016-03-29 14:35:05 -04003768 }
Julia Reynolds7c96b582017-05-25 12:35:36 -04003769 hasValidVibrate = vibration != null;
Marta Białka39c992f2011-03-10 10:27:24 +01003770
Julia Reynolds5f8e0b882017-06-19 08:16:04 -04003771 boolean hasAudibleAlert = hasValidSound || hasValidVibrate;
3772
3773 if (hasAudibleAlert && !shouldMuteNotificationLocked(record)) {
3774 if (DBG) Slog.v(TAG, "Interrupting!");
Julia Reynolds7c96b582017-05-25 12:35:36 -04003775 if (hasValidSound) {
3776 mSoundNotificationKey = key;
3777 if (mInCall) {
3778 playInCallNotification();
3779 beep = true;
3780 } else {
3781 beep = playSound(record, soundUri);
3782 }
3783 }
Chris Wren93bb8b82016-03-29 14:35:05 -04003784
Julia Reynolds7c96b582017-05-25 12:35:36 -04003785 final boolean ringerModeSilent =
3786 mAudioManager.getRingerModeInternal()
3787 == AudioManager.RINGER_MODE_SILENT;
3788 if (!mInCall && hasValidVibrate && !ringerModeSilent) {
3789 mVibrateNotificationKey = key;
3790
Jean-Michel Triviea0eb5f2017-05-25 18:32:40 -07003791 buzz = playVibration(record, vibration, hasValidSound);
Julia Reynolds7c96b582017-05-25 12:35:36 -04003792 }
Chris Wrena3446562014-06-03 18:11:47 -04003793 }
3794 }
Chris Wren93bb8b82016-03-29 14:35:05 -04003795 }
3796 // If a notification is updated to remove the actively playing sound or vibrate,
3797 // cancel that feedback now
3798 if (wasBeep && !hasValidSound) {
3799 clearSoundLocked();
3800 }
3801 if (wasBuzz && !hasValidVibrate) {
3802 clearVibrateLocked();
Chris Wrena3446562014-06-03 18:11:47 -04003803 }
3804
3805 // light
3806 // release the light
Chris Wren93bb8b82016-03-29 14:35:05 -04003807 boolean wasShowLights = mLights.remove(key);
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05003808 if (record.getLight() != null && aboveThreshold
Julia Reynoldsf612869ae2015-11-05 16:48:55 -05003809 && ((record.getSuppressedVisualEffects()
Julia Reynoldsd5607292016-02-05 15:25:58 -05003810 & NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_OFF) == 0)) {
Chris Wren93bb8b82016-03-29 14:35:05 -04003811 mLights.add(key);
Chris Wrena3446562014-06-03 18:11:47 -04003812 updateLightsLocked();
Chris Wren5116a822014-06-04 15:59:50 -04003813 if (mUseAttentionLight) {
3814 mAttentionLight.pulse();
3815 }
Chris Wren82ba59d2015-06-05 11:23:44 -04003816 blink = true;
Chris Wrena3446562014-06-03 18:11:47 -04003817 } else if (wasShowLights) {
3818 updateLightsLocked();
3819 }
Chris Wren82ba59d2015-06-05 11:23:44 -04003820 if (buzz || beep || blink) {
Julia Reynolds445cfa82017-05-08 15:41:45 -04003821 MetricsLogger.action(record.getLogMaker()
3822 .setCategory(MetricsEvent.NOTIFICATION_ALERT)
3823 .setType(MetricsEvent.TYPE_OPEN)
3824 .setSubtype((buzz ? 1 : 0) | (beep ? 2 : 0) | (blink ? 4 : 0)));
3825 EventLogTags.writeNotificationAlert(key, buzz ? 1 : 0, beep ? 1 : 0, blink ? 1 : 0);
John Spurlockcad57682014-07-26 17:09:56 -04003826 }
Chris Wrena3446562014-06-03 18:11:47 -04003827 }
3828
Julia Reynolds88860ce2017-06-01 16:55:49 -04003829 @GuardedBy("mNotificationLock")
Julia Reynoldsa79c3712017-04-21 10:29:57 -04003830 boolean shouldMuteNotificationLocked(final NotificationRecord record) {
Julia Reynolds5f8e0b882017-06-19 08:16:04 -04003831 // Suppressed because it's a silent update
Julia Reynoldsa79c3712017-04-21 10:29:57 -04003832 final Notification notification = record.getNotification();
3833 if(record.isUpdate
3834 && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0) {
3835 return true;
3836 }
Julia Reynolds5f8e0b882017-06-19 08:16:04 -04003837
Julia Reynolds5f8e0b882017-06-19 08:16:04 -04003838 // muted by listener
3839 final String disableEffects = disableNotificationEffects(record);
3840 if (disableEffects != null) {
3841 ZenLog.traceDisableEffects(record, disableEffects);
3842 return true;
3843 }
3844
3845 // suppressed due to DND
3846 if (record.isIntercepted()) {
3847 return true;
3848 }
3849
3850 // Suppressed because another notification in its group handles alerting
Julia Reynoldsa79c3712017-04-21 10:29:57 -04003851 if (record.sbn.isGroup()) {
Julia Reynolds30203152017-05-26 13:36:31 -04003852 return notification.suppressAlertingDueToGrouping();
Julia Reynoldsa79c3712017-04-21 10:29:57 -04003853 }
Julia Reynolds5f8e0b882017-06-19 08:16:04 -04003854
Julia Reynolds65b85cf2017-07-20 09:19:20 -04003855 // Suppressed for being too recently noisy
3856 final String pkg = record.sbn.getPackageName();
3857 if (mUsageStats.isAlertRateLimited(pkg)) {
3858 Slog.e(TAG, "Muting recently noisy " + record.getKey());
3859 return true;
3860 }
3861
Julia Reynoldsa79c3712017-04-21 10:29:57 -04003862 return false;
3863 }
3864
Julia Reynolds0c299d42016-11-15 14:37:04 -05003865 private boolean playSound(final NotificationRecord record, Uri soundUri) {
3866 boolean looping = (record.getNotification().flags & Notification.FLAG_INSISTENT) != 0;
3867 // do not play notifications if there is a user of exclusive audio focus
Julia Reynolds2143e5d2017-01-17 16:28:48 -05003868 // or the device is in vibrate mode
3869 if (!mAudioManager.isAudioFocusExclusive() && mAudioManager.getRingerModeInternal()
3870 != AudioManager.RINGER_MODE_VIBRATE) {
Julia Reynolds0c299d42016-11-15 14:37:04 -05003871 final long identity = Binder.clearCallingIdentity();
3872 try {
3873 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
3874 if (player != null) {
3875 if (DBG) Slog.v(TAG, "Playing sound " + soundUri
3876 + " with attributes " + record.getAudioAttributes());
3877 player.playAsync(soundUri, record.sbn.getUser(), looping,
3878 record.getAudioAttributes());
3879 return true;
3880 }
3881 } catch (RemoteException e) {
3882 } finally {
3883 Binder.restoreCallingIdentity(identity);
3884 }
3885 }
3886 return false;
3887 }
3888
Jean-Michel Triviea0eb5f2017-05-25 18:32:40 -07003889 private boolean playVibration(final NotificationRecord record, long[] vibration,
3890 boolean delayVibForSound) {
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04003891 // Escalate privileges so we can use the vibrator even if the
3892 // notifying app does not have the VIBRATE permission.
3893 long identity = Binder.clearCallingIdentity();
3894 try {
Jean-Michel Triviea0eb5f2017-05-25 18:32:40 -07003895 final VibrationEffect effect;
3896 try {
3897 final boolean insistent =
3898 (record.getNotification().flags & Notification.FLAG_INSISTENT) != 0;
3899 effect = VibrationEffect.createWaveform(
3900 vibration, insistent ? 0 : -1 /*repeatIndex*/);
3901 } catch (IllegalArgumentException e) {
3902 Slog.e(TAG, "Error creating vibration waveform with pattern: " +
3903 Arrays.toString(vibration));
3904 return false;
3905 }
3906 if (delayVibForSound) {
3907 new Thread(() -> {
3908 // delay the vibration by the same amount as the notification sound
3909 final int waitMs = mAudioManager.getFocusRampTimeMs(
3910 AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK,
3911 record.getAudioAttributes());
3912 if (DBG) Slog.v(TAG, "Delaying vibration by " + waitMs + "ms");
3913 try {
3914 Thread.sleep(waitMs);
3915 } catch (InterruptedException e) { }
3916 mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
3917 effect, record.getAudioAttributes());
3918 }).start();
3919 } else {
3920 mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
3921 effect, record.getAudioAttributes());
3922 }
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04003923 return true;
3924 } finally{
3925 Binder.restoreCallingIdentity(identity);
3926 }
3927 }
3928
Julia Reynolds7c96b582017-05-25 12:35:36 -04003929 private boolean isNotificationForCurrentUser(NotificationRecord record) {
3930 final int currentUser;
3931 final long token = Binder.clearCallingIdentity();
3932 try {
3933 currentUser = ActivityManager.getCurrentUser();
3934 } finally {
3935 Binder.restoreCallingIdentity(token);
3936 }
3937 return (record.getUserId() == UserHandle.USER_ALL ||
3938 record.getUserId() == currentUser ||
3939 mUserProfiles.isCurrentProfile(record.getUserId()));
3940 }
3941
Marta Białka39c992f2011-03-10 10:27:24 +01003942 private void playInCallNotification() {
3943 new Thread() {
3944 @Override
3945 public void run() {
3946 // If toneGenerator creation fails, just continue the call
3947 // without playing the notification sound.
3948 try {
3949 synchronized (mInCallToneGeneratorLock) {
3950 if (mInCallToneGenerator != null) {
3951 // limit this tone to 1 second; BEEP2 should in fact be much shorter
3952 mInCallToneGenerator.startTone(ToneGenerator.TONE_PROP_BEEP2, 1000);
3953 }
3954 }
3955 } catch (RuntimeException e) {
3956 Log.w(TAG, "Exception from ToneGenerator: " + e);
3957 }
3958 }
3959 }.start();
3960 }
3961
Julia Reynolds88860ce2017-06-01 16:55:49 -04003962 @GuardedBy("mToastQueue")
Adam Lesinski182f73f2013-12-05 16:48:06 -08003963 void showNextToastLocked() {
3964 ToastRecord record = mToastQueue.get(0);
3965 while (record != null) {
3966 if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback);
3967 try {
Svetoslav Ganovaa076532016-08-01 19:16:43 -07003968 record.callback.show(record.token);
Adam Lesinski182f73f2013-12-05 16:48:06 -08003969 scheduleTimeoutLocked(record);
3970 return;
3971 } catch (RemoteException e) {
3972 Slog.w(TAG, "Object died trying to show notification " + record.callback
3973 + " in package " + record.pkg);
3974 // remove it from the list and let the process die
3975 int index = mToastQueue.indexOf(record);
3976 if (index >= 0) {
3977 mToastQueue.remove(index);
3978 }
Svetoslav Ganovaa076532016-08-01 19:16:43 -07003979 keepProcessAliveIfNeededLocked(record.pid);
Adam Lesinski182f73f2013-12-05 16:48:06 -08003980 if (mToastQueue.size() > 0) {
3981 record = mToastQueue.get(0);
3982 } else {
3983 record = null;
3984 }
3985 }
3986 }
3987 }
3988
Julia Reynolds88860ce2017-06-01 16:55:49 -04003989 @GuardedBy("mToastQueue")
Adam Lesinski182f73f2013-12-05 16:48:06 -08003990 void cancelToastLocked(int index) {
3991 ToastRecord record = mToastQueue.get(index);
3992 try {
3993 record.callback.hide();
3994 } catch (RemoteException e) {
3995 Slog.w(TAG, "Object died trying to hide notification " + record.callback
3996 + " in package " + record.pkg);
3997 // don't worry about this, we're about to remove it from
3998 // the list anyway
3999 }
Svetoslav Ganovaa076532016-08-01 19:16:43 -07004000
4001 ToastRecord lastToast = mToastQueue.remove(index);
Wale Ogunwaleac2561e2016-11-01 15:43:46 -07004002 mWindowManagerInternal.removeWindowToken(lastToast.token, true, DEFAULT_DISPLAY);
Svetoslav Ganovaa076532016-08-01 19:16:43 -07004003
4004 keepProcessAliveIfNeededLocked(record.pid);
Adam Lesinski182f73f2013-12-05 16:48:06 -08004005 if (mToastQueue.size() > 0) {
4006 // Show the next one. If the callback fails, this will remove
4007 // it from the list, so don't assume that the list hasn't changed
4008 // after this point.
4009 showNextToastLocked();
4010 }
4011 }
4012
Julia Reynolds88860ce2017-06-01 16:55:49 -04004013 @GuardedBy("mToastQueue")
Adam Lesinski182f73f2013-12-05 16:48:06 -08004014 private void scheduleTimeoutLocked(ToastRecord r)
4015 {
4016 mHandler.removeCallbacksAndMessages(r);
4017 Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r);
4018 long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY;
4019 mHandler.sendMessageDelayed(m, delay);
4020 }
4021
4022 private void handleTimeout(ToastRecord record)
4023 {
4024 if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback);
4025 synchronized (mToastQueue) {
4026 int index = indexOfToastLocked(record.pkg, record.callback);
4027 if (index >= 0) {
4028 cancelToastLocked(index);
4029 }
4030 }
4031 }
4032
Julia Reynolds88860ce2017-06-01 16:55:49 -04004033 @GuardedBy("mToastQueue")
Adam Lesinski182f73f2013-12-05 16:48:06 -08004034 int indexOfToastLocked(String pkg, ITransientNotification callback)
4035 {
4036 IBinder cbak = callback.asBinder();
4037 ArrayList<ToastRecord> list = mToastQueue;
4038 int len = list.size();
4039 for (int i=0; i<len; i++) {
4040 ToastRecord r = list.get(i);
4041 if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) {
4042 return i;
4043 }
4044 }
4045 return -1;
4046 }
4047
Julia Reynolds88860ce2017-06-01 16:55:49 -04004048 @GuardedBy("mToastQueue")
Svetoslav Ganovaa076532016-08-01 19:16:43 -07004049 void keepProcessAliveIfNeededLocked(int pid)
Adam Lesinski182f73f2013-12-05 16:48:06 -08004050 {
4051 int toastCount = 0; // toasts from this pid
4052 ArrayList<ToastRecord> list = mToastQueue;
4053 int N = list.size();
4054 for (int i=0; i<N; i++) {
4055 ToastRecord r = list.get(i);
4056 if (r.pid == pid) {
4057 toastCount++;
4058 }
4059 }
4060 try {
Dianne Hackbornf965f402017-05-04 23:27:23 -07004061 mAm.setProcessImportant(mForegroundToken, pid, toastCount > 0, "toast");
Adam Lesinski182f73f2013-12-05 16:48:06 -08004062 } catch (RemoteException e) {
4063 // Shouldn't happen.
4064 }
4065 }
4066
Chris Wrenf9536642014-04-17 10:01:54 -04004067 private void handleRankingReconsideration(Message message) {
Chris Wren470c1ac2014-05-21 15:28:10 -04004068 if (!(message.obj instanceof RankingReconsideration)) return;
4069 RankingReconsideration recon = (RankingReconsideration) message.obj;
4070 recon.run();
Chris Wren333a61c2014-05-28 16:40:57 -04004071 boolean changed;
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004072 synchronized (mNotificationLock) {
Chris Wren470c1ac2014-05-21 15:28:10 -04004073 final NotificationRecord record = mNotificationsByKey.get(recon.getKey());
4074 if (record == null) {
4075 return;
Chris Wrenf9536642014-04-17 10:01:54 -04004076 }
Chris Wren333a61c2014-05-28 16:40:57 -04004077 int indexBefore = findNotificationRecordIndexLocked(record);
4078 boolean interceptBefore = record.isIntercepted();
Julia Reynoldsaf4dc282017-06-23 16:13:20 -04004079 float contactAffinityBefore = record.getContactAffinity();
Chris Wren3ad4e3a2014-09-02 17:23:51 -04004080 int visibilityBefore = record.getPackageVisibilityOverride();
Chris Wren470c1ac2014-05-21 15:28:10 -04004081 recon.applyChangesLocked(record);
Chris Wren333a61c2014-05-28 16:40:57 -04004082 applyZenModeLocked(record);
Chris Wren54bbef42014-07-09 18:37:56 -04004083 mRankingHelper.sort(mNotificationList);
Chris Wren333a61c2014-05-28 16:40:57 -04004084 int indexAfter = findNotificationRecordIndexLocked(record);
4085 boolean interceptAfter = record.isIntercepted();
Julia Reynoldsaf4dc282017-06-23 16:13:20 -04004086 float contactAffinityAfter = record.getContactAffinity();
Chris Wren3ad4e3a2014-09-02 17:23:51 -04004087 int visibilityAfter = record.getPackageVisibilityOverride();
4088 changed = indexBefore != indexAfter || interceptBefore != interceptAfter
4089 || visibilityBefore != visibilityAfter;
Julia Reynoldsaf4dc282017-06-23 16:13:20 -04004090 if (interceptBefore && !interceptAfter
4091 && Float.compare(contactAffinityBefore, contactAffinityAfter) != 0) {
Chris Wrena3446562014-06-03 18:11:47 -04004092 buzzBeepBlinkLocked(record);
4093 }
Chris Wrenf9536642014-04-17 10:01:54 -04004094 }
Chris Wren333a61c2014-05-28 16:40:57 -04004095 if (changed) {
Chris Wren470c1ac2014-05-21 15:28:10 -04004096 scheduleSendRankingUpdate();
4097 }
4098 }
4099
Julia Reynolds22f02b32016-12-01 15:05:13 -05004100 private void handleRankingSort(Message msg) {
4101 if (!(msg.obj instanceof Boolean)) return;
Chris Wren89aa2262017-05-05 18:05:56 -04004102 if (mRankingHelper == null) return;
Julia Reynolds22f02b32016-12-01 15:05:13 -05004103 boolean forceUpdate = ((Boolean) msg.obj == null) ? false : (boolean) msg.obj;
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004104 synchronized (mNotificationLock) {
Chris Wren54bbef42014-07-09 18:37:56 -04004105 final int N = mNotificationList.size();
Julia Reynolds924eed12017-01-19 09:52:07 -05004106 // Any field that can change via one of the extractors or by the assistant
4107 // needs to be added here.
Chris Wren54bbef42014-07-09 18:37:56 -04004108 ArrayList<String> orderBefore = new ArrayList<String>(N);
Julia Reynoldse46bb372016-03-17 11:05:58 -04004109 ArrayList<String> groupOverrideBefore = new ArrayList<>(N);
Chris Wren3ad4e3a2014-09-02 17:23:51 -04004110 int[] visibilities = new int[N];
Julia Reynolds924eed12017-01-19 09:52:07 -05004111 boolean[] showBadges = new boolean[N];
Chris Wren54bbef42014-07-09 18:37:56 -04004112 for (int i = 0; i < N; i++) {
4113 final NotificationRecord r = mNotificationList.get(i);
4114 orderBefore.add(r.getKey());
Julia Reynoldse46bb372016-03-17 11:05:58 -04004115 groupOverrideBefore.add(r.sbn.getGroupKey());
Chris Wren3ad4e3a2014-09-02 17:23:51 -04004116 visibilities[i] = r.getPackageVisibilityOverride();
Julia Reynolds924eed12017-01-19 09:52:07 -05004117 showBadges[i] = r.canShowBadge();
Chris Wren54bbef42014-07-09 18:37:56 -04004118 mRankingHelper.extractSignals(r);
4119 }
Chris Wren19a02b02015-12-22 10:34:22 -05004120 mRankingHelper.sort(mNotificationList);
Chris Wren54bbef42014-07-09 18:37:56 -04004121 for (int i = 0; i < N; i++) {
Chris Wren3ad4e3a2014-09-02 17:23:51 -04004122 final NotificationRecord r = mNotificationList.get(i);
Julia Reynolds22f02b32016-12-01 15:05:13 -05004123 if (forceUpdate
4124 || !orderBefore.get(i).equals(r.getKey())
Julia Reynolds69766692016-02-01 15:35:08 -05004125 || visibilities[i] != r.getPackageVisibilityOverride()
Julia Reynolds924eed12017-01-19 09:52:07 -05004126 || !groupOverrideBefore.get(i).equals(r.sbn.getGroupKey())
4127 || showBadges[i] != r.canShowBadge()) {
Chris Wren54bbef42014-07-09 18:37:56 -04004128 scheduleSendRankingUpdate();
4129 return;
4130 }
4131 }
4132 }
4133 }
4134
Julia Reynolds88860ce2017-06-01 16:55:49 -04004135 @GuardedBy("mNotificationLock")
Julia Reynoldsc6b371b2016-06-14 08:31:03 -04004136 private void recordCallerLocked(NotificationRecord record) {
4137 if (mZenModeHelper.isCall(record)) {
4138 mZenModeHelper.recordCaller(record);
4139 }
4140 }
4141
Christoph Studerd5092bc2014-07-03 17:47:58 +02004142 // let zen mode evaluate this record
Julia Reynolds88860ce2017-06-01 16:55:49 -04004143 @GuardedBy("mNotificationLock")
Chris Wren333a61c2014-05-28 16:40:57 -04004144 private void applyZenModeLocked(NotificationRecord record) {
Christoph Studerd5092bc2014-07-03 17:47:58 +02004145 record.setIntercepted(mZenModeHelper.shouldIntercept(record));
Julia Reynoldsf612869ae2015-11-05 16:48:55 -05004146 if (record.isIntercepted()) {
Julia Reynoldsd5607292016-02-05 15:25:58 -05004147 int suppressed = (mZenModeHelper.shouldSuppressWhenScreenOff()
4148 ? SUPPRESSED_EFFECT_SCREEN_OFF : 0)
4149 | (mZenModeHelper.shouldSuppressWhenScreenOn()
4150 ? SUPPRESSED_EFFECT_SCREEN_ON : 0);
Julia Reynoldsf612869ae2015-11-05 16:48:55 -05004151 record.setSuppressedVisualEffects(suppressed);
Julia Reynolds445cfa82017-05-08 15:41:45 -04004152 } else {
4153 record.setSuppressedVisualEffects(0);
Julia Reynoldsf612869ae2015-11-05 16:48:55 -05004154 }
Chris Wren333a61c2014-05-28 16:40:57 -04004155 }
4156
Julia Reynolds88860ce2017-06-01 16:55:49 -04004157 @GuardedBy("mNotificationLock")
Chris Wren470c1ac2014-05-21 15:28:10 -04004158 private int findNotificationRecordIndexLocked(NotificationRecord target) {
Chris Wren54bbef42014-07-09 18:37:56 -04004159 return mRankingHelper.indexOf(mNotificationList, target);
Chris Wrenf9536642014-04-17 10:01:54 -04004160 }
4161
4162 private void scheduleSendRankingUpdate() {
Chris Wren52020492016-04-06 11:12:02 -04004163 if (!mHandler.hasMessages(MESSAGE_SEND_RANKING_UPDATE)) {
4164 Message m = Message.obtain(mHandler, MESSAGE_SEND_RANKING_UPDATE);
4165 mHandler.sendMessage(m);
4166 }
Chris Wrenf9536642014-04-17 10:01:54 -04004167 }
4168
4169 private void handleSendRankingUpdate() {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004170 synchronized (mNotificationLock) {
Chris Wren333a61c2014-05-28 16:40:57 -04004171 mListeners.notifyRankingUpdateLocked();
Chris Wrenf9536642014-04-17 10:01:54 -04004172 }
4173 }
4174
John Spurlockd8afe3c2014-08-01 14:04:07 -04004175 private void scheduleListenerHintsChanged(int state) {
4176 mHandler.removeMessages(MESSAGE_LISTENER_HINTS_CHANGED);
4177 mHandler.obtainMessage(MESSAGE_LISTENER_HINTS_CHANGED, state, 0).sendToTarget();
John Spurlock1fa865f2014-07-21 14:56:39 -04004178 }
4179
Christoph Studer85a384b2014-08-27 20:16:15 +02004180 private void scheduleInterruptionFilterChanged(int listenerInterruptionFilter) {
4181 mHandler.removeMessages(MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED);
4182 mHandler.obtainMessage(
4183 MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED,
4184 listenerInterruptionFilter,
4185 0).sendToTarget();
4186 }
4187
John Spurlockd8afe3c2014-08-01 14:04:07 -04004188 private void handleListenerHintsChanged(int hints) {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004189 synchronized (mNotificationLock) {
John Spurlockd8afe3c2014-08-01 14:04:07 -04004190 mListeners.notifyListenerHintsChangedLocked(hints);
John Spurlock1fa865f2014-07-21 14:56:39 -04004191 }
4192 }
4193
Christoph Studer85a384b2014-08-27 20:16:15 +02004194 private void handleListenerInterruptionFilterChanged(int interruptionFilter) {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004195 synchronized (mNotificationLock) {
Christoph Studer85a384b2014-08-27 20:16:15 +02004196 mListeners.notifyInterruptionFilterChanged(interruptionFilter);
4197 }
4198 }
4199
Adam Lesinski182f73f2013-12-05 16:48:06 -08004200 private final class WorkerHandler extends Handler
4201 {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004202 public WorkerHandler(Looper looper) {
4203 super(looper);
4204 }
4205
Adam Lesinski182f73f2013-12-05 16:48:06 -08004206 @Override
4207 public void handleMessage(Message msg)
4208 {
4209 switch (msg.what)
4210 {
4211 case MESSAGE_TIMEOUT:
4212 handleTimeout((ToastRecord)msg.obj);
4213 break;
John Spurlock056c5192014-04-20 21:52:01 -04004214 case MESSAGE_SAVE_POLICY_FILE:
4215 handleSavePolicyFile();
4216 break;
Chris Wrenf9536642014-04-17 10:01:54 -04004217 case MESSAGE_SEND_RANKING_UPDATE:
4218 handleSendRankingUpdate();
4219 break;
John Spurlockd8afe3c2014-08-01 14:04:07 -04004220 case MESSAGE_LISTENER_HINTS_CHANGED:
4221 handleListenerHintsChanged(msg.arg1);
John Spurlock1fa865f2014-07-21 14:56:39 -04004222 break;
Christoph Studer85a384b2014-08-27 20:16:15 +02004223 case MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED:
4224 handleListenerInterruptionFilterChanged(msg.arg1);
4225 break;
Chris Wrenf9536642014-04-17 10:01:54 -04004226 }
4227 }
4228
4229 }
4230
Chris Wren51017d02015-12-15 15:34:46 -05004231 private final class RankingHandlerWorker extends Handler implements RankingHandler
Chris Wrenf9536642014-04-17 10:01:54 -04004232 {
Chris Wren51017d02015-12-15 15:34:46 -05004233 public RankingHandlerWorker(Looper looper) {
Chris Wrenf9536642014-04-17 10:01:54 -04004234 super(looper);
4235 }
4236
4237 @Override
4238 public void handleMessage(Message msg) {
4239 switch (msg.what) {
4240 case MESSAGE_RECONSIDER_RANKING:
4241 handleRankingReconsideration(msg);
4242 break;
Chris Wren51017d02015-12-15 15:34:46 -05004243 case MESSAGE_RANKING_SORT:
Julia Reynolds22f02b32016-12-01 15:05:13 -05004244 handleRankingSort(msg);
Chris Wren54bbef42014-07-09 18:37:56 -04004245 break;
Adam Lesinski182f73f2013-12-05 16:48:06 -08004246 }
4247 }
Chris Wren51017d02015-12-15 15:34:46 -05004248
Julia Reynolds22f02b32016-12-01 15:05:13 -05004249 public void requestSort(boolean forceUpdate) {
Chris Wren51017d02015-12-15 15:34:46 -05004250 removeMessages(MESSAGE_RANKING_SORT);
Julia Reynolds22f02b32016-12-01 15:05:13 -05004251 Message msg = Message.obtain();
4252 msg.what = MESSAGE_RANKING_SORT;
4253 msg.obj = forceUpdate;
4254 sendMessage(msg);
Chris Wren51017d02015-12-15 15:34:46 -05004255 }
4256
4257 public void requestReconsideration(RankingReconsideration recon) {
4258 Message m = Message.obtain(this,
4259 NotificationManagerService.MESSAGE_RECONSIDER_RANKING, recon);
4260 long delay = recon.getDelay(TimeUnit.MILLISECONDS);
4261 sendMessageDelayed(m, delay);
4262 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08004263 }
4264
Adam Lesinski182f73f2013-12-05 16:48:06 -08004265 // Notifications
4266 // ============================================================================
4267 static int clamp(int x, int low, int high) {
4268 return (x < low) ? low : ((x > high) ? high : x);
4269 }
4270
Dan Sandler8ce1be42017-08-01 20:55:59 -04004271 @VisibleForTesting
Adam Lesinski182f73f2013-12-05 16:48:06 -08004272 void sendAccessibilityEvent(Notification notification, CharSequence packageName) {
Dan Sandler8ce1be42017-08-01 20:55:59 -04004273 final AccessibilityManager accessibilityManager
4274 = AccessibilityManager.getInstance(getContext());
4275 if (accessibilityManager == null || !accessibilityManager.isEnabled()) {
svetoslavganov75986cf2009-05-14 22:28:01 -07004276 return;
4277 }
4278
4279 AccessibilityEvent event =
4280 AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
4281 event.setPackageName(packageName);
4282 event.setClassName(Notification.class.getName());
4283 event.setParcelableData(notification);
4284 CharSequence tickerText = notification.tickerText;
4285 if (!TextUtils.isEmpty(tickerText)) {
4286 event.getText().add(tickerText);
4287 }
4288
Dan Sandler8ce1be42017-08-01 20:55:59 -04004289 accessibilityManager.sendAccessibilityEvent(event);
svetoslavganov75986cf2009-05-14 22:28:01 -07004290 }
4291
Julia Reynolds0839c022017-06-15 15:24:01 -04004292 /**
4293 * Removes all NotificationsRecords with the same key as the given notification record
4294 * from both lists. Do not call this method while iterating over either list.
4295 */
Julia Reynolds88860ce2017-06-01 16:55:49 -04004296 @GuardedBy("mNotificationLock")
Julia Reynolds0839c022017-06-15 15:24:01 -04004297 private boolean removeFromNotificationListsLocked(NotificationRecord r) {
4298 // Remove from both lists, either list could have a separate Record for what is
4299 // effectively the same notification.
Geoffrey Pitschccc0b972017-02-15 10:52:26 -05004300 boolean wasPosted = false;
4301 NotificationRecord recordInList = null;
Julia Reynolds0839c022017-06-15 15:24:01 -04004302 if ((recordInList = findNotificationByListLocked(mNotificationList, r.getKey()))
4303 != null) {
Geoffrey Pitschccc0b972017-02-15 10:52:26 -05004304 mNotificationList.remove(recordInList);
4305 mNotificationsByKey.remove(recordInList.sbn.getKey());
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004306 wasPosted = true;
Geoffrey Pitschccc0b972017-02-15 10:52:26 -05004307 }
Geoffrey Pitsch27684152017-05-02 11:41:31 -04004308 while ((recordInList = findNotificationByListLocked(mEnqueuedNotifications, r.getKey()))
Geoffrey Pitschccc0b972017-02-15 10:52:26 -05004309 != null) {
4310 mEnqueuedNotifications.remove(recordInList);
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004311 }
Julia Reynolds0839c022017-06-15 15:24:01 -04004312 return wasPosted;
4313 }
4314
4315 @GuardedBy("mNotificationLock")
4316 private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason,
4317 boolean wasPosted) {
4318 final String canceledKey = r.getKey();
Julia Reynoldsc6b371b2016-06-14 08:31:03 -04004319
4320 // Record caller.
4321 recordCallerLocked(r);
4322
Joe Onorato46439ce2010-11-19 13:56:21 -08004323 // tell the app
4324 if (sendDelete) {
Daniel Sandlerfde19b12013-01-17 00:21:05 -05004325 if (r.getNotification().deleteIntent != null) {
Joe Onorato46439ce2010-11-19 13:56:21 -08004326 try {
Daniel Sandlerfde19b12013-01-17 00:21:05 -05004327 r.getNotification().deleteIntent.send();
Joe Onorato46439ce2010-11-19 13:56:21 -08004328 } catch (PendingIntent.CanceledException ex) {
4329 // do nothing - there's no relevant way to recover, and
4330 // no reason to let this propagate
Daniel Sandler4f91efd2013-04-25 16:38:41 -04004331 Slog.w(TAG, "canceled PendingIntent for " + r.sbn.getPackageName(), ex);
Joe Onorato46439ce2010-11-19 13:56:21 -08004332 }
4333 }
4334 }
4335
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004336 // Only cancel these if this notification actually got to be posted.
4337 if (wasPosted) {
4338 // status bar
4339 if (r.getNotification().getSmallIcon() != null) {
Julia Reynoldsa8b766f2017-03-07 16:30:21 -05004340 if (reason != REASON_SNOOZED) {
4341 r.isCanceled = true;
4342 }
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004343 mListeners.notifyRemovedLocked(r.sbn, reason);
4344 mHandler.post(new Runnable() {
4345 @Override
4346 public void run() {
4347 mGroupHelper.onNotificationRemoved(r.sbn);
4348 }
4349 });
4350 }
4351
4352 // sound
4353 if (canceledKey.equals(mSoundNotificationKey)) {
4354 mSoundNotificationKey = null;
4355 final long identity = Binder.clearCallingIdentity();
4356 try {
4357 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
4358 if (player != null) {
4359 player.stopAsync();
4360 }
4361 } catch (RemoteException e) {
4362 } finally {
4363 Binder.restoreCallingIdentity(identity);
Julia Reynolds8f488d32016-10-14 10:59:01 -04004364 }
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004365 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004366
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004367 // vibrate
4368 if (canceledKey.equals(mVibrateNotificationKey)) {
4369 mVibrateNotificationKey = null;
4370 long identity = Binder.clearCallingIdentity();
4371 try {
4372 mVibrator.cancel();
Jeff Sharkey098d5802012-04-26 17:30:34 -07004373 }
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004374 finally {
4375 Binder.restoreCallingIdentity(identity);
4376 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004377 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004378
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004379 // light
4380 mLights.remove(canceledKey);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004381 }
4382
Christoph Studer546bec82014-03-14 12:17:12 +01004383 // Record usage stats
Julia Reynoldse46bb372016-03-17 11:05:58 -04004384 // TODO: add unbundling stats?
Christoph Studer546bec82014-03-14 12:17:12 +01004385 switch (reason) {
Julia Reynoldsf619bc52017-03-17 08:32:23 -04004386 case REASON_CANCEL:
4387 case REASON_CANCEL_ALL:
Christoph Studer546bec82014-03-14 12:17:12 +01004388 case REASON_LISTENER_CANCEL:
4389 case REASON_LISTENER_CANCEL_ALL:
4390 mUsageStats.registerDismissedByUser(r);
4391 break;
Chris Wren9fa689f2015-11-20 16:44:53 -05004392 case REASON_APP_CANCEL:
4393 case REASON_APP_CANCEL_ALL:
Christoph Studer546bec82014-03-14 12:17:12 +01004394 mUsageStats.registerRemovedByApp(r);
4395 break;
Christoph Studer546bec82014-03-14 12:17:12 +01004396 }
4397
Christoph Studer265c1052014-07-23 17:14:33 +02004398 String groupKey = r.getGroupKey();
4399 NotificationRecord groupSummary = mSummaryByGroupKey.get(groupKey);
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004400 if (groupSummary != null && groupSummary.getKey().equals(canceledKey)) {
Christoph Studer265c1052014-07-23 17:14:33 +02004401 mSummaryByGroupKey.remove(groupKey);
4402 }
Julia Reynoldseae43fb2016-05-09 12:42:58 -04004403 final ArrayMap<String, String> summaries = mAutobundledSummaries.get(r.sbn.getUserId());
4404 if (summaries != null && r.sbn.getKey().equals(summaries.get(r.sbn.getPackageName()))) {
4405 summaries.remove(r.sbn.getPackageName());
Julia Reynoldse46bb372016-03-17 11:05:58 -04004406 }
Christoph Studercef37cf2014-07-25 14:18:17 +02004407
Daniel Sandler23d7c702013-03-07 16:32:06 -05004408 // Save it for users of getHistoricalNotifications()
4409 mArchive.record(r.sbn);
Christoph Studer81e5b5f2014-10-22 17:19:56 +02004410
Chris Wren6650e572015-05-15 17:19:25 -04004411 final long now = System.currentTimeMillis();
Chris Wren9eb5e102017-01-26 13:15:06 -05004412 MetricsLogger.action(r.getLogMaker(now)
4413 .setCategory(MetricsEvent.NOTIFICATION_ITEM)
4414 .setType(MetricsEvent.TYPE_DISMISS)
4415 .setSubtype(reason));
Chris Wrene6ddb8a2015-05-27 15:21:00 -04004416 EventLogTags.writeNotificationCanceled(canceledKey, reason,
4417 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004418 }
4419
4420 /**
Dianne Hackbornd8a43f62009-08-17 23:33:56 -07004421 * Cancels a notification ONLY if it has all of the {@code mustHaveFlags}
Doug Zongkerab5c49c2009-12-04 10:31:43 -08004422 * and none of the {@code mustNotHaveFlags}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004423 */
John Spurlocke6a7d932014-03-13 12:29:00 -04004424 void cancelNotification(final int callingUid, final int callingPid,
4425 final String pkg, final String tag, final int id,
Svetoslav Ganov835835e2013-08-04 20:17:52 -07004426 final int mustHaveFlags, final int mustNotHaveFlags, final boolean sendDelete,
John Spurlock7340fc82014-04-24 18:50:12 -04004427 final int userId, final int reason, final ManagedServiceInfo listener) {
Svetoslav Ganov835835e2013-08-04 20:17:52 -07004428 // In enqueueNotificationInternal notifications are added by scheduling the
4429 // work on the worker handler. Hence, we also schedule the cancel on this
4430 // handler to avoid a scenario where an add notification call followed by a
4431 // remove notification call ends up in not removing the notification.
4432 mHandler.post(new Runnable() {
4433 @Override
4434 public void run() {
Christoph Studere4ef156b2014-07-04 18:41:57 +02004435 String listenerName = listener == null ? null : listener.component.toShortString();
Chris Wrenbddb5bc2015-03-04 08:47:46 -08004436 if (DBG) EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, id, tag,
4437 userId, mustHaveFlags, mustNotHaveFlags, reason, listenerName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004438
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004439 synchronized (mNotificationLock) {
4440 // Look for the notification, searching both the posted and enqueued lists.
4441 NotificationRecord r = findNotificationLocked(pkg, tag, id, userId);
4442 if (r != null) {
4443 // The notification was found, check if it should be removed.
Doug Zongkerab5c49c2009-12-04 10:31:43 -08004444
Christoph Studer546bec82014-03-14 12:17:12 +01004445 // Ideally we'd do this in the caller of this method. However, that would
4446 // require the caller to also find the notification.
Julia Reynoldsf619bc52017-03-17 08:32:23 -04004447 if (reason == REASON_CLICK) {
Christoph Studer546bec82014-03-14 12:17:12 +01004448 mUsageStats.registerClickedByUser(r);
4449 }
4450
Svetoslav Ganov835835e2013-08-04 20:17:52 -07004451 if ((r.getNotification().flags & mustHaveFlags) != mustHaveFlags) {
4452 return;
4453 }
4454 if ((r.getNotification().flags & mustNotHaveFlags) != 0) {
4455 return;
4456 }
4457
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004458 // Cancel the notification.
Julia Reynolds0839c022017-06-15 15:24:01 -04004459 boolean wasPosted = removeFromNotificationListsLocked(r);
4460 cancelNotificationLocked(r, sendDelete, reason, wasPosted);
Christoph Studer265c1052014-07-23 17:14:33 +02004461 cancelGroupChildrenLocked(r, callingUid, callingPid, listenerName,
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004462 sendDelete);
Svetoslav Ganov835835e2013-08-04 20:17:52 -07004463 updateLightsLocked();
Julia Reynolds72f1cbb2016-09-19 14:57:31 -04004464 } else {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004465 // No notification was found, assume that it is snoozed and cancel it.
Julia Reynoldsa8b766f2017-03-07 16:30:21 -05004466 if (reason != REASON_SNOOZED) {
4467 final boolean wasSnoozed = mSnoozeHelper.cancel(userId, pkg, tag, id);
4468 if (wasSnoozed) {
4469 savePolicyFile();
4470 }
Julia Reynolds72f1cbb2016-09-19 14:57:31 -04004471 }
Svetoslav Ganov835835e2013-08-04 20:17:52 -07004472 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004473 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004474 }
Svetoslav Ganov835835e2013-08-04 20:17:52 -07004475 });
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004476 }
4477
4478 /**
Daniel Sandler321e9c52012-10-12 10:59:26 -07004479 * Determine whether the userId applies to the notification in question, either because
4480 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard).
4481 */
4482 private boolean notificationMatchesUserId(NotificationRecord r, int userId) {
4483 return
4484 // looking for USER_ALL notifications? match everything
4485 userId == UserHandle.USER_ALL
4486 // a notification sent to USER_ALL matches any query
Daniel Sandlerfde19b12013-01-17 00:21:05 -05004487 || r.getUserId() == UserHandle.USER_ALL
Daniel Sandler321e9c52012-10-12 10:59:26 -07004488 // an exact user match
Daniel Sandlerfde19b12013-01-17 00:21:05 -05004489 || r.getUserId() == userId;
Daniel Sandler321e9c52012-10-12 10:59:26 -07004490 }
4491
4492 /**
Kenny Guy3a7c4a52014-03-03 18:24:03 +00004493 * Determine whether the userId applies to the notification in question, either because
4494 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard) or
Kenny Guy2a764942014-04-02 13:29:20 +01004495 * because it matches one of the users profiles.
Kenny Guy3a7c4a52014-03-03 18:24:03 +00004496 */
Kenny Guy2a764942014-04-02 13:29:20 +01004497 private boolean notificationMatchesCurrentProfiles(NotificationRecord r, int userId) {
Kenny Guya263e4e2014-03-03 18:24:03 +00004498 return notificationMatchesUserId(r, userId)
John Spurlockb408e8e2014-04-23 21:12:45 -04004499 || mUserProfiles.isCurrentProfile(r.getUserId());
Kenny Guy3a7c4a52014-03-03 18:24:03 +00004500 }
4501
4502 /**
Julia Reynoldsef37f282016-02-12 09:11:27 -05004503 * Cancels all notifications from a given package that have all of the
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004504 * {@code mustHaveFlags}.
4505 */
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004506 void cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, String channelId,
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04004507 int mustHaveFlags, int mustNotHaveFlags, boolean doit, int userId, int reason,
John Spurlock7340fc82014-04-24 18:50:12 -04004508 ManagedServiceInfo listener) {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004509 mHandler.post(new Runnable() {
4510 @Override
4511 public void run() {
4512 String listenerName = listener == null ? null : listener.component.toShortString();
4513 EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
4514 pkg, userId, mustHaveFlags, mustNotHaveFlags, reason,
4515 listenerName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004516
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004517 // Why does this parameter exist? Do we actually want to execute the above if doit
4518 // is false?
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08004519 if (!doit) {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004520 return;
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08004521 }
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004522
4523 synchronized (mNotificationLock) {
4524 FlagChecker flagChecker = (int flags) -> {
4525 if ((flags & mustHaveFlags) != mustHaveFlags) {
4526 return false;
4527 }
4528 if ((flags & mustNotHaveFlags) != 0) {
4529 return false;
4530 }
4531 return true;
4532 };
4533
4534 cancelAllNotificationsByListLocked(mNotificationList, callingUid, callingPid,
4535 pkg, true /*nullPkgIndicatesUserSwitch*/, channelId, flagChecker,
4536 false /*includeCurrentProfiles*/, userId, false /*sendDelete*/, reason,
Julia Reynolds0839c022017-06-15 15:24:01 -04004537 listenerName, true /* wasPosted */);
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004538 cancelAllNotificationsByListLocked(mEnqueuedNotifications, callingUid,
4539 callingPid, pkg, true /*nullPkgIndicatesUserSwitch*/, channelId,
4540 flagChecker, false /*includeCurrentProfiles*/, userId,
Julia Reynolds0839c022017-06-15 15:24:01 -04004541 false /*sendDelete*/, reason, listenerName, false /* wasPosted */);
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004542 mSnoozeHelper.cancel(userId, pkg);
Christoph Studere4ef156b2014-07-04 18:41:57 +02004543 }
4544 }
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004545 });
4546 }
4547
4548 private interface FlagChecker {
4549 // Returns false if these flags do not pass the defined flag test.
4550 public boolean apply(int flags);
4551 }
4552
Julia Reynolds88860ce2017-06-01 16:55:49 -04004553 @GuardedBy("mNotificationLock")
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004554 private void cancelAllNotificationsByListLocked(ArrayList<NotificationRecord> notificationList,
4555 int callingUid, int callingPid, String pkg, boolean nullPkgIndicatesUserSwitch,
4556 String channelId, FlagChecker flagChecker, boolean includeCurrentProfiles, int userId,
Julia Reynolds0839c022017-06-15 15:24:01 -04004557 boolean sendDelete, int reason, String listenerName, boolean wasPosted) {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004558 ArrayList<NotificationRecord> canceledNotifications = null;
4559 for (int i = notificationList.size() - 1; i >= 0; --i) {
4560 NotificationRecord r = notificationList.get(i);
4561 if (includeCurrentProfiles) {
4562 if (!notificationMatchesCurrentProfiles(r, userId)) {
4563 continue;
4564 }
4565 } else if (!notificationMatchesUserId(r, userId)) {
4566 continue;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004567 }
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004568 // Don't remove notifications to all, if there's no package name specified
4569 if (nullPkgIndicatesUserSwitch && pkg == null && r.getUserId() == UserHandle.USER_ALL) {
4570 continue;
4571 }
4572 if (!flagChecker.apply(r.getFlags())) {
4573 continue;
4574 }
4575 if (pkg != null && !r.sbn.getPackageName().equals(pkg)) {
4576 continue;
4577 }
4578 if (channelId != null && !channelId.equals(r.getChannel().getId())) {
4579 continue;
4580 }
4581
4582 if (canceledNotifications == null) {
4583 canceledNotifications = new ArrayList<>();
4584 }
Julia Reynolds0839c022017-06-15 15:24:01 -04004585 notificationList.remove(i);
Julia Reynolds080361e2017-07-13 11:23:12 -04004586 mNotificationsByKey.remove(r.getKey());
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004587 canceledNotifications.add(r);
Julia Reynolds0839c022017-06-15 15:24:01 -04004588 cancelNotificationLocked(r, sendDelete, reason, wasPosted);
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004589 }
4590 if (canceledNotifications != null) {
4591 final int M = canceledNotifications.size();
4592 for (int i = 0; i < M; i++) {
4593 cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid,
4594 listenerName, false /* sendDelete */);
4595 }
4596 updateLightsLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004597 }
4598 }
4599
Julia Reynolds50989772017-02-23 14:32:16 -05004600 void snoozeNotificationInt(String key, long duration, String snoozeCriterionId,
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004601 ManagedServiceInfo listener) {
Julia Reynolds79672302017-01-12 08:30:16 -05004602 String listenerName = listener == null ? null : listener.component.toShortString();
Julia Reynoldsa8b766f2017-03-07 16:30:21 -05004603 if (duration <= 0 && snoozeCriterionId == null || key == null) {
Julia Reynoldscf63ff12017-01-24 13:55:48 -05004604 return;
4605 }
Julia Reynolds520df6e2017-02-13 09:05:10 -05004606
Julia Reynolds79672302017-01-12 08:30:16 -05004607 if (DBG) {
Julia Reynolds50989772017-02-23 14:32:16 -05004608 Slog.d(TAG, String.format("snooze event(%s, %d, %s, %s)", key, duration,
4609 snoozeCriterionId, listenerName));
Julia Reynolds79672302017-01-12 08:30:16 -05004610 }
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004611 // Needs to post so that it can cancel notifications not yet enqueued.
Julia Reynoldsa78cdff2017-04-26 10:19:25 -04004612 mHandler.post(new SnoozeNotificationRunnable(key, duration, snoozeCriterionId));
Julia Reynoldsb6c1f992016-11-22 09:26:46 -05004613 }
4614
4615 void unsnoozeNotificationInt(String key, ManagedServiceInfo listener) {
4616 String listenerName = listener == null ? null : listener.component.toShortString();
Julia Reynoldsb6c1f992016-11-22 09:26:46 -05004617 if (DBG) {
4618 Slog.d(TAG, String.format("unsnooze event(%s, %s)", key, listenerName));
4619 }
Julia Reynolds79672302017-01-12 08:30:16 -05004620 mSnoozeHelper.repost(key);
4621 savePolicyFile();
Julia Reynoldsb6c1f992016-11-22 09:26:46 -05004622 }
4623
Julia Reynolds88860ce2017-06-01 16:55:49 -04004624 @GuardedBy("mNotificationLock")
Adam Lesinski350159c2014-03-27 11:15:11 -07004625 void cancelAllLocked(int callingUid, int callingPid, int userId, int reason,
John Spurlock7340fc82014-04-24 18:50:12 -04004626 ManagedServiceInfo listener, boolean includeCurrentProfiles) {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004627 mHandler.post(new Runnable() {
4628 @Override
4629 public void run() {
4630 synchronized (mNotificationLock) {
4631 String listenerName =
4632 listener == null ? null : listener.component.toShortString();
4633 EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
4634 null, userId, 0, 0, reason, listenerName);
Christoph Studer546bec82014-03-14 12:17:12 +01004635
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004636 FlagChecker flagChecker = (int flags) -> {
4637 if ((flags & (Notification.FLAG_ONGOING_EVENT | Notification.FLAG_NO_CLEAR))
4638 != 0) {
4639 return false;
4640 }
4641 return true;
4642 };
4643
4644 cancelAllNotificationsByListLocked(mNotificationList, callingUid, callingPid,
4645 null, false /*nullPkgIndicatesUserSwitch*/, null, flagChecker,
4646 includeCurrentProfiles, userId, true /*sendDelete*/, reason,
Julia Reynolds0839c022017-06-15 15:24:01 -04004647 listenerName, true);
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004648 cancelAllNotificationsByListLocked(mEnqueuedNotifications, callingUid,
4649 callingPid, null, false /*nullPkgIndicatesUserSwitch*/, null,
4650 flagChecker, includeCurrentProfiles, userId, true /*sendDelete*/,
Julia Reynolds0839c022017-06-15 15:24:01 -04004651 reason, listenerName, false);
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004652 mSnoozeHelper.cancel(userId, includeCurrentProfiles);
Kenny Guya263e4e2014-03-03 18:24:03 +00004653 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004654 }
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004655 });
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004656 }
4657
Christoph Studere4ef156b2014-07-04 18:41:57 +02004658 // Warning: The caller is responsible for invoking updateLightsLocked().
Julia Reynolds88860ce2017-06-01 16:55:49 -04004659 @GuardedBy("mNotificationLock")
Christoph Studere4ef156b2014-07-04 18:41:57 +02004660 private void cancelGroupChildrenLocked(NotificationRecord r, int callingUid, int callingPid,
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004661 String listenerName, boolean sendDelete) {
Christoph Studere4ef156b2014-07-04 18:41:57 +02004662 Notification n = r.getNotification();
Christoph Studer3f31f5d2014-07-31 16:55:32 +02004663 if (!n.isGroupSummary()) {
Christoph Studere4ef156b2014-07-04 18:41:57 +02004664 return;
4665 }
4666
4667 String pkg = r.sbn.getPackageName();
Christoph Studere4ef156b2014-07-04 18:41:57 +02004668
4669 if (pkg == null) {
4670 if (DBG) Log.e(TAG, "No package for group summary: " + r.getKey());
4671 return;
4672 }
4673
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004674 cancelGroupChildrenByListLocked(mNotificationList, r, callingUid, callingPid, listenerName,
Julia Reynolds0839c022017-06-15 15:24:01 -04004675 sendDelete, true);
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004676 cancelGroupChildrenByListLocked(mEnqueuedNotifications, r, callingUid, callingPid,
Julia Reynolds0839c022017-06-15 15:24:01 -04004677 listenerName, sendDelete, false);
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004678 }
4679
Julia Reynolds88860ce2017-06-01 16:55:49 -04004680 @GuardedBy("mNotificationLock")
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004681 private void cancelGroupChildrenByListLocked(ArrayList<NotificationRecord> notificationList,
4682 NotificationRecord parentNotification, int callingUid, int callingPid,
Julia Reynolds0839c022017-06-15 15:24:01 -04004683 String listenerName, boolean sendDelete, boolean wasPosted) {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004684 final String pkg = parentNotification.sbn.getPackageName();
4685 final int userId = parentNotification.getUserId();
4686 final int reason = REASON_GROUP_SUMMARY_CANCELED;
4687 for (int i = notificationList.size() - 1; i >= 0; i--) {
4688 final NotificationRecord childR = notificationList.get(i);
4689 final StatusBarNotification childSbn = childR.sbn;
Julia Reynoldse46bb372016-03-17 11:05:58 -04004690 if ((childSbn.isGroup() && !childSbn.getNotification().isGroupSummary()) &&
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004691 childR.getGroupKey().equals(parentNotification.getGroupKey())
Selim Cinek1d359792017-01-13 14:43:43 -08004692 && (childR.getFlags() & Notification.FLAG_FOREGROUND_SERVICE) == 0) {
Christoph Studer265c1052014-07-23 17:14:33 +02004693 EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, childSbn.getId(),
4694 childSbn.getTag(), userId, 0, 0, reason, listenerName);
Julia Reynolds0839c022017-06-15 15:24:01 -04004695 notificationList.remove(i);
Julia Reynolds080361e2017-07-13 11:23:12 -04004696 mNotificationsByKey.remove(childR.getKey());
Julia Reynolds0839c022017-06-15 15:24:01 -04004697 cancelNotificationLocked(childR, sendDelete, reason, wasPosted);
Christoph Studere4ef156b2014-07-04 18:41:57 +02004698 }
4699 }
4700 }
4701
Julia Reynolds88860ce2017-06-01 16:55:49 -04004702 @GuardedBy("mNotificationLock")
Adam Lesinski182f73f2013-12-05 16:48:06 -08004703 void updateLightsLocked()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004704 {
The Android Open Source Project10592532009-03-18 17:39:46 -07004705 // handle notification lights
Chris Wren6054e612014-11-25 17:16:46 -05004706 NotificationRecord ledNotification = null;
4707 while (ledNotification == null && !mLights.isEmpty()) {
4708 final String owner = mLights.get(mLights.size() - 1);
4709 ledNotification = mNotificationsByKey.get(owner);
4710 if (ledNotification == null) {
4711 Slog.wtfStack(TAG, "LED Notification does not exist: " + owner);
4712 mLights.remove(owner);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004713 }
4714 }
Mike Lockwoodc22404a2009-12-02 11:15:02 -05004715
Mike Lockwood63b5ad92011-08-30 09:55:30 -04004716 // Don't flash while we are in a call or screen is on
Chris Wren6054e612014-11-25 17:16:46 -05004717 if (ledNotification == null || mInCall || mScreenOn) {
Mike Lockwood3cb67a32009-11-27 14:25:58 -05004718 mNotificationLight.turnOff();
The Android Open Source Project10592532009-03-18 17:39:46 -07004719 } else {
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05004720 NotificationRecord.Light light = ledNotification.getLight();
4721 if (light != null && mNotificationPulseEnabled) {
Mike Lockwood670f9322010-01-20 12:13:36 -05004722 // pulse repeatedly
Julia Reynoldsa33f5c42017-01-31 16:53:35 -05004723 mNotificationLight.setFlashing(light.color, Light.LIGHT_FLASH_TIMED,
4724 light.onMs, light.offMs);
Mike Lockwood670f9322010-01-20 12:13:36 -05004725 }
The Android Open Source Project10592532009-03-18 17:39:46 -07004726 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004727 }
4728
Julia Reynolds88860ce2017-06-01 16:55:49 -04004729 @GuardedBy("mNotificationLock")
Julia Reynoldsa78cdff2017-04-26 10:19:25 -04004730 @NonNull List<NotificationRecord> findGroupNotificationsLocked(String pkg,
4731 String groupKey, int userId) {
4732 List<NotificationRecord> records = new ArrayList<>();
4733 records.addAll(findGroupNotificationByListLocked(mNotificationList, pkg, groupKey, userId));
4734 records.addAll(
4735 findGroupNotificationByListLocked(mEnqueuedNotifications, pkg, groupKey, userId));
4736 return records;
4737 }
4738
4739
Julia Reynolds88860ce2017-06-01 16:55:49 -04004740 @GuardedBy("mNotificationLock")
Julia Reynoldsa78cdff2017-04-26 10:19:25 -04004741 private @NonNull List<NotificationRecord> findGroupNotificationByListLocked(
4742 ArrayList<NotificationRecord> list, String pkg, String groupKey, int userId) {
4743 List<NotificationRecord> records = new ArrayList<>();
4744 final int len = list.size();
4745 for (int i = 0; i < len; i++) {
4746 NotificationRecord r = list.get(i);
4747 if (notificationMatchesUserId(r, userId) && r.getGroupKey().equals(groupKey)
4748 && r.sbn.getPackageName().equals(pkg)) {
4749 records.add(r);
4750 }
4751 }
4752 return records;
4753 }
4754
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004755 // Searches both enqueued and posted notifications by key.
4756 // TODO: need to combine a bunch of these getters with slightly different behavior.
4757 // TODO: Should enqueuing just add to mNotificationsByKey instead?
Julia Reynolds88860ce2017-06-01 16:55:49 -04004758 @GuardedBy("mNotificationLock")
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004759 private NotificationRecord findNotificationByKeyLocked(String key) {
Geoffrey Pitschccc0b972017-02-15 10:52:26 -05004760 NotificationRecord r;
4761 if ((r = findNotificationByListLocked(mNotificationList, key)) != null) {
4762 return r;
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004763 }
Geoffrey Pitschccc0b972017-02-15 10:52:26 -05004764 if ((r = findNotificationByListLocked(mEnqueuedNotifications, key)) != null) {
4765 return r;
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004766 }
4767 return null;
4768 }
4769
Julia Reynolds88860ce2017-06-01 16:55:49 -04004770 @GuardedBy("mNotificationLock")
Julia Reynoldsa78cdff2017-04-26 10:19:25 -04004771 NotificationRecord findNotificationLocked(String pkg, String tag, int id, int userId) {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004772 NotificationRecord r;
4773 if ((r = findNotificationByListLocked(mNotificationList, pkg, tag, id, userId)) != null) {
4774 return r;
4775 }
4776 if ((r = findNotificationByListLocked(mEnqueuedNotifications, pkg, tag, id, userId))
4777 != null) {
4778 return r;
4779 }
4780 return null;
4781 }
4782
Julia Reynolds88860ce2017-06-01 16:55:49 -04004783 @GuardedBy("mNotificationLock")
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004784 private NotificationRecord findNotificationByListLocked(ArrayList<NotificationRecord> list,
Geoffrey Pitschccc0b972017-02-15 10:52:26 -05004785 String pkg, String tag, int id, int userId) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004786 final int len = list.size();
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004787 for (int i = 0; i < len; i++) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004788 NotificationRecord r = list.get(i);
Vladimir Marko2526f332013-09-11 11:13:55 +01004789 if (notificationMatchesUserId(r, userId) && r.sbn.getId() == id &&
4790 TextUtils.equals(r.sbn.getTag(), tag) && r.sbn.getPackageName().equals(pkg)) {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004791 return r;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004792 }
4793 }
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004794 return null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004795 }
4796
Julia Reynolds88860ce2017-06-01 16:55:49 -04004797 @GuardedBy("mNotificationLock")
Geoffrey Pitschccc0b972017-02-15 10:52:26 -05004798 private NotificationRecord findNotificationByListLocked(ArrayList<NotificationRecord> list,
Julia Reynolds88860ce2017-06-01 16:55:49 -04004799 String key) {
Geoffrey Pitschccc0b972017-02-15 10:52:26 -05004800 final int N = list.size();
4801 for (int i = 0; i < N; i++) {
4802 if (key.equals(list.get(i).getKey())) {
4803 return list.get(i);
4804 }
4805 }
4806 return null;
4807 }
4808
Julia Reynolds88860ce2017-06-01 16:55:49 -04004809 @GuardedBy("mNotificationLock")
Christoph Studer71f18fd2014-05-20 17:02:04 +02004810 int indexOfNotificationLocked(String key) {
Christoph Studerc5115552014-06-12 20:22:31 +02004811 final int N = mNotificationList.size();
4812 for (int i = 0; i < N; i++) {
4813 if (key.equals(mNotificationList.get(i).getKey())) {
4814 return i;
4815 }
Christoph Studer71f18fd2014-05-20 17:02:04 +02004816 }
Christoph Studerc5115552014-06-12 20:22:31 +02004817 return -1;
Christoph Studer71f18fd2014-05-20 17:02:04 +02004818 }
4819
Mike Lockwoodc22404a2009-12-02 11:15:02 -05004820 private void updateNotificationPulse() {
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05004821 synchronized (mNotificationLock) {
Mike Lockwoodc22404a2009-12-02 11:15:02 -05004822 updateLightsLocked();
4823 }
4824 }
John Spurlocke677d712014-02-13 12:52:19 -05004825
Geoffrey Pitsch27684152017-05-02 11:41:31 -04004826 protected boolean isCallingUidSystem() {
4827 final int uid = Binder.getCallingUid();
4828 return uid == Process.SYSTEM_UID;
4829 }
4830
4831 protected boolean isUidSystemOrPhone(int uid) {
John Spurlock7340fc82014-04-24 18:50:12 -04004832 final int appid = UserHandle.getAppId(uid);
4833 return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0);
4834 }
John Spurlockb408e8e2014-04-23 21:12:45 -04004835
Geoffrey Pitsch27684152017-05-02 11:41:31 -04004836 // TODO: Most calls should probably move to isCallerSystem.
4837 protected boolean isCallerSystemOrPhone() {
4838 return isUidSystemOrPhone(Binder.getCallingUid());
John Spurlock7340fc82014-04-24 18:50:12 -04004839 }
4840
Julia Reynolds73ed76b2017-04-04 17:04:38 -04004841 private void checkCallerIsSystem() {
Geoffrey Pitsch27684152017-05-02 11:41:31 -04004842 if (isCallerSystemOrPhone()) {
John Spurlock7340fc82014-04-24 18:50:12 -04004843 return;
4844 }
4845 throw new SecurityException("Disallowed call for uid " + Binder.getCallingUid());
4846 }
4847
Geoffrey Pitsche75a66e2016-11-22 11:12:11 -05004848 private void checkCallerIsSystemOrSameApp(String pkg) {
Geoffrey Pitsch27684152017-05-02 11:41:31 -04004849 if (isCallerSystemOrPhone()) {
John Spurlock7340fc82014-04-24 18:50:12 -04004850 return;
4851 }
Julia Reynolds0cd1b782016-06-29 08:43:00 -04004852 checkCallerIsSameApp(pkg);
4853 }
4854
Chad Brubaker6b68f102017-01-27 13:39:00 -08004855 private boolean isCallerInstantApp(String pkg) {
4856 // System is always allowed to act for ephemeral apps.
Geoffrey Pitsch27684152017-05-02 11:41:31 -04004857 if (isCallerSystemOrPhone()) {
Chad Brubaker6b68f102017-01-27 13:39:00 -08004858 return false;
4859 }
4860
4861 mAppOps.checkPackage(Binder.getCallingUid(), pkg);
4862
4863 try {
4864 ApplicationInfo ai = mPackageManager.getApplicationInfo(pkg, 0,
4865 UserHandle.getCallingUserId());
4866 if (ai == null) {
4867 throw new SecurityException("Unknown package " + pkg);
4868 }
4869 return ai.isInstantApp();
4870 } catch (RemoteException re) {
4871 throw new SecurityException("Unknown package " + pkg, re);
4872 }
4873
4874 }
4875
Geoffrey Pitsche75a66e2016-11-22 11:12:11 -05004876 private void checkCallerIsSameApp(String pkg) {
John Spurlock7340fc82014-04-24 18:50:12 -04004877 final int uid = Binder.getCallingUid();
4878 try {
Geoffrey Pitsche75a66e2016-11-22 11:12:11 -05004879 ApplicationInfo ai = mPackageManager.getApplicationInfo(
John Spurlock7340fc82014-04-24 18:50:12 -04004880 pkg, 0, UserHandle.getCallingUserId());
Dan Sandler09afc2e2014-07-18 14:29:20 -04004881 if (ai == null) {
4882 throw new SecurityException("Unknown package " + pkg);
4883 }
John Spurlock7340fc82014-04-24 18:50:12 -04004884 if (!UserHandle.isSameApp(ai.uid, uid)) {
Geoffrey Pitsche75a66e2016-11-22 11:12:11 -05004885 throw new SecurityException("Calling uid " + uid + " gave package "
John Spurlock7340fc82014-04-24 18:50:12 -04004886 + pkg + " which is owned by uid " + ai.uid);
4887 }
4888 } catch (RemoteException re) {
4889 throw new SecurityException("Unknown package " + pkg + "\n" + re);
4890 }
4891 }
4892
John Spurlock32fe4c62014-10-02 12:16:02 -04004893 private static String callStateToString(int state) {
4894 switch (state) {
4895 case TelephonyManager.CALL_STATE_IDLE: return "CALL_STATE_IDLE";
4896 case TelephonyManager.CALL_STATE_RINGING: return "CALL_STATE_RINGING";
4897 case TelephonyManager.CALL_STATE_OFFHOOK: return "CALL_STATE_OFFHOOK";
4898 default: return "CALL_STATE_UNKNOWN_" + state;
4899 }
4900 }
4901
4902 private void listenForCallState() {
4903 TelephonyManager.from(getContext()).listen(new PhoneStateListener() {
4904 @Override
4905 public void onCallStateChanged(int state, String incomingNumber) {
4906 if (mCallState == state) return;
4907 if (DBG) Slog.d(TAG, "Call state changed: " + callStateToString(state));
4908 mCallState = state;
4909 }
4910 }, PhoneStateListener.LISTEN_CALL_STATE);
4911 }
4912
Christoph Studer05ad4822014-05-16 14:16:03 +02004913 /**
4914 * Generates a NotificationRankingUpdate from 'sbns', considering only
4915 * notifications visible to the given listener.
4916 */
Julia Reynolds88860ce2017-06-01 16:55:49 -04004917 @GuardedBy("mNotificationLock")
Chris Wren333a61c2014-05-28 16:40:57 -04004918 private NotificationRankingUpdate makeRankingUpdateLocked(ManagedServiceInfo info) {
Chris Wren333a61c2014-05-28 16:40:57 -04004919 final int N = mNotificationList.size();
4920 ArrayList<String> keys = new ArrayList<String>(N);
Christoph Studer1d599da2014-06-12 15:25:59 +02004921 ArrayList<String> interceptedKeys = new ArrayList<String>(N);
Chris Wrenbdf33762015-12-04 15:50:51 -05004922 ArrayList<Integer> importance = new ArrayList<>(N);
Julia Reynoldse46bb372016-03-17 11:05:58 -04004923 Bundle overrideGroupKeys = new Bundle();
Chris Wren3ad4e3a2014-09-02 17:23:51 -04004924 Bundle visibilityOverrides = new Bundle();
Julia Reynoldsf612869ae2015-11-05 16:48:55 -05004925 Bundle suppressedVisualEffects = new Bundle();
Chris Wrenbdf33762015-12-04 15:50:51 -05004926 Bundle explanation = new Bundle();
Julia Reynolds924eed12017-01-19 09:52:07 -05004927 Bundle channels = new Bundle();
Julia Reynolds22f02b32016-12-01 15:05:13 -05004928 Bundle overridePeople = new Bundle();
4929 Bundle snoozeCriteria = new Bundle();
Julia Reynolds924eed12017-01-19 09:52:07 -05004930 Bundle showBadge = new Bundle();
Chris Wren333a61c2014-05-28 16:40:57 -04004931 for (int i = 0; i < N; i++) {
4932 NotificationRecord record = mNotificationList.get(i);
Christoph Studercef37cf2014-07-25 14:18:17 +02004933 if (!isVisibleToListener(record.sbn, info)) {
Christoph Studer05ad4822014-05-16 14:16:03 +02004934 continue;
4935 }
Chris Wrenbdf33762015-12-04 15:50:51 -05004936 final String key = record.sbn.getKey();
4937 keys.add(key);
4938 importance.add(record.getImportance());
4939 if (record.getImportanceExplanation() != null) {
4940 explanation.putCharSequence(key, record.getImportanceExplanation());
4941 }
Chris Wren333a61c2014-05-28 16:40:57 -04004942 if (record.isIntercepted()) {
Chris Wrenbdf33762015-12-04 15:50:51 -05004943 interceptedKeys.add(key);
Julia Reynoldsf612869ae2015-11-05 16:48:55 -05004944
Christoph Studer05ad4822014-05-16 14:16:03 +02004945 }
Chris Wrenbdf33762015-12-04 15:50:51 -05004946 suppressedVisualEffects.putInt(key, record.getSuppressedVisualEffects());
Chris Wren3ad4e3a2014-09-02 17:23:51 -04004947 if (record.getPackageVisibilityOverride()
4948 != NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE) {
Chris Wrenbdf33762015-12-04 15:50:51 -05004949 visibilityOverrides.putInt(key, record.getPackageVisibilityOverride());
Chris Wren3ad4e3a2014-09-02 17:23:51 -04004950 }
Julia Reynoldse46bb372016-03-17 11:05:58 -04004951 overrideGroupKeys.putString(key, record.sbn.getOverrideGroupKey());
Julia Reynolds924eed12017-01-19 09:52:07 -05004952 channels.putParcelable(key, record.getChannel());
Julia Reynolds22f02b32016-12-01 15:05:13 -05004953 overridePeople.putStringArrayList(key, record.getPeopleOverride());
4954 snoozeCriteria.putParcelableArrayList(key, record.getSnoozeCriteria());
Julia Reynolds924eed12017-01-19 09:52:07 -05004955 showBadge.putBoolean(key, record.canShowBadge());
Christoph Studer05ad4822014-05-16 14:16:03 +02004956 }
Chris Wrenbdf33762015-12-04 15:50:51 -05004957 final int M = keys.size();
4958 String[] keysAr = keys.toArray(new String[M]);
Christoph Studer1d599da2014-06-12 15:25:59 +02004959 String[] interceptedKeysAr = interceptedKeys.toArray(new String[interceptedKeys.size()]);
Chris Wrenbdf33762015-12-04 15:50:51 -05004960 int[] importanceAr = new int[M];
4961 for (int i = 0; i < M; i++) {
4962 importanceAr[i] = importance.get(i);
4963 }
Chris Wren3ad4e3a2014-09-02 17:23:51 -04004964 return new NotificationRankingUpdate(keysAr, interceptedKeysAr, visibilityOverrides,
Julia Reynolds22f02b32016-12-01 15:05:13 -05004965 suppressedVisualEffects, importanceAr, explanation, overrideGroupKeys,
Julia Reynolds924eed12017-01-19 09:52:07 -05004966 channels, overridePeople, snoozeCriteria, showBadge);
Christoph Studer05ad4822014-05-16 14:16:03 +02004967 }
4968
Julia Reynoldsda781472017-04-12 09:41:16 -04004969 boolean hasCompanionDevice(ManagedServiceInfo info) {
Julia Reynolds73ed76b2017-04-04 17:04:38 -04004970 if (mCompanionManager == null) {
Julia Reynolds727a7282017-04-13 10:54:01 -04004971 mCompanionManager = getCompanionManager();
4972 }
4973 // Companion mgr doesn't exist on all device types
4974 if (mCompanionManager == null) {
4975 return false;
Julia Reynolds73ed76b2017-04-04 17:04:38 -04004976 }
Julia Reynoldsda781472017-04-12 09:41:16 -04004977 long identity = Binder.clearCallingIdentity();
4978 try {
4979 List<String> associations = mCompanionManager.getAssociations(
4980 info.component.getPackageName(), info.userid);
4981 if (!ArrayUtils.isEmpty(associations)) {
4982 return true;
4983 }
4984 } catch (SecurityException se) {
4985 // Not a privileged listener
4986 } catch (RemoteException re) {
4987 Slog.e(TAG, "Cannot reach companion device service", re);
4988 } catch (Exception e) {
4989 Slog.e(TAG, "Cannot verify listener " + info, e);
4990 } finally {
4991 Binder.restoreCallingIdentity(identity);
Julia Reynolds73ed76b2017-04-04 17:04:38 -04004992 }
Julia Reynoldsda781472017-04-12 09:41:16 -04004993 return false;
Julia Reynolds73ed76b2017-04-04 17:04:38 -04004994 }
4995
Julia Reynolds727a7282017-04-13 10:54:01 -04004996 protected ICompanionDeviceManager getCompanionManager() {
4997 return ICompanionDeviceManager.Stub.asInterface(
4998 ServiceManager.getService(Context.COMPANION_DEVICE_SERVICE));
4999 }
5000
Christoph Studercef37cf2014-07-25 14:18:17 +02005001 private boolean isVisibleToListener(StatusBarNotification sbn, ManagedServiceInfo listener) {
5002 if (!listener.enabledAndUserMatches(sbn.getUserId())) {
5003 return false;
5004 }
Justin Koh8d11a5a2014-08-04 18:29:49 -07005005 // TODO: remove this for older listeners.
Christoph Studercef37cf2014-07-25 14:18:17 +02005006 return true;
5007 }
5008
Andrei Stingaceanu355b2322016-02-12 16:43:51 +00005009 private boolean isPackageSuspendedForUser(String pkg, int uid) {
Andrei Stingaceanu0122f6512016-01-22 15:33:03 +00005010 int userId = UserHandle.getUserId(uid);
Andrei Stingaceanu0122f6512016-01-22 15:33:03 +00005011 try {
Geoffrey Pitsche75a66e2016-11-22 11:12:11 -05005012 return mPackageManager.isPackageSuspendedForUser(pkg, userId);
Andrei Stingaceanu0122f6512016-01-22 15:33:03 +00005013 } catch (RemoteException re) {
5014 throw new SecurityException("Could not talk to package manager service");
Andrei Stingaceanuefc4a342016-03-22 14:43:01 +00005015 } catch (IllegalArgumentException ex) {
5016 // Package not found.
5017 return false;
Andrei Stingaceanu0122f6512016-01-22 15:33:03 +00005018 }
Andrei Stingaceanu0122f6512016-01-22 15:33:03 +00005019 }
5020
Chris Wren47633422016-01-22 09:56:59 -05005021 private class TrimCache {
5022 StatusBarNotification heavy;
5023 StatusBarNotification sbnClone;
5024 StatusBarNotification sbnCloneLight;
5025
5026 TrimCache(StatusBarNotification sbn) {
5027 heavy = sbn;
5028 }
5029
5030 StatusBarNotification ForListener(ManagedServiceInfo info) {
5031 if (mListeners.getOnNotificationPostedTrim(info) == TRIM_LIGHT) {
5032 if (sbnCloneLight == null) {
5033 sbnCloneLight = heavy.cloneLight();
5034 }
5035 return sbnCloneLight;
5036 } else {
5037 if (sbnClone == null) {
5038 sbnClone = heavy.clone();
5039 }
5040 return sbnClone;
5041 }
5042 }
5043 }
5044
Julia Reynolds77b2cc92016-11-08 14:41:09 -05005045 public class NotificationAssistants extends ManagedServices {
Chris Wren51017d02015-12-15 15:34:46 -05005046
Julia Reynolds77b2cc92016-11-08 14:41:09 -05005047 public NotificationAssistants() {
Julia Reynolds88860ce2017-06-01 16:55:49 -04005048 super(getContext(), mHandler, mNotificationLock, mUserProfiles);
Chris Wren51017d02015-12-15 15:34:46 -05005049 }
5050
5051 @Override
5052 protected Config getConfig() {
5053 Config c = new Config();
Julia Reynolds77b2cc92016-11-08 14:41:09 -05005054 c.caption = "notification assistant service";
5055 c.serviceInterface = NotificationAssistantService.SERVICE_INTERFACE;
5056 c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_ASSISTANT;
5057 c.bindPermission = Manifest.permission.BIND_NOTIFICATION_ASSISTANT_SERVICE;
Chris Wren51017d02015-12-15 15:34:46 -05005058 c.settingsAction = Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS;
Chris Wrene0ba7eb2016-03-04 17:30:43 -05005059 c.clientLabel = R.string.notification_ranker_binding_label;
Chris Wren51017d02015-12-15 15:34:46 -05005060 return c;
5061 }
5062
5063 @Override
5064 protected IInterface asInterface(IBinder binder) {
5065 return INotificationListener.Stub.asInterface(binder);
5066 }
5067
5068 @Override
5069 protected boolean checkType(IInterface service) {
5070 return service instanceof INotificationListener;
5071 }
5072
5073 @Override
5074 protected void onServiceAdded(ManagedServiceInfo info) {
5075 mListeners.registerGuestService(info);
5076 }
5077
5078 @Override
Julia Reynolds88860ce2017-06-01 16:55:49 -04005079 @GuardedBy("mNotificationLock")
Chris Wren51017d02015-12-15 15:34:46 -05005080 protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
5081 mListeners.unregisterService(removed.service, removed.userid);
5082 }
Chris Wren47633422016-01-22 09:56:59 -05005083
5084 public void onNotificationEnqueued(final NotificationRecord r) {
5085 final StatusBarNotification sbn = r.sbn;
5086 TrimCache trimCache = new TrimCache(sbn);
5087
Chris Wren47633422016-01-22 09:56:59 -05005088 // There should be only one, but it's a list, so while we enforce
5089 // singularity elsewhere, we keep it general here, to avoid surprises.
Julia Reynolds00314d92017-04-14 10:01:24 -04005090 for (final ManagedServiceInfo info : NotificationAssistants.this.getServices()) {
Chris Wren47633422016-01-22 09:56:59 -05005091 boolean sbnVisible = isVisibleToListener(sbn, info);
5092 if (!sbnVisible) {
5093 continue;
5094 }
5095
5096 final int importance = r.getImportance();
5097 final boolean fromUser = r.isImportanceFromUser();
5098 final StatusBarNotification sbnToPost = trimCache.ForListener(info);
Chris Wrene0ba7eb2016-03-04 17:30:43 -05005099 mHandler.post(new Runnable() {
Chris Wren47633422016-01-22 09:56:59 -05005100 @Override
5101 public void run() {
Julia Reynoldsceecfcf2017-01-31 09:44:26 -05005102 notifyEnqueued(info, sbnToPost);
Chris Wren47633422016-01-22 09:56:59 -05005103 }
5104 });
5105 }
5106 }
5107
5108 private void notifyEnqueued(final ManagedServiceInfo info,
Julia Reynoldsceecfcf2017-01-31 09:44:26 -05005109 final StatusBarNotification sbn) {
Julia Reynolds77b2cc92016-11-08 14:41:09 -05005110 final INotificationListener assistant = (INotificationListener) info.service;
Chris Wren47633422016-01-22 09:56:59 -05005111 StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
5112 try {
Julia Reynoldsceecfcf2017-01-31 09:44:26 -05005113 assistant.onNotificationEnqueued(sbnHolder);
Chris Wren47633422016-01-22 09:56:59 -05005114 } catch (RemoteException ex) {
Julia Reynolds77b2cc92016-11-08 14:41:09 -05005115 Log.e(TAG, "unable to notify assistant (enqueued): " + assistant, ex);
Chris Wren47633422016-01-22 09:56:59 -05005116 }
5117 }
5118
Julia Reynolds79672302017-01-12 08:30:16 -05005119 /**
5120 * asynchronously notify the assistant that a notification has been snoozed until a
5121 * context
5122 */
Julia Reynolds88860ce2017-06-01 16:55:49 -04005123 @GuardedBy("mNotificationLock")
Julia Reynolds79672302017-01-12 08:30:16 -05005124 public void notifyAssistantSnoozedLocked(final StatusBarNotification sbn,
5125 final String snoozeCriterionId) {
5126 TrimCache trimCache = new TrimCache(sbn);
Julia Reynolds00314d92017-04-14 10:01:24 -04005127 for (final ManagedServiceInfo info : getServices()) {
Julia Reynolds79672302017-01-12 08:30:16 -05005128 final StatusBarNotification sbnToPost = trimCache.ForListener(info);
5129 mHandler.post(new Runnable() {
5130 @Override
5131 public void run() {
5132 final INotificationListener assistant =
5133 (INotificationListener) info.service;
5134 StatusBarNotificationHolder sbnHolder
5135 = new StatusBarNotificationHolder(sbnToPost);
5136 try {
5137 assistant.onNotificationSnoozedUntilContext(
5138 sbnHolder, snoozeCriterionId);
5139 } catch (RemoteException ex) {
5140 Log.e(TAG, "unable to notify assistant (snoozed): " + assistant, ex);
5141 }
5142 }
5143 });
5144 }
5145 }
5146
Chris Wren47633422016-01-22 09:56:59 -05005147 public boolean isEnabled() {
Julia Reynolds00314d92017-04-14 10:01:24 -04005148 return !getServices().isEmpty();
Chris Wren47633422016-01-22 09:56:59 -05005149 }
Chris Wren51017d02015-12-15 15:34:46 -05005150 }
5151
John Spurlock7340fc82014-04-24 18:50:12 -04005152 public class NotificationListeners extends ManagedServices {
5153
Christoph Studerb82bc782014-08-20 14:29:43 +02005154 private final ArraySet<ManagedServiceInfo> mLightTrimListeners = new ArraySet<>();
5155
John Spurlock7340fc82014-04-24 18:50:12 -04005156 public NotificationListeners() {
Julia Reynolds88860ce2017-06-01 16:55:49 -04005157 super(getContext(), mHandler, mNotificationLock, mUserProfiles);
John Spurlock7340fc82014-04-24 18:50:12 -04005158 }
5159
5160 @Override
5161 protected Config getConfig() {
5162 Config c = new Config();
5163 c.caption = "notification listener";
5164 c.serviceInterface = NotificationListenerService.SERVICE_INTERFACE;
5165 c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_LISTENERS;
5166 c.bindPermission = android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE;
5167 c.settingsAction = Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS;
5168 c.clientLabel = R.string.notification_listener_binding_label;
5169 return c;
5170 }
5171
5172 @Override
5173 protected IInterface asInterface(IBinder binder) {
5174 return INotificationListener.Stub.asInterface(binder);
5175 }
5176
5177 @Override
Chris Wren51017d02015-12-15 15:34:46 -05005178 protected boolean checkType(IInterface service) {
5179 return service instanceof INotificationListener;
5180 }
5181
5182 @Override
John Spurlock3b98b3f2014-05-01 09:08:48 -04005183 public void onServiceAdded(ManagedServiceInfo info) {
5184 final INotificationListener listener = (INotificationListener) info.service;
Chris Wren333a61c2014-05-28 16:40:57 -04005185 final NotificationRankingUpdate update;
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05005186 synchronized (mNotificationLock) {
Chris Wren333a61c2014-05-28 16:40:57 -04005187 update = makeRankingUpdateLocked(info);
Christoph Studer05ad4822014-05-16 14:16:03 +02005188 }
John Spurlock7340fc82014-04-24 18:50:12 -04005189 try {
Chris Wren333a61c2014-05-28 16:40:57 -04005190 listener.onListenerConnected(update);
John Spurlock7340fc82014-04-24 18:50:12 -04005191 } catch (RemoteException e) {
5192 // we tried
5193 }
5194 }
5195
John Spurlock1fa865f2014-07-21 14:56:39 -04005196 @Override
Julia Reynolds88860ce2017-06-01 16:55:49 -04005197 @GuardedBy("mNotificationLock")
John Spurlock1fa865f2014-07-21 14:56:39 -04005198 protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
Bryce Lee7219ada2016-04-08 10:54:23 -07005199 if (removeDisabledHints(removed)) {
John Spurlockd8afe3c2014-08-01 14:04:07 -04005200 updateListenerHintsLocked();
Christoph Studer0d6ef4b2014-12-02 15:00:48 +01005201 updateEffectsSuppressorLocked();
John Spurlock1fa865f2014-07-21 14:56:39 -04005202 }
Christoph Studerb82bc782014-08-20 14:29:43 +02005203 mLightTrimListeners.remove(removed);
5204 }
5205
Julia Reynolds88860ce2017-06-01 16:55:49 -04005206 @GuardedBy("mNotificationLock")
Christoph Studerb82bc782014-08-20 14:29:43 +02005207 public void setOnNotificationPostedTrimLocked(ManagedServiceInfo info, int trim) {
5208 if (trim == TRIM_LIGHT) {
5209 mLightTrimListeners.add(info);
5210 } else {
5211 mLightTrimListeners.remove(info);
5212 }
5213 }
5214
5215 public int getOnNotificationPostedTrim(ManagedServiceInfo info) {
5216 return mLightTrimListeners.contains(info) ? TRIM_LIGHT : TRIM_FULL;
John Spurlock1fa865f2014-07-21 14:56:39 -04005217 }
5218
John Spurlock7340fc82014-04-24 18:50:12 -04005219 /**
5220 * asynchronously notify all listeners about a new notification
Christoph Studercef37cf2014-07-25 14:18:17 +02005221 *
5222 * <p>
5223 * Also takes care of removing a notification that has been visible to a listener before,
5224 * but isn't anymore.
John Spurlock7340fc82014-04-24 18:50:12 -04005225 */
Julia Reynolds88860ce2017-06-01 16:55:49 -04005226 @GuardedBy("mNotificationLock")
Christoph Studercef37cf2014-07-25 14:18:17 +02005227 public void notifyPostedLocked(StatusBarNotification sbn, StatusBarNotification oldSbn) {
Christoph Studerb82bc782014-08-20 14:29:43 +02005228 // Lazily initialized snapshots of the notification.
Chris Wren47633422016-01-22 09:56:59 -05005229 TrimCache trimCache = new TrimCache(sbn);
Christoph Studerb82bc782014-08-20 14:29:43 +02005230
Julia Reynolds00314d92017-04-14 10:01:24 -04005231 for (final ManagedServiceInfo info : getServices()) {
Christoph Studercef37cf2014-07-25 14:18:17 +02005232 boolean sbnVisible = isVisibleToListener(sbn, info);
5233 boolean oldSbnVisible = oldSbn != null ? isVisibleToListener(oldSbn, info) : false;
5234 // This notification hasn't been and still isn't visible -> ignore.
5235 if (!oldSbnVisible && !sbnVisible) {
Christoph Studer05ad4822014-05-16 14:16:03 +02005236 continue;
Chris Wrenf9536642014-04-17 10:01:54 -04005237 }
Chris Wren333a61c2014-05-28 16:40:57 -04005238 final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
Christoph Studercef37cf2014-07-25 14:18:17 +02005239
5240 // This notification became invisible -> remove the old one.
5241 if (oldSbnVisible && !sbnVisible) {
5242 final StatusBarNotification oldSbnLightClone = oldSbn.cloneLight();
5243 mHandler.post(new Runnable() {
5244 @Override
5245 public void run() {
Julia Reynolds3aa5f1e2016-11-09 15:43:49 -05005246 notifyRemoved(info, oldSbnLightClone, update, REASON_USER_STOPPED);
Christoph Studercef37cf2014-07-25 14:18:17 +02005247 }
5248 });
Christoph Studer05ad4822014-05-16 14:16:03 +02005249 continue;
5250 }
Christoph Studercef37cf2014-07-25 14:18:17 +02005251
Chris Wren47633422016-01-22 09:56:59 -05005252 final StatusBarNotification sbnToPost = trimCache.ForListener(info);
Christoph Studer05ad4822014-05-16 14:16:03 +02005253 mHandler.post(new Runnable() {
5254 @Override
5255 public void run() {
Christoph Studerb82bc782014-08-20 14:29:43 +02005256 notifyPosted(info, sbnToPost, update);
Christoph Studer05ad4822014-05-16 14:16:03 +02005257 }
5258 });
Kenny Guy3a7c4a52014-03-03 18:24:03 +00005259 }
5260 }
Kenny Guy3a7c4a52014-03-03 18:24:03 +00005261
John Spurlock7340fc82014-04-24 18:50:12 -04005262 /**
5263 * asynchronously notify all listeners about a removed notification
5264 */
Julia Reynolds88860ce2017-06-01 16:55:49 -04005265 @GuardedBy("mNotificationLock")
Julia Reynolds3aa5f1e2016-11-09 15:43:49 -05005266 public void notifyRemovedLocked(StatusBarNotification sbn, int reason) {
John Spurlock7340fc82014-04-24 18:50:12 -04005267 // make a copy in case changes are made to the underlying Notification object
5268 // NOTE: this copy is lightweight: it doesn't include heavyweight parts of the
5269 // notification
5270 final StatusBarNotification sbnLight = sbn.cloneLight();
Julia Reynolds00314d92017-04-14 10:01:24 -04005271 for (final ManagedServiceInfo info : getServices()) {
Christoph Studercef37cf2014-07-25 14:18:17 +02005272 if (!isVisibleToListener(sbn, info)) {
Christoph Studer05ad4822014-05-16 14:16:03 +02005273 continue;
Chris Wrenf9536642014-04-17 10:01:54 -04005274 }
Chris Wren333a61c2014-05-28 16:40:57 -04005275 final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
Christoph Studer05ad4822014-05-16 14:16:03 +02005276 mHandler.post(new Runnable() {
5277 @Override
5278 public void run() {
Julia Reynolds3aa5f1e2016-11-09 15:43:49 -05005279 notifyRemoved(info, sbnLight, update, reason);
Christoph Studer05ad4822014-05-16 14:16:03 +02005280 }
5281 });
Chris Wrenf9536642014-04-17 10:01:54 -04005282 }
5283 }
5284
5285 /**
5286 * asynchronously notify all listeners about a reordering of notifications
Chris Wrenf9536642014-04-17 10:01:54 -04005287 */
Julia Reynolds88860ce2017-06-01 16:55:49 -04005288 @GuardedBy("mNotificationLock")
Chris Wren333a61c2014-05-28 16:40:57 -04005289 public void notifyRankingUpdateLocked() {
Julia Reynolds00314d92017-04-14 10:01:24 -04005290 for (final ManagedServiceInfo serviceInfo : getServices()) {
Christoph Studer05ad4822014-05-16 14:16:03 +02005291 if (!serviceInfo.isEnabledForCurrentProfiles()) {
5292 continue;
5293 }
Christoph Studercef37cf2014-07-25 14:18:17 +02005294 final NotificationRankingUpdate update = makeRankingUpdateLocked(serviceInfo);
John Spurlock7340fc82014-04-24 18:50:12 -04005295 mHandler.post(new Runnable() {
5296 @Override
5297 public void run() {
Chris Wren333a61c2014-05-28 16:40:57 -04005298 notifyRankingUpdate(serviceInfo, update);
John Spurlock7340fc82014-04-24 18:50:12 -04005299 }
5300 });
Kenny Guya263e4e2014-03-03 18:24:03 +00005301 }
Kenny Guya263e4e2014-03-03 18:24:03 +00005302 }
Kenny Guya263e4e2014-03-03 18:24:03 +00005303
Julia Reynolds88860ce2017-06-01 16:55:49 -04005304 @GuardedBy("mNotificationLock")
John Spurlockd8afe3c2014-08-01 14:04:07 -04005305 public void notifyListenerHintsChangedLocked(final int hints) {
Julia Reynolds00314d92017-04-14 10:01:24 -04005306 for (final ManagedServiceInfo serviceInfo : getServices()) {
John Spurlock1fa865f2014-07-21 14:56:39 -04005307 if (!serviceInfo.isEnabledForCurrentProfiles()) {
5308 continue;
5309 }
5310 mHandler.post(new Runnable() {
5311 @Override
5312 public void run() {
John Spurlockd8afe3c2014-08-01 14:04:07 -04005313 notifyListenerHintsChanged(serviceInfo, hints);
John Spurlock1fa865f2014-07-21 14:56:39 -04005314 }
5315 });
5316 }
5317 }
5318
Christoph Studer85a384b2014-08-27 20:16:15 +02005319 public void notifyInterruptionFilterChanged(final int interruptionFilter) {
Julia Reynolds00314d92017-04-14 10:01:24 -04005320 for (final ManagedServiceInfo serviceInfo : getServices()) {
Christoph Studer85a384b2014-08-27 20:16:15 +02005321 if (!serviceInfo.isEnabledForCurrentProfiles()) {
5322 continue;
5323 }
5324 mHandler.post(new Runnable() {
5325 @Override
5326 public void run() {
5327 notifyInterruptionFilterChanged(serviceInfo, interruptionFilter);
5328 }
5329 });
5330 }
5331 }
5332
Julia Reynoldsf27d6b22017-04-13 15:48:16 -04005333 protected void notifyNotificationChannelChanged(final String pkg, final UserHandle user,
Julia Reynolds73ed76b2017-04-04 17:04:38 -04005334 final NotificationChannel channel, final int modificationType) {
5335 if (channel == null) {
5336 return;
5337 }
5338 for (final ManagedServiceInfo serviceInfo : getServices()) {
Julia Reynoldsda781472017-04-12 09:41:16 -04005339 if (!serviceInfo.enabledAndUserMatches(UserHandle.getCallingUserId())) {
Julia Reynolds73ed76b2017-04-04 17:04:38 -04005340 continue;
5341 }
Julia Reynolds018aa622017-04-20 11:31:30 -04005342
Julia Reynoldsda781472017-04-12 09:41:16 -04005343 mHandler.post(new Runnable() {
5344 @Override
5345 public void run() {
Julia Reynolds018aa622017-04-20 11:31:30 -04005346 if (hasCompanionDevice(serviceInfo)) {
5347 notifyNotificationChannelChanged(
5348 serviceInfo, pkg, user, channel, modificationType);
5349 }
Julia Reynoldsda781472017-04-12 09:41:16 -04005350 }
5351 });
Julia Reynolds73ed76b2017-04-04 17:04:38 -04005352 }
5353 }
5354
Julia Reynoldsf27d6b22017-04-13 15:48:16 -04005355 protected void notifyNotificationChannelGroupChanged(
5356 final String pkg, final UserHandle user, final NotificationChannelGroup group,
5357 final int modificationType) {
Julia Reynolds73ed76b2017-04-04 17:04:38 -04005358 if (group == null) {
5359 return;
5360 }
5361 for (final ManagedServiceInfo serviceInfo : getServices()) {
Julia Reynoldsda781472017-04-12 09:41:16 -04005362 if (!serviceInfo.enabledAndUserMatches(UserHandle.getCallingUserId())) {
Julia Reynolds73ed76b2017-04-04 17:04:38 -04005363 continue;
5364 }
Julia Reynolds018aa622017-04-20 11:31:30 -04005365
Julia Reynoldsda781472017-04-12 09:41:16 -04005366 mHandler.post(new Runnable() {
5367 @Override
5368 public void run() {
Julia Reynolds018aa622017-04-20 11:31:30 -04005369 if (hasCompanionDevice(serviceInfo)) {
5370 notifyNotificationChannelGroupChanged(
5371 serviceInfo, pkg, user, group, modificationType);
5372 }
Julia Reynoldsda781472017-04-12 09:41:16 -04005373 }
5374 });
Julia Reynolds73ed76b2017-04-04 17:04:38 -04005375 }
5376 }
5377
Christoph Studercef37cf2014-07-25 14:18:17 +02005378 private void notifyPosted(final ManagedServiceInfo info,
Christoph Studer05ad4822014-05-16 14:16:03 +02005379 final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) {
Julia Reynoldsa75c7522017-03-21 17:34:25 -04005380 final INotificationListener listener = (INotificationListener) info.service;
Griff Hazen84a00ea2014-09-02 17:10:47 -07005381 StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
John Spurlock7340fc82014-04-24 18:50:12 -04005382 try {
Griff Hazen84a00ea2014-09-02 17:10:47 -07005383 listener.onNotificationPosted(sbnHolder, rankingUpdate);
John Spurlock7340fc82014-04-24 18:50:12 -04005384 } catch (RemoteException ex) {
5385 Log.e(TAG, "unable to notify listener (posted): " + listener, ex);
5386 }
5387 }
5388
Christoph Studercef37cf2014-07-25 14:18:17 +02005389 private void notifyRemoved(ManagedServiceInfo info, StatusBarNotification sbn,
Julia Reynolds3aa5f1e2016-11-09 15:43:49 -05005390 NotificationRankingUpdate rankingUpdate, int reason) {
John Spurlock7340fc82014-04-24 18:50:12 -04005391 if (!info.enabledAndUserMatches(sbn.getUserId())) {
5392 return;
5393 }
Christoph Studer05ad4822014-05-16 14:16:03 +02005394 final INotificationListener listener = (INotificationListener) info.service;
Griff Hazen84a00ea2014-09-02 17:10:47 -07005395 StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
John Spurlock7340fc82014-04-24 18:50:12 -04005396 try {
Julia Reynolds3aa5f1e2016-11-09 15:43:49 -05005397 listener.onNotificationRemoved(sbnHolder, rankingUpdate, reason);
John Spurlock7340fc82014-04-24 18:50:12 -04005398 } catch (RemoteException ex) {
5399 Log.e(TAG, "unable to notify listener (removed): " + listener, ex);
John Spurlockb408e8e2014-04-23 21:12:45 -04005400 }
Kenny Guya263e4e2014-03-03 18:24:03 +00005401 }
Chris Wrenf9536642014-04-17 10:01:54 -04005402
Christoph Studer05ad4822014-05-16 14:16:03 +02005403 private void notifyRankingUpdate(ManagedServiceInfo info,
5404 NotificationRankingUpdate rankingUpdate) {
5405 final INotificationListener listener = (INotificationListener) info.service;
Chris Wrenf9536642014-04-17 10:01:54 -04005406 try {
Christoph Studer05ad4822014-05-16 14:16:03 +02005407 listener.onNotificationRankingUpdate(rankingUpdate);
Chris Wrenf9536642014-04-17 10:01:54 -04005408 } catch (RemoteException ex) {
5409 Log.e(TAG, "unable to notify listener (ranking update): " + listener, ex);
5410 }
5411 }
John Spurlock1fa865f2014-07-21 14:56:39 -04005412
John Spurlockd8afe3c2014-08-01 14:04:07 -04005413 private void notifyListenerHintsChanged(ManagedServiceInfo info, int hints) {
John Spurlock1fa865f2014-07-21 14:56:39 -04005414 final INotificationListener listener = (INotificationListener) info.service;
5415 try {
John Spurlockd8afe3c2014-08-01 14:04:07 -04005416 listener.onListenerHintsChanged(hints);
John Spurlock1fa865f2014-07-21 14:56:39 -04005417 } catch (RemoteException ex) {
John Spurlockd8afe3c2014-08-01 14:04:07 -04005418 Log.e(TAG, "unable to notify listener (listener hints): " + listener, ex);
John Spurlock1fa865f2014-07-21 14:56:39 -04005419 }
5420 }
Justin Koh38156c52014-06-04 13:57:49 -07005421
Christoph Studer85a384b2014-08-27 20:16:15 +02005422 private void notifyInterruptionFilterChanged(ManagedServiceInfo info,
5423 int interruptionFilter) {
5424 final INotificationListener listener = (INotificationListener) info.service;
5425 try {
5426 listener.onInterruptionFilterChanged(interruptionFilter);
5427 } catch (RemoteException ex) {
5428 Log.e(TAG, "unable to notify listener (interruption filter): " + listener, ex);
5429 }
5430 }
5431
Julia Reynolds73ed76b2017-04-04 17:04:38 -04005432 void notifyNotificationChannelChanged(ManagedServiceInfo info,
Julia Reynoldsf27d6b22017-04-13 15:48:16 -04005433 final String pkg, final UserHandle user, final NotificationChannel channel,
Julia Reynolds73ed76b2017-04-04 17:04:38 -04005434 final int modificationType) {
5435 final INotificationListener listener = (INotificationListener) info.service;
5436 try {
Julia Reynoldsf27d6b22017-04-13 15:48:16 -04005437 listener.onNotificationChannelModification(pkg, user, channel, modificationType);
Julia Reynolds73ed76b2017-04-04 17:04:38 -04005438 } catch (RemoteException ex) {
5439 Log.e(TAG, "unable to notify listener (channel changed): " + listener, ex);
5440 }
5441 }
5442
5443 private void notifyNotificationChannelGroupChanged(ManagedServiceInfo info,
Julia Reynoldsf27d6b22017-04-13 15:48:16 -04005444 final String pkg, final UserHandle user, final NotificationChannelGroup group,
Julia Reynolds73ed76b2017-04-04 17:04:38 -04005445 final int modificationType) {
5446 final INotificationListener listener = (INotificationListener) info.service;
5447 try {
Julia Reynoldsf27d6b22017-04-13 15:48:16 -04005448 listener.onNotificationChannelGroupModification(pkg, user, group, modificationType);
Julia Reynolds73ed76b2017-04-04 17:04:38 -04005449 } catch (RemoteException ex) {
5450 Log.e(TAG, "unable to notify listener (channel group changed): " + listener, ex);
5451 }
5452 }
5453
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05005454 public boolean isListenerPackage(String packageName) {
Justin Koh38156c52014-06-04 13:57:49 -07005455 if (packageName == null) {
5456 return false;
5457 }
5458 // TODO: clean up locking object later
Geoffrey Pitsch331a64d2017-01-17 14:00:47 -05005459 synchronized (mNotificationLock) {
Julia Reynolds00314d92017-04-14 10:01:24 -04005460 for (final ManagedServiceInfo serviceInfo : getServices()) {
Justin Koh38156c52014-06-04 13:57:49 -07005461 if (packageName.equals(serviceInfo.component.getPackageName())) {
5462 return true;
5463 }
5464 }
5465 }
5466 return false;
5467 }
Kenny Guya263e4e2014-03-03 18:24:03 +00005468 }
John Spurlock25e2d242014-06-27 13:58:23 -04005469
5470 public static final class DumpFilter {
Dan Sandlera1770312015-07-10 13:59:29 -04005471 public boolean filtered = false;
John Spurlock25e2d242014-06-27 13:58:23 -04005472 public String pkgFilter;
John Spurlock50806fc2014-07-15 10:22:02 -04005473 public boolean zen;
Chris Wrene4b38802015-07-07 15:54:19 -04005474 public long since;
5475 public boolean stats;
Dan Sandlera1770312015-07-10 13:59:29 -04005476 public boolean redact = true;
Julia Reynoldsc9842c12017-02-07 12:46:41 -05005477 public boolean proto = false;
John Spurlock25e2d242014-06-27 13:58:23 -04005478
5479 public static DumpFilter parseFromArguments(String[] args) {
Dan Sandlera1770312015-07-10 13:59:29 -04005480 final DumpFilter filter = new DumpFilter();
5481 for (int ai = 0; ai < args.length; ai++) {
5482 final String a = args[ai];
Julia Reynoldsc9842c12017-02-07 12:46:41 -05005483 if ("--proto".equals(args[0])) {
5484 filter.proto = true;
5485 }
Dan Sandlera1770312015-07-10 13:59:29 -04005486 if ("--noredact".equals(a) || "--reveal".equals(a)) {
5487 filter.redact = false;
5488 } else if ("p".equals(a) || "pkg".equals(a) || "--package".equals(a)) {
5489 if (ai < args.length-1) {
5490 ai++;
5491 filter.pkgFilter = args[ai].trim().toLowerCase();
5492 if (filter.pkgFilter.isEmpty()) {
5493 filter.pkgFilter = null;
5494 } else {
5495 filter.filtered = true;
5496 }
5497 }
5498 } else if ("--zen".equals(a) || "zen".equals(a)) {
5499 filter.filtered = true;
5500 filter.zen = true;
5501 } else if ("--stats".equals(a)) {
5502 filter.stats = true;
5503 if (ai < args.length-1) {
5504 ai++;
Tobias Thierer28532d02016-04-21 14:52:10 +01005505 filter.since = Long.parseLong(args[ai]);
Dan Sandlera1770312015-07-10 13:59:29 -04005506 } else {
5507 filter.since = 0;
5508 }
5509 }
John Spurlock25e2d242014-06-27 13:58:23 -04005510 }
Dan Sandlera1770312015-07-10 13:59:29 -04005511 return filter;
John Spurlock25e2d242014-06-27 13:58:23 -04005512 }
5513
5514 public boolean matches(StatusBarNotification sbn) {
Dan Sandlera1770312015-07-10 13:59:29 -04005515 if (!filtered) return true;
5516 return zen ? true : sbn != null
John Spurlock50806fc2014-07-15 10:22:02 -04005517 && (matches(sbn.getPackageName()) || matches(sbn.getOpPkg()));
John Spurlock25e2d242014-06-27 13:58:23 -04005518 }
5519
5520 public boolean matches(ComponentName component) {
Dan Sandlera1770312015-07-10 13:59:29 -04005521 if (!filtered) return true;
5522 return zen ? true : component != null && matches(component.getPackageName());
John Spurlock25e2d242014-06-27 13:58:23 -04005523 }
5524
5525 public boolean matches(String pkg) {
Dan Sandlera1770312015-07-10 13:59:29 -04005526 if (!filtered) return true;
5527 return zen ? true : pkg != null && pkg.toLowerCase().contains(pkgFilter);
John Spurlock50806fc2014-07-15 10:22:02 -04005528 }
5529
5530 @Override
5531 public String toString() {
Chris Wrene4b38802015-07-07 15:54:19 -04005532 return stats ? "stats" : zen ? "zen" : ('\'' + pkgFilter + '\'');
John Spurlock25e2d242014-06-27 13:58:23 -04005533 }
5534 }
Griff Hazen84a00ea2014-09-02 17:10:47 -07005535
5536 /**
5537 * Wrapper for a StatusBarNotification object that allows transfer across a oneway
5538 * binder without sending large amounts of data over a oneway transaction.
5539 */
5540 private static final class StatusBarNotificationHolder
5541 extends IStatusBarNotificationHolder.Stub {
Griff Hazene9aac5f2014-09-05 20:04:09 -07005542 private StatusBarNotification mValue;
Griff Hazen84a00ea2014-09-02 17:10:47 -07005543
5544 public StatusBarNotificationHolder(StatusBarNotification value) {
5545 mValue = value;
5546 }
5547
Griff Hazene9aac5f2014-09-05 20:04:09 -07005548 /** Get the held value and clear it. This function should only be called once per holder */
Griff Hazen84a00ea2014-09-02 17:10:47 -07005549 @Override
5550 public StatusBarNotification get() {
Griff Hazene9aac5f2014-09-05 20:04:09 -07005551 StatusBarNotification value = mValue;
5552 mValue = null;
5553 return value;
Griff Hazen84a00ea2014-09-02 17:10:47 -07005554 }
5555 }
John Spurlock7c74f782015-06-04 13:01:42 -04005556
5557 private final class PolicyAccess {
5558 private static final String SEPARATOR = ":";
5559 private final String[] PERM = {
5560 android.Manifest.permission.ACCESS_NOTIFICATION_POLICY
5561 };
5562
5563 public boolean isPackageGranted(String pkg) {
5564 return pkg != null && getGrantedPackages().contains(pkg);
5565 }
5566
5567 public void put(String pkg, boolean granted) {
5568 if (pkg == null) return;
5569 final ArraySet<String> pkgs = getGrantedPackages();
5570 boolean changed;
5571 if (granted) {
5572 changed = pkgs.add(pkg);
5573 } else {
5574 changed = pkgs.remove(pkg);
5575 }
5576 if (!changed) return;
5577 final String setting = TextUtils.join(SEPARATOR, pkgs);
5578 final int currentUser = ActivityManager.getCurrentUser();
5579 Settings.Secure.putStringForUser(getContext().getContentResolver(),
5580 Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES,
5581 setting,
5582 currentUser);
5583 getContext().sendBroadcastAsUser(new Intent(NotificationManager
5584 .ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED)
5585 .setPackage(pkg)
5586 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), new UserHandle(currentUser), null);
5587 }
5588
5589 public ArraySet<String> getGrantedPackages() {
5590 final ArraySet<String> pkgs = new ArraySet<>();
Julia Reynoldsea6c4482015-08-13 09:01:33 -04005591
5592 long identity = Binder.clearCallingIdentity();
5593 try {
5594 final String setting = Settings.Secure.getStringForUser(
5595 getContext().getContentResolver(),
5596 Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES,
5597 ActivityManager.getCurrentUser());
5598 if (setting != null) {
5599 final String[] tokens = setting.split(SEPARATOR);
5600 for (int i = 0; i < tokens.length; i++) {
5601 String token = tokens[i];
5602 if (token != null) {
Andreas Gampe1ed71f32015-12-11 15:49:07 -08005603 token = token.trim();
Julia Reynoldsea6c4482015-08-13 09:01:33 -04005604 }
5605 if (TextUtils.isEmpty(token)) {
5606 continue;
5607 }
5608 pkgs.add(token);
John Spurlock7c74f782015-06-04 13:01:42 -04005609 }
John Spurlock7c74f782015-06-04 13:01:42 -04005610 }
Julia Reynoldsea6c4482015-08-13 09:01:33 -04005611 } finally {
5612 Binder.restoreCallingIdentity(identity);
John Spurlock7c74f782015-06-04 13:01:42 -04005613 }
5614 return pkgs;
5615 }
5616
5617 public String[] getRequestingPackages() throws RemoteException {
Geoffrey Pitsche75a66e2016-11-22 11:12:11 -05005618 final ParceledListSlice list = mPackageManager
John Spurlock7c74f782015-06-04 13:01:42 -04005619 .getPackagesHoldingPermissions(PERM, 0 /*flags*/,
5620 ActivityManager.getCurrentUser());
5621 final List<PackageInfo> pkgs = list.getList();
5622 if (pkgs == null || pkgs.isEmpty()) return new String[0];
5623 final int N = pkgs.size();
5624 final String[] rt = new String[N];
5625 for (int i = 0; i < N; i++) {
5626 rt[i] = pkgs.get(i).packageName;
5627 }
5628 return rt;
5629 }
5630 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005631}