blob: 0c7c8aa8a9b56aeeeda0e7efd76dc54420e5a45c [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Adam Lesinski182f73f2013-12-05 16:48:06 -080017package com.android.server.notification;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080018
Chris Wrene0ba7eb2016-03-04 17:30:43 -050019import static android.service.notification.NotificationRankerService.REASON_APP_CANCEL;
20import static android.service.notification.NotificationRankerService.REASON_APP_CANCEL_ALL;
Julia Reynoldsb5e44b72016-08-16 15:00:25 -040021import static android.service.notification.NotificationRankerService.REASON_CHANNEL_BANNED;
Chris Wrene0ba7eb2016-03-04 17:30:43 -050022import static android.service.notification.NotificationRankerService.REASON_DELEGATE_CANCEL;
23import static android.service.notification.NotificationRankerService.REASON_DELEGATE_CANCEL_ALL;
24import static android.service.notification.NotificationRankerService.REASON_DELEGATE_CLICK;
25import static android.service.notification.NotificationRankerService.REASON_DELEGATE_ERROR;
Chris Wrene0ba7eb2016-03-04 17:30:43 -050026import static android.service.notification.NotificationRankerService.REASON_GROUP_SUMMARY_CANCELED;
27import static android.service.notification.NotificationRankerService.REASON_LISTENER_CANCEL;
28import static android.service.notification.NotificationRankerService.REASON_LISTENER_CANCEL_ALL;
29import static android.service.notification.NotificationRankerService.REASON_PACKAGE_BANNED;
30import static android.service.notification.NotificationRankerService.REASON_PACKAGE_CHANGED;
31import static android.service.notification.NotificationRankerService.REASON_PACKAGE_SUSPENDED;
32import static android.service.notification.NotificationRankerService.REASON_PROFILE_TURNED_OFF;
Julia Reynoldse46bb372016-03-17 11:05:58 -040033import static android.service.notification.NotificationRankerService.REASON_UNAUTOBUNDLED;
Chris Wrene0ba7eb2016-03-04 17:30:43 -050034import static android.service.notification.NotificationRankerService.REASON_USER_STOPPED;
Jason Monk63506742015-12-16 12:06:51 -050035import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_EFFECTS;
Bryce Lee7219ada2016-04-08 10:54:23 -070036import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_NOTIFICATION_EFFECTS;
37import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS;
Julia Reynoldsd5607292016-02-05 15:25:58 -050038import static android.service.notification.NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_OFF;
Julia Reynolds61721582016-01-05 08:35:25 -050039import static android.service.notification.NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_ON;
Christoph Studerb82bc782014-08-20 14:29:43 +020040import static android.service.notification.NotificationListenerService.TRIM_FULL;
41import static android.service.notification.NotificationListenerService.TRIM_LIGHT;
Julia Reynoldsf0f629f2016-02-25 09:34:04 -050042import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_DEFAULT;
Chris Wren4a4b49d2016-02-09 11:25:08 -050043import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_NONE;
Felipe Lemea1b79bf2016-05-24 13:06:54 -070044
Jeff Sharkey098d5802012-04-26 17:30:34 -070045import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
svetoslavganov75986cf2009-05-14 22:28:01 -070046
Chris Wren51017d02015-12-15 15:34:46 -050047import android.Manifest;
Wei Liu97e56662016-03-04 10:52:33 -080048import android.annotation.Nullable;
Dianne Hackborn41203752012-08-31 14:05:51 -070049import android.app.ActivityManager;
Felipe Lemea1b79bf2016-05-24 13:06:54 -070050import android.app.ActivityManagerInternal;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080051import android.app.ActivityManagerNative;
John Spurlock7340fc82014-04-24 18:50:12 -040052import android.app.AppGlobals;
Daniel Sandler4a900ac2013-01-30 14:04:10 -050053import android.app.AppOpsManager;
Julia Reynoldsa47a27f2015-08-24 08:31:47 -040054import android.app.AutomaticZenRule;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080055import android.app.IActivityManager;
56import android.app.INotificationManager;
57import android.app.ITransientNotification;
58import android.app.Notification;
Julia Reynoldsb5e44b72016-08-16 15:00:25 -040059import android.app.NotificationChannel;
John Spurlockb4782522014-08-22 14:54:46 -040060import android.app.NotificationManager;
John Spurlock1fc476d2015-04-14 16:05:20 -040061import android.app.NotificationManager.Policy;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080062import android.app.PendingIntent;
63import android.app.StatusBarManager;
John Spurlock35ef0a62015-05-28 11:24:10 -040064import android.app.backup.BackupManager;
Amith Yamasanif47e51e2015-04-17 10:02:15 -070065import android.app.usage.UsageEvents;
Amith Yamasanif47e51e2015-04-17 10:02:15 -070066import android.app.usage.UsageStatsManagerInternal;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080067import android.content.BroadcastReceiver;
Daniel Sandler5feceeb2013-03-22 18:29:23 -070068import android.content.ComponentName;
Dianne Hackborn1dac2772009-06-26 18:16:48 -070069import android.content.ContentResolver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080070import android.content.Context;
71import android.content.Intent;
72import android.content.IntentFilter;
John Spurlock7340fc82014-04-24 18:50:12 -040073import android.content.pm.ApplicationInfo;
Kenny Guy70058402014-10-28 20:45:06 +000074import android.content.pm.IPackageManager;
Daniel Sandler4a900ac2013-01-30 14:04:10 -050075import android.content.pm.PackageInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080076import android.content.pm.PackageManager;
77import android.content.pm.PackageManager.NameNotFoundException;
Christoph Studercee44ba2014-05-20 18:36:43 +020078import android.content.pm.ParceledListSlice;
Chris Wren66189fc2015-06-25 14:04:33 -040079import android.content.pm.UserInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080080import android.content.res.Resources;
Dianne Hackborn1dac2772009-06-26 18:16:48 -070081import android.database.ContentObserver;
John Spurlock7b414672014-07-18 13:02:39 -040082import android.media.AudioAttributes;
svetoslavganov75986cf2009-05-14 22:28:01 -070083import android.media.AudioManager;
John Spurlockcdb57ae2015-02-11 19:04:11 -050084import android.media.AudioManagerInternal;
Jean-Michel Triviceb79bc2014-09-05 11:09:14 -070085import android.media.AudioSystem;
Jeff Sharkey098d5802012-04-26 17:30:34 -070086import android.media.IRingtonePlayer;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080087import android.net.Uri;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080088import android.os.Binder;
John Spurlock2b122f42014-08-27 16:29:47 -040089import android.os.Bundle;
John Spurlock056c5192014-04-20 21:52:01 -040090import android.os.Environment;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080091import android.os.Handler;
Chris Wrenf9536642014-04-17 10:01:54 -040092import android.os.HandlerThread;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080093import android.os.IBinder;
John Spurlock7340fc82014-04-24 18:50:12 -040094import android.os.IInterface;
Chris Wrenf9536642014-04-17 10:01:54 -040095import android.os.Looper;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080096import android.os.Message;
Dianne Hackbornd8a43f62009-08-17 23:33:56 -070097import android.os.Process;
svetoslavganov75986cf2009-05-14 22:28:01 -070098import android.os.RemoteException;
Chris Wrenc8673a82016-05-17 17:11:29 -040099import android.os.SystemClock;
Selim Cinekb5605e52015-02-20 18:21:41 +0100100import android.os.SystemProperties;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700101import android.os.UserHandle;
Chris Wren66189fc2015-06-25 14:04:33 -0400102import android.os.UserManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800103import android.os.Vibrator;
104import android.provider.Settings;
Julia Reynoldse46bb372016-03-17 11:05:58 -0400105import android.service.notification.Adjustment;
Chris Wren333a61c2014-05-28 16:40:57 -0400106import android.service.notification.Condition;
John Spurlock7340fc82014-04-24 18:50:12 -0400107import android.service.notification.IConditionProvider;
Chris Wren333a61c2014-05-28 16:40:57 -0400108import android.service.notification.INotificationListener;
Griff Hazen84a00ea2014-09-02 17:10:47 -0700109import android.service.notification.IStatusBarNotificationHolder;
Chris Wrene0ba7eb2016-03-04 17:30:43 -0500110import android.service.notification.NotificationRankerService;
John Spurlock7340fc82014-04-24 18:50:12 -0400111import android.service.notification.NotificationListenerService;
Christoph Studer05ad4822014-05-16 14:16:03 +0200112import android.service.notification.NotificationRankingUpdate;
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700113import android.service.notification.StatusBarNotification;
John Spurlock056c5192014-04-20 21:52:01 -0400114import android.service.notification.ZenModeConfig;
John Spurlock32fe4c62014-10-02 12:16:02 -0400115import android.telephony.PhoneStateListener;
Daniel Sandlere96ffb12010-03-11 13:38:06 -0500116import android.telephony.TelephonyManager;
svetoslavganov75986cf2009-05-14 22:28:01 -0700117import android.text.TextUtils;
John Spurlocka4294292014-03-24 18:02:32 -0400118import android.util.ArrayMap;
John Spurlock1fa865f2014-07-21 14:56:39 -0400119import android.util.ArraySet;
Dianne Hackborn39606a02012-07-31 17:54:35 -0700120import android.util.AtomicFile;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800121import android.util.Log;
Andy Stadler110988c2010-12-03 14:29:16 -0800122import android.util.Slog;
Bryce Lee7219ada2016-04-08 10:54:23 -0700123import android.util.SparseArray;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400124import android.util.Xml;
Svetoslav Ganovaa076532016-08-01 19:16:43 -0700125import android.view.WindowManager;
126import android.view.WindowManagerInternal;
svetoslavganov75986cf2009-05-14 22:28:01 -0700127import android.view.accessibility.AccessibilityEvent;
128import android.view.accessibility.AccessibilityManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800129import android.widget.Toast;
Rubin Xu7eadc1b2016-02-01 16:13:45 +0000130
Scott Greenwald9a05b312013-06-28 00:37:54 -0400131import com.android.internal.R;
Chris Wren93bb8b82016-03-29 14:35:05 -0400132import com.android.internal.annotations.VisibleForTesting;
Chris Wrend1dbc922015-06-19 17:51:16 -0400133import com.android.internal.statusbar.NotificationVisibility;
John Spurlock056c5192014-04-20 21:52:01 -0400134import com.android.internal.util.FastXmlSerializer;
Julia Reynoldsa47a27f2015-08-24 08:31:47 -0400135import com.android.internal.util.Preconditions;
Felipe Lemea1b79bf2016-05-24 13:06:54 -0700136import com.android.server.DeviceIdleController;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800137import com.android.server.EventLogTags;
Amith Yamasanif47e51e2015-04-17 10:02:15 -0700138import com.android.server.LocalServices;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800139import com.android.server.SystemService;
140import com.android.server.lights.Light;
141import com.android.server.lights.LightsManager;
John Spurlock7340fc82014-04-24 18:50:12 -0400142import com.android.server.notification.ManagedServices.ManagedServiceInfo;
Svetoslav Ganovaa076532016-08-01 19:16:43 -0700143import com.android.server.policy.PhoneWindowManager;
John Spurlockb408e8e2014-04-23 21:12:45 -0400144import com.android.server.statusbar.StatusBarManagerInternal;
Ruben Brunke24b9a62016-02-16 21:38:24 -0800145import com.android.server.notification.ManagedServices.UserProfiles;
Ruben Brunkdd18a0b2015-12-04 16:16:31 -0800146
John Spurlockb408e8e2014-04-23 21:12:45 -0400147import libcore.io.IoUtils;
Rubin Xu7eadc1b2016-02-01 16:13:45 +0000148
Chris Wrene4b38802015-07-07 15:54:19 -0400149import org.json.JSONException;
150import org.json.JSONObject;
Jeff Sharkey098d5802012-04-26 17:30:34 -0700151import org.xmlpull.v1.XmlPullParser;
152import org.xmlpull.v1.XmlPullParserException;
John Spurlock056c5192014-04-20 21:52:01 -0400153import org.xmlpull.v1.XmlSerializer;
Jeff Sharkey098d5802012-04-26 17:30:34 -0700154
John Spurlock35ef0a62015-05-28 11:24:10 -0400155import java.io.ByteArrayInputStream;
156import java.io.ByteArrayOutputStream;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400157import java.io.File;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800158import java.io.FileDescriptor;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400159import java.io.FileInputStream;
160import java.io.FileNotFoundException;
John Spurlock056c5192014-04-20 21:52:01 -0400161import java.io.FileOutputStream;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400162import java.io.IOException;
John Spurlock35ef0a62015-05-28 11:24:10 -0400163import java.io.InputStream;
164import java.io.OutputStream;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800165import java.io.PrintWriter;
Wojciech Staszkiewicz9e9e2e72015-05-08 14:58:46 +0100166import java.nio.charset.StandardCharsets;
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500167import java.util.ArrayDeque;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800168import java.util.ArrayList;
Julia Reynolds1c9bd422016-03-15 09:25:56 -0400169import java.util.Arrays;
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500170import java.util.Iterator;
John Spurlock7c74f782015-06-04 13:01:42 -0400171import java.util.List;
Chris Wrenacf424a2016-03-15 12:48:55 -0400172import java.util.Map;
Christoph Studer265c1052014-07-23 17:14:33 +0200173import java.util.Map.Entry;
Chris Wren0efdb882016-03-01 17:17:47 -0500174import java.util.Set;
Chris Wren51017d02015-12-15 15:34:46 -0500175import java.util.concurrent.TimeUnit;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400176
Daniel Sandlerd0a2f862010-08-03 15:29:31 -0400177/** {@hide} */
Adam Lesinski182f73f2013-12-05 16:48:06 -0800178public class NotificationManagerService extends SystemService {
179 static final String TAG = "NotificationService";
Christoph Studer1f32c652014-11-26 15:32:20 +0100180 static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
Selim Cinek40412492015-12-08 18:03:22 -0800181 public static final boolean ENABLE_CHILD_NOTIFICATIONS
182 = SystemProperties.getBoolean("debug.child_notifs", true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800183
Adam Lesinski182f73f2013-12-05 16:48:06 -0800184 static final int MAX_PACKAGE_NOTIFICATIONS = 50;
Chris Wrena61f1792016-08-04 11:24:42 -0400185 static final float DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE = 10f;
Joe Onoratobd73d012010-06-04 11:44:54 -0700186
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800187 // message codes
Adam Lesinski182f73f2013-12-05 16:48:06 -0800188 static final int MESSAGE_TIMEOUT = 2;
John Spurlock056c5192014-04-20 21:52:01 -0400189 static final int MESSAGE_SAVE_POLICY_FILE = 3;
Chris Wren51017d02015-12-15 15:34:46 -0500190 static final int MESSAGE_SEND_RANKING_UPDATE = 4;
191 static final int MESSAGE_LISTENER_HINTS_CHANGED = 5;
192 static final int MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED = 6;
193
194 // ranking thread messages
195 private static final int MESSAGE_RECONSIDER_RANKING = 1000;
196 private static final int MESSAGE_RANKING_SORT = 1001;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800197
Svetoslav Ganovaa076532016-08-01 19:16:43 -0700198 static final int LONG_DELAY = PhoneWindowManager.TOAST_WINDOW_TIMEOUT;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800199 static final int SHORT_DELAY = 2000; // 2 seconds
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800200
Adam Lesinski182f73f2013-12-05 16:48:06 -0800201 static final long[] DEFAULT_VIBRATE_PATTERN = {0, 250, 250, 250};
Christoph Studer265c1052014-07-23 17:14:33 +0200202
Adam Lesinski182f73f2013-12-05 16:48:06 -0800203 static final int VIBRATE_PATTERN_MAXLEN = 8 * 2 + 1; // up to eight bumps
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800204
Adam Lesinski182f73f2013-12-05 16:48:06 -0800205 static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_NOTIFICATION;
Daniel Sandler526fa0e2012-12-04 14:51:50 -0500206
Adam Lesinski182f73f2013-12-05 16:48:06 -0800207 static final boolean ENABLE_BLOCKED_NOTIFICATIONS = true;
208 static final boolean ENABLE_BLOCKED_TOASTS = true;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400209
Christoph Studer12aeda82014-09-23 19:08:56 +0200210 // When #matchesCallFilter is called from the ringer, wait at most
211 // 3s to resolve the contacts. This timeout is required since
212 // ContactsProvider might take a long time to start up.
213 //
214 // Return STARRED_CONTACT when the timeout is hit in order to avoid
215 // missed calls in ZEN mode "Important".
216 static final int MATCHES_CALL_FILTER_CONTACTS_TIMEOUT_MS = 3000;
217 static final float MATCHES_CALL_FILTER_TIMEOUT_AFFINITY =
218 ValidateNotificationPeople.STARRED_CONTACT;
219
Christoph Studer265c1052014-07-23 17:14:33 +0200220 /** notification_enqueue status value for a newly enqueued notification. */
221 private static final int EVENTLOG_ENQUEUE_STATUS_NEW = 0;
222
223 /** notification_enqueue status value for an existing notification. */
224 private static final int EVENTLOG_ENQUEUE_STATUS_UPDATE = 1;
225
226 /** notification_enqueue status value for an ignored notification. */
227 private static final int EVENTLOG_ENQUEUE_STATUS_IGNORED = 2;
Chris Wrenc8673a82016-05-17 17:11:29 -0400228 private static final long MIN_PACKAGE_OVERRATE_LOG_INTERVAL = 5000; // milliseconds
Chris Wren0efdb882016-03-01 17:17:47 -0500229 private String mRankerServicePackageName;
Christoph Studer265c1052014-07-23 17:14:33 +0200230
Adam Lesinski182f73f2013-12-05 16:48:06 -0800231 private IActivityManager mAm;
232 AudioManager mAudioManager;
John Spurlockcdb57ae2015-02-11 19:04:11 -0500233 AudioManagerInternal mAudioManagerInternal;
Wei Liu97e56662016-03-04 10:52:33 -0800234 @Nullable StatusBarManagerInternal mStatusBar;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800235 Vibrator mVibrator;
Svetoslav Ganovaa076532016-08-01 19:16:43 -0700236 private WindowManagerInternal mWindowManagerInternal;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800237
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800238 final IBinder mForegroundToken = new Binder();
Chris Wren93bb8b82016-03-29 14:35:05 -0400239 private Handler mHandler;
Chris Wrenf9536642014-04-17 10:01:54 -0400240 private final HandlerThread mRankingThread = new HandlerThread("ranker",
241 Process.THREAD_PRIORITY_BACKGROUND);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800242
Adam Lesinski182f73f2013-12-05 16:48:06 -0800243 private Light mNotificationLight;
244 Light mAttentionLight;
Mike Lockwood670f9322010-01-20 12:13:36 -0500245 private int mDefaultNotificationColor;
246 private int mDefaultNotificationLedOn;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800247
Mike Lockwood670f9322010-01-20 12:13:36 -0500248 private int mDefaultNotificationLedOff;
Daniel Sandleredbb3802012-11-13 20:49:47 -0800249 private long[] mDefaultVibrationPattern;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800250
Daniel Sandleredbb3802012-11-13 20:49:47 -0800251 private long[] mFallbackVibrationPattern;
Chris Wren5116a822014-06-04 15:59:50 -0400252 private boolean mUseAttentionLight;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800253 boolean mSystemReady;
Daniel Sandleredbb3802012-11-13 20:49:47 -0800254
John Spurlockd8afe3c2014-08-01 14:04:07 -0400255 private boolean mDisableNotificationEffects;
John Spurlock32fe4c62014-10-02 12:16:02 -0400256 private int mCallState;
Chris Wren6054e612014-11-25 17:16:46 -0500257 private String mSoundNotificationKey;
258 private String mVibrateNotificationKey;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800259
Bryce Lee7219ada2016-04-08 10:54:23 -0700260 private final SparseArray<ArraySet<ManagedServiceInfo>> mListenersDisablingEffects =
261 new SparseArray<ArraySet<ManagedServiceInfo>>();
262 private List<ComponentName> mEffectsSuppressors = new ArrayList<ComponentName>();
John Spurlockd8afe3c2014-08-01 14:04:07 -0400263 private int mListenerHints; // right now, all hints are global
John Spurlock83104102015-02-12 23:25:12 -0500264 private int mInterruptionFilter = NotificationListenerService.INTERRUPTION_FILTER_UNKNOWN;
John Spurlock1fa865f2014-07-21 14:56:39 -0400265
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500266 // for enabling and disabling notification pulse behavior
Mike Lockwood63b5ad92011-08-30 09:55:30 -0400267 private boolean mScreenOn = true;
Daniel Sandlere96ffb12010-03-11 13:38:06 -0500268 private boolean mInCall = false;
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500269 private boolean mNotificationPulseEnabled;
270
Daniel Sandler09a247e2013-02-14 10:24:17 -0500271 // used as a mutex for access to all active notifications & listeners
Adam Lesinski182f73f2013-12-05 16:48:06 -0800272 final ArrayList<NotificationRecord> mNotificationList =
Fred Quintana6ecaff12009-09-25 14:23:13 -0700273 new ArrayList<NotificationRecord>();
John Spurlocka4294292014-03-24 18:02:32 -0400274 final ArrayMap<String, NotificationRecord> mNotificationsByKey =
275 new ArrayMap<String, NotificationRecord>();
Julia Reynoldseae43fb2016-05-09 12:42:58 -0400276 final ArrayMap<Integer, ArrayMap<String, String>> mAutobundledSummaries = new ArrayMap<>();
Adam Lesinski182f73f2013-12-05 16:48:06 -0800277 final ArrayList<ToastRecord> mToastQueue = new ArrayList<ToastRecord>();
Christoph Studer265c1052014-07-23 17:14:33 +0200278 final ArrayMap<String, NotificationRecord> mSummaryByGroupKey = new ArrayMap<>();
John Spurlock7c74f782015-06-04 13:01:42 -0400279 final PolicyAccess mPolicyAccess = new PolicyAccess();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800280
Chris Wren6054e612014-11-25 17:16:46 -0500281 // The last key in this list owns the hardware.
282 ArrayList<String> mLights = new ArrayList<>();
svetoslavganov75986cf2009-05-14 22:28:01 -0700283
Adam Lesinski182f73f2013-12-05 16:48:06 -0800284 private AppOpsManager mAppOps;
Amith Yamasanif47e51e2015-04-17 10:02:15 -0700285 private UsageStatsManagerInternal mAppUsageStats;
Daniel Sandler4a900ac2013-01-30 14:04:10 -0500286
Griff Hazen9f637d12014-06-10 11:13:51 -0700287 private Archive mArchive;
288
John Spurlock21258a32015-05-27 18:22:55 -0400289 // Persistent storage for notification policy
Daniel Sandler0da673f2012-04-11 12:33:16 -0400290 private AtomicFile mPolicyFile;
John Spurlock21258a32015-05-27 18:22:55 -0400291
Daniel Sandler0da673f2012-04-11 12:33:16 -0400292 private static final int DB_VERSION = 1;
293
John Spurlock21258a32015-05-27 18:22:55 -0400294 private static final String TAG_NOTIFICATION_POLICY = "notification-policy";
Daniel Sandler0da673f2012-04-11 12:33:16 -0400295 private static final String ATTR_VERSION = "version";
296
Chris Wren54bbef42014-07-09 18:37:56 -0400297 private RankingHelper mRankingHelper;
Scott Greenwald9a05b312013-06-28 00:37:54 -0400298
John Spurlockb408e8e2014-04-23 21:12:45 -0400299 private final UserProfiles mUserProfiles = new UserProfiles();
John Spurlock7340fc82014-04-24 18:50:12 -0400300 private NotificationListeners mListeners;
Chris Wren0efdb882016-03-01 17:17:47 -0500301 private NotificationRankers mRankerServices;
John Spurlock7340fc82014-04-24 18:50:12 -0400302 private ConditionProviders mConditionProviders;
Christoph Studer1c3f81f2014-04-16 15:05:56 +0200303 private NotificationUsageStats mUsageStats;
Christoph Studer546bec82014-03-14 12:17:12 +0100304
John Spurlocke6a7d932014-03-13 12:29:00 -0400305 private static final int MY_UID = Process.myUid();
306 private static final int MY_PID = Process.myPid();
Chris Wren51017d02015-12-15 15:34:46 -0500307 private RankingHandler mRankingHandler;
Chris Wrenc8673a82016-05-17 17:11:29 -0400308 private long mLastOverRateLogTime;
Chris Wren763a9bb2016-05-31 17:14:12 -0400309 private float mMaxPackageEnqueueRate = DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE;
Chris Wrend4054312016-06-24 17:07:40 -0400310 private String mSystemNotificationSound;
John Spurlocke6a7d932014-03-13 12:29:00 -0400311
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500312 private static class Archive {
Griff Hazen9f637d12014-06-10 11:13:51 -0700313 final int mBufferSize;
314 final ArrayDeque<StatusBarNotification> mBuffer;
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500315
Griff Hazen9f637d12014-06-10 11:13:51 -0700316 public Archive(int size) {
317 mBufferSize = size;
318 mBuffer = new ArrayDeque<StatusBarNotification>(mBufferSize);
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500319 }
Jeff Sharkey0c1baf92013-04-03 13:08:52 -0700320
Daniel Sandler5e62e3a2013-04-15 20:57:02 -0400321 public String toString() {
322 final StringBuilder sb = new StringBuilder();
323 final int N = mBuffer.size();
324 sb.append("Archive (");
325 sb.append(N);
326 sb.append(" notification");
327 sb.append((N==1)?")":"s)");
328 return sb.toString();
329 }
330
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500331 public void record(StatusBarNotification nr) {
Griff Hazen9f637d12014-06-10 11:13:51 -0700332 if (mBuffer.size() == mBufferSize) {
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500333 mBuffer.removeFirst();
334 }
Daniel Sandler26b81d52013-05-20 20:56:43 -0400335
336 // We don't want to store the heavy bits of the notification in the archive,
337 // but other clients in the system process might be using the object, so we
338 // store a (lightened) copy.
339 mBuffer.addLast(nr.cloneLight());
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500340 }
341
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500342 public Iterator<StatusBarNotification> descendingIterator() {
343 return mBuffer.descendingIterator();
344 }
Daniel Sandler78d0d252013-02-12 08:14:52 -0500345
346 public StatusBarNotification[] getArray(int count) {
Griff Hazen9f637d12014-06-10 11:13:51 -0700347 if (count == 0) count = mBufferSize;
Daniel Sandler78d0d252013-02-12 08:14:52 -0500348 final StatusBarNotification[] a
349 = new StatusBarNotification[Math.min(count, mBuffer.size())];
350 Iterator<StatusBarNotification> iter = descendingIterator();
351 int i=0;
352 while (iter.hasNext() && i < count) {
353 a[i++] = iter.next();
354 }
355 return a;
356 }
357
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500358 }
359
John Spurlock35ef0a62015-05-28 11:24:10 -0400360 private void readPolicyXml(InputStream stream, boolean forRestore)
361 throws XmlPullParserException, NumberFormatException, IOException {
362 final XmlPullParser parser = Xml.newPullParser();
363 parser.setInput(stream, StandardCharsets.UTF_8.name());
364
Chris Wrenacf424a2016-03-15 12:48:55 -0400365 while (parser.next() != END_DOCUMENT) {
John Spurlock35ef0a62015-05-28 11:24:10 -0400366 mZenModeHelper.readXml(parser, forRestore);
367 mRankingHelper.readXml(parser, forRestore);
368 }
369 }
370
John Spurlock056c5192014-04-20 21:52:01 -0400371 private void loadPolicyFile() {
John Spurlock21258a32015-05-27 18:22:55 -0400372 if (DBG) Slog.d(TAG, "loadPolicyFile");
John Spurlock056c5192014-04-20 21:52:01 -0400373 synchronized(mPolicyFile) {
Daniel Sandler0da673f2012-04-11 12:33:16 -0400374
John Spurlock056c5192014-04-20 21:52:01 -0400375 FileInputStream infile = null;
376 try {
377 infile = mPolicyFile.openRead();
John Spurlock35ef0a62015-05-28 11:24:10 -0400378 readPolicyXml(infile, false /*forRestore*/);
John Spurlock056c5192014-04-20 21:52:01 -0400379 } catch (FileNotFoundException e) {
380 // No data yet
381 } catch (IOException e) {
382 Log.wtf(TAG, "Unable to read notification policy", e);
383 } catch (NumberFormatException e) {
384 Log.wtf(TAG, "Unable to parse notification policy", e);
385 } catch (XmlPullParserException e) {
386 Log.wtf(TAG, "Unable to parse notification policy", e);
387 } finally {
388 IoUtils.closeQuietly(infile);
389 }
390 }
391 }
392
393 public void savePolicyFile() {
394 mHandler.removeMessages(MESSAGE_SAVE_POLICY_FILE);
395 mHandler.sendEmptyMessage(MESSAGE_SAVE_POLICY_FILE);
396 }
397
398 private void handleSavePolicyFile() {
John Spurlock21258a32015-05-27 18:22:55 -0400399 if (DBG) Slog.d(TAG, "handleSavePolicyFile");
John Spurlock056c5192014-04-20 21:52:01 -0400400 synchronized (mPolicyFile) {
401 final FileOutputStream stream;
402 try {
403 stream = mPolicyFile.startWrite();
404 } catch (IOException e) {
405 Slog.w(TAG, "Failed to save policy file", e);
406 return;
407 }
408
409 try {
John Spurlock35ef0a62015-05-28 11:24:10 -0400410 writePolicyXml(stream, false /*forBackup*/);
John Spurlock056c5192014-04-20 21:52:01 -0400411 mPolicyFile.finishWrite(stream);
412 } catch (IOException e) {
413 Slog.w(TAG, "Failed to save policy file, restoring backup", e);
414 mPolicyFile.failWrite(stream);
Daniel Sandler0da673f2012-04-11 12:33:16 -0400415 }
416 }
John Spurlock35ef0a62015-05-28 11:24:10 -0400417 BackupManager.dataChanged(getContext().getPackageName());
418 }
419
420 private void writePolicyXml(OutputStream stream, boolean forBackup) throws IOException {
421 final XmlSerializer out = new FastXmlSerializer();
422 out.setOutput(stream, StandardCharsets.UTF_8.name());
423 out.startDocument(null, true);
424 out.startTag(null, TAG_NOTIFICATION_POLICY);
425 out.attribute(null, ATTR_VERSION, Integer.toString(DB_VERSION));
426 mZenModeHelper.writeXml(out, forBackup);
427 mRankingHelper.writeXml(out, forBackup);
428 out.endTag(null, TAG_NOTIFICATION_POLICY);
429 out.endDocument();
Daniel Sandler0da673f2012-04-11 12:33:16 -0400430 }
431
Daniel Sandler4a900ac2013-01-30 14:04:10 -0500432 /** Use this when you actually want to post a notification or toast.
433 *
434 * Unchecked. Not exposed via Binder, but can be called in the course of enqueue*().
435 */
436 private boolean noteNotificationOp(String pkg, int uid) {
437 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
438 != AppOpsManager.MODE_ALLOWED) {
439 Slog.v(TAG, "notifications are disabled by AppOps for " + pkg);
440 return false;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400441 }
Daniel Sandler4a900ac2013-01-30 14:04:10 -0500442 return true;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400443 }
444
Chris Wren66189fc2015-06-25 14:04:33 -0400445 /** Use this to check if a package can post a notification or toast. */
446 private boolean checkNotificationOp(String pkg, int uid) {
447 return mAppOps.checkOp(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
Andrei Stingaceanu355b2322016-02-12 16:43:51 +0000448 == AppOpsManager.MODE_ALLOWED && !isPackageSuspendedForUser(pkg, uid);
Chris Wren66189fc2015-06-25 14:04:33 -0400449 }
450
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800451 private static final class ToastRecord
452 {
453 final int pid;
454 final String pkg;
455 final ITransientNotification callback;
456 int duration;
Svetoslav Ganovaa076532016-08-01 19:16:43 -0700457 Binder token;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800458
Svetoslav Ganovaa076532016-08-01 19:16:43 -0700459 ToastRecord(int pid, String pkg, ITransientNotification callback, int duration,
460 Binder token) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800461 this.pid = pid;
462 this.pkg = pkg;
463 this.callback = callback;
464 this.duration = duration;
Svetoslav Ganovaa076532016-08-01 19:16:43 -0700465 this.token = token;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800466 }
467
468 void update(int duration) {
469 this.duration = duration;
470 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800471
John Spurlock25e2d242014-06-27 13:58:23 -0400472 void dump(PrintWriter pw, String prefix, DumpFilter filter) {
473 if (filter != null && !filter.matches(pkg)) return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800474 pw.println(prefix + this);
475 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800476
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800477 @Override
478 public final String toString()
479 {
480 return "ToastRecord{"
481 + Integer.toHexString(System.identityHashCode(this))
482 + " pkg=" + pkg
483 + " callback=" + callback
484 + " duration=" + duration;
485 }
486 }
487
Adam Lesinski182f73f2013-12-05 16:48:06 -0800488 private final NotificationDelegate mNotificationDelegate = new NotificationDelegate() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800489
Adam Lesinski182f73f2013-12-05 16:48:06 -0800490 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800491 public void onSetDisabled(int status) {
492 synchronized (mNotificationList) {
John Spurlockd8afe3c2014-08-01 14:04:07 -0400493 mDisableNotificationEffects =
494 (status & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0;
John Spurlock32fe4c62014-10-02 12:16:02 -0400495 if (disableNotificationEffects(null) != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800496 // cancel whatever's going on
497 long identity = Binder.clearCallingIdentity();
498 try {
Adam Lesinski182f73f2013-12-05 16:48:06 -0800499 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
Jeff Sharkey098d5802012-04-26 17:30:34 -0700500 if (player != null) {
501 player.stopAsync();
502 }
503 } catch (RemoteException e) {
504 } finally {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800505 Binder.restoreCallingIdentity(identity);
506 }
507
508 identity = Binder.clearCallingIdentity();
509 try {
510 mVibrator.cancel();
Jeff Sharkey098d5802012-04-26 17:30:34 -0700511 } finally {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800512 Binder.restoreCallingIdentity(identity);
513 }
514 }
515 }
516 }
517
Adam Lesinski182f73f2013-12-05 16:48:06 -0800518 @Override
John Spurlocke6a7d932014-03-13 12:29:00 -0400519 public void onClearAll(int callingUid, int callingPid, int userId) {
Adam Lesinskie8240262014-03-26 16:01:00 -0700520 synchronized (mNotificationList) {
Kenny Guya263e4e2014-03-03 18:24:03 +0000521 cancelAllLocked(callingUid, callingPid, userId, REASON_DELEGATE_CANCEL_ALL, null,
522 /*includeCurrentProfiles*/ true);
Adam Lesinskie8240262014-03-26 16:01:00 -0700523 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800524 }
525
Adam Lesinski182f73f2013-12-05 16:48:06 -0800526 @Override
Christoph Studer03b87a22014-04-30 17:33:27 +0200527 public void onNotificationClick(int callingUid, int callingPid, String key) {
528 synchronized (mNotificationList) {
Christoph Studer03b87a22014-04-30 17:33:27 +0200529 NotificationRecord r = mNotificationsByKey.get(key);
530 if (r == null) {
531 Log.w(TAG, "No notification with key: " + key);
532 return;
533 }
Chris Wrene6ddb8a2015-05-27 15:21:00 -0400534 final long now = System.currentTimeMillis();
535 EventLogTags.writeNotificationClicked(key,
536 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
537
Christoph Studer03b87a22014-04-30 17:33:27 +0200538 StatusBarNotification sbn = r.sbn;
539 cancelNotification(callingUid, callingPid, sbn.getPackageName(), sbn.getTag(),
540 sbn.getId(), Notification.FLAG_AUTO_CANCEL,
541 Notification.FLAG_FOREGROUND_SERVICE, false, r.getUserId(),
542 REASON_DELEGATE_CLICK, null);
543 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800544 }
545
Adam Lesinski182f73f2013-12-05 16:48:06 -0800546 @Override
Christoph Studer4da84cd2014-10-21 17:24:20 +0200547 public void onNotificationActionClick(int callingUid, int callingPid, String key,
548 int actionIndex) {
549 synchronized (mNotificationList) {
Christoph Studer4da84cd2014-10-21 17:24:20 +0200550 NotificationRecord r = mNotificationsByKey.get(key);
551 if (r == null) {
552 Log.w(TAG, "No notification with key: " + key);
553 return;
554 }
Chris Wrene6ddb8a2015-05-27 15:21:00 -0400555 final long now = System.currentTimeMillis();
556 EventLogTags.writeNotificationActionClicked(key, actionIndex,
557 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
Christoph Studer4da84cd2014-10-21 17:24:20 +0200558 // TODO: Log action click via UsageStats.
559 }
560 }
561
562 @Override
John Spurlocke6a7d932014-03-13 12:29:00 -0400563 public void onNotificationClear(int callingUid, int callingPid,
564 String pkg, String tag, int id, int userId) {
565 cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
Kenny Guy3a7c4a52014-03-03 18:24:03 +0000566 Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
John Spurlocke6a7d932014-03-13 12:29:00 -0400567 true, userId, REASON_DELEGATE_CANCEL, null);
Daniel Sandler0f0b11c2010-08-04 15:54:58 -0400568 }
569
Adam Lesinski182f73f2013-12-05 16:48:06 -0800570 @Override
Chris Wrenb659c4f2015-06-25 17:12:27 -0400571 public void onPanelRevealed(boolean clearEffects, int items) {
572 EventLogTags.writeNotificationPanelRevealed(items);
Christoph Studer1f32c652014-11-26 15:32:20 +0100573 if (clearEffects) {
574 clearEffects();
575 }
576 }
577
578 @Override
579 public void onPanelHidden() {
580 EventLogTags.writeNotificationPanelHidden();
581 }
582
583 @Override
584 public void clearEffects() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800585 synchronized (mNotificationList) {
Christoph Studer1f32c652014-11-26 15:32:20 +0100586 if (DBG) Slog.d(TAG, "clearEffects");
Chris Wren93bb8b82016-03-29 14:35:05 -0400587 clearSoundLocked();
588 clearVibrateLocked();
589 clearLightsLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800590 }
591 }
Joe Onorato005847b2010-06-04 16:08:02 -0400592
Adam Lesinski182f73f2013-12-05 16:48:06 -0800593 @Override
John Spurlocke6a7d932014-03-13 12:29:00 -0400594 public void onNotificationError(int callingUid, int callingPid, String pkg, String tag, int id,
Kenny Guy3a7c4a52014-03-03 18:24:03 +0000595 int uid, int initialPid, String message, int userId) {
Daniel Sandlerd0a2f862010-08-03 15:29:31 -0400596 Slog.d(TAG, "onNotification error pkg=" + pkg + " tag=" + tag + " id=" + id
597 + "; will crashApplication(uid=" + uid + ", pid=" + initialPid + ")");
John Spurlocke6a7d932014-03-13 12:29:00 -0400598 cancelNotification(callingUid, callingPid, pkg, tag, id, 0, 0, false, userId,
599 REASON_DELEGATE_ERROR, null);
Dianne Hackborn9d39d0c2010-06-24 15:57:42 -0700600 long ident = Binder.clearCallingIdentity();
601 try {
602 ActivityManagerNative.getDefault().crashApplication(uid, initialPid, pkg,
603 "Bad notification posted from package " + pkg
604 + ": " + message);
605 } catch (RemoteException e) {
606 }
607 Binder.restoreCallingIdentity(ident);
Joe Onorato005847b2010-06-04 16:08:02 -0400608 }
John Spurlocke677d712014-02-13 12:52:19 -0500609
610 @Override
Chris Wrend1dbc922015-06-19 17:51:16 -0400611 public void onNotificationVisibilityChanged(NotificationVisibility[] newlyVisibleKeys,
612 NotificationVisibility[] noLongerVisibleKeys) {
Christoph Studerffeb0c32014-05-07 22:23:56 +0200613 synchronized (mNotificationList) {
Chris Wrend1dbc922015-06-19 17:51:16 -0400614 for (NotificationVisibility nv : newlyVisibleKeys) {
615 NotificationRecord r = mNotificationsByKey.get(nv.key);
Christoph Studerffeb0c32014-05-07 22:23:56 +0200616 if (r == null) continue;
Chris Wrend1dbc922015-06-19 17:51:16 -0400617 r.setVisibility(true, nv.rank);
618 nv.recycle();
Christoph Studerffeb0c32014-05-07 22:23:56 +0200619 }
620 // Note that we might receive this event after notifications
621 // have already left the system, e.g. after dismissing from the
622 // shade. Hence not finding notifications in
623 // mNotificationsByKey is not an exceptional condition.
Chris Wrend1dbc922015-06-19 17:51:16 -0400624 for (NotificationVisibility nv : noLongerVisibleKeys) {
625 NotificationRecord r = mNotificationsByKey.get(nv.key);
Christoph Studerffeb0c32014-05-07 22:23:56 +0200626 if (r == null) continue;
Chris Wrend1dbc922015-06-19 17:51:16 -0400627 r.setVisibility(false, nv.rank);
628 nv.recycle();
Christoph Studerffeb0c32014-05-07 22:23:56 +0200629 }
630 }
Christoph Studer92b389d2014-04-01 18:44:40 +0200631 }
Chris Wren78403d72014-07-28 10:23:24 +0100632
633 @Override
634 public void onNotificationExpansionChanged(String key,
635 boolean userAction, boolean expanded) {
Chris Wren78403d72014-07-28 10:23:24 +0100636 synchronized (mNotificationList) {
637 NotificationRecord r = mNotificationsByKey.get(key);
638 if (r != null) {
639 r.stats.onExpansionChanged(userAction, expanded);
Chris Wrene6ddb8a2015-05-27 15:21:00 -0400640 final long now = System.currentTimeMillis();
641 EventLogTags.writeNotificationExpansion(key,
642 userAction ? 1 : 0, expanded ? 1 : 0,
643 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
Chris Wren78403d72014-07-28 10:23:24 +0100644 }
645 }
646 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800647 };
648
Chris Wren93bb8b82016-03-29 14:35:05 -0400649 private void clearSoundLocked() {
650 mSoundNotificationKey = null;
651 long identity = Binder.clearCallingIdentity();
652 try {
653 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
654 if (player != null) {
655 player.stopAsync();
656 }
657 } catch (RemoteException e) {
658 } finally {
659 Binder.restoreCallingIdentity(identity);
660 }
661 }
662
663 private void clearVibrateLocked() {
664 mVibrateNotificationKey = null;
665 long identity = Binder.clearCallingIdentity();
666 try {
667 mVibrator.cancel();
668 } finally {
669 Binder.restoreCallingIdentity(identity);
670 }
671 }
672
673 private void clearLightsLocked() {
674 // light
675 mLights.clear();
676 updateLightsLocked();
677 }
678
Kenny Guy70058402014-10-28 20:45:06 +0000679 private final BroadcastReceiver mPackageIntentReceiver = new BroadcastReceiver() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800680 @Override
681 public void onReceive(Context context, Intent intent) {
682 String action = intent.getAction();
Dianne Hackborn29cd7f12015-01-08 10:37:05 -0800683 if (action == null) {
684 return;
685 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800686
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800687 boolean queryRestart = false;
Chris Wrenae9bb572013-05-15 14:50:28 -0400688 boolean queryRemove = false;
Daniel Sandler26ece572012-06-01 15:38:46 -0400689 boolean packageChanged = false;
John Spurlock79f78922013-05-16 09:10:05 -0400690 boolean cancelNotifications = true;
Andrei Stingaceanu0122f6512016-01-22 15:33:03 +0000691 int reason = REASON_PACKAGE_CHANGED;
Chris Wrenf9536642014-04-17 10:01:54 -0400692
Chris Wren3da73022013-05-10 14:41:21 -0400693 if (action.equals(Intent.ACTION_PACKAGE_ADDED)
Chris Wrenae9bb572013-05-15 14:50:28 -0400694 || (queryRemove=action.equals(Intent.ACTION_PACKAGE_REMOVED))
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800695 || action.equals(Intent.ACTION_PACKAGE_RESTARTED)
Daniel Sandler26ece572012-06-01 15:38:46 -0400696 || (packageChanged=action.equals(Intent.ACTION_PACKAGE_CHANGED))
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800697 || (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART))
Andrei Stingaceanu0122f6512016-01-22 15:33:03 +0000698 || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)
699 || action.equals(Intent.ACTION_PACKAGES_SUSPENDED)) {
Kenny Guy70058402014-10-28 20:45:06 +0000700 int changeUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
701 UserHandle.USER_ALL);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800702 String pkgList[] = null;
Julia Reynolds6434eb22016-08-08 17:19:26 -0400703 boolean removingPackage = queryRemove &&
704 !intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
705 if (DBG) Slog.i(TAG, "action=" + action + " removing=" + removingPackage);
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800706 if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800707 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
Andrei Stingaceanu0122f6512016-01-22 15:33:03 +0000708 } else if (action.equals(Intent.ACTION_PACKAGES_SUSPENDED)) {
709 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
710 reason = REASON_PACKAGE_SUSPENDED;
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800711 } else if (queryRestart) {
712 pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800713 } else {
714 Uri uri = intent.getData();
715 if (uri == null) {
716 return;
717 }
718 String pkgName = uri.getSchemeSpecificPart();
719 if (pkgName == null) {
720 return;
721 }
Daniel Sandler26ece572012-06-01 15:38:46 -0400722 if (packageChanged) {
723 // We cancel notifications for packages which have just been disabled
Christopher Tate06e5fed2013-10-09 14:39:15 -0700724 try {
Kenny Guy70058402014-10-28 20:45:06 +0000725 final IPackageManager pm = AppGlobals.getPackageManager();
726 final int enabled = pm.getApplicationEnabledSetting(pkgName,
727 changeUserId != UserHandle.USER_ALL ? changeUserId :
Xiaohui Chenddbe4ca2015-08-13 16:20:56 -0700728 UserHandle.USER_SYSTEM);
Christopher Tate06e5fed2013-10-09 14:39:15 -0700729 if (enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED
730 || enabled == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
731 cancelNotifications = false;
732 }
733 } catch (IllegalArgumentException e) {
734 // Package doesn't exist; probably racing with uninstall.
735 // cancelNotifications is already true, so nothing to do here.
736 if (DBG) {
737 Slog.i(TAG, "Exception trying to look up app enabled setting", e);
738 }
Kenny Guy70058402014-10-28 20:45:06 +0000739 } catch (RemoteException e) {
740 // Failed to talk to PackageManagerService Should never happen!
Daniel Sandler26ece572012-06-01 15:38:46 -0400741 }
742 }
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800743 pkgList = new String[]{pkgName};
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800744 }
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700745
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800746 if (pkgList != null && (pkgList.length > 0)) {
747 for (String pkgName : pkgList) {
John Spurlock79f78922013-05-16 09:10:05 -0400748 if (cancelNotifications) {
Julia Reynoldsb5e44b72016-08-16 15:00:25 -0400749 cancelAllNotificationsInt(MY_UID, MY_PID, pkgName, null, 0, 0,
750 !queryRestart, changeUserId, reason, null);
John Spurlock79f78922013-05-16 09:10:05 -0400751 }
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800752 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800753 }
Julia Reynolds6434eb22016-08-08 17:19:26 -0400754 mListeners.onPackagesChanged(removingPackage, pkgList);
755 mRankerServices.onPackagesChanged(removingPackage, pkgList);
756 mConditionProviders.onPackagesChanged(removingPackage, pkgList);
757 mRankingHelper.onPackagesChanged(removingPackage, pkgList);
Kenny Guy70058402014-10-28 20:45:06 +0000758 }
759 }
760 };
761
762 private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
763 @Override
764 public void onReceive(Context context, Intent intent) {
765 String action = intent.getAction();
766
767 if (action.equals(Intent.ACTION_SCREEN_ON)) {
Mike Lockwood63b5ad92011-08-30 09:55:30 -0400768 // Keep track of screen on/off state, but do not turn off the notification light
769 // until user passes through the lock screen or views the notification.
770 mScreenOn = true;
Christoph Studer1f32c652014-11-26 15:32:20 +0100771 updateNotificationPulse();
Mike Lockwood63b5ad92011-08-30 09:55:30 -0400772 } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
773 mScreenOn = false;
Christoph Studer1f32c652014-11-26 15:32:20 +0100774 updateNotificationPulse();
Daniel Sandlere96ffb12010-03-11 13:38:06 -0500775 } else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) {
John Spurlock5d2eeb12014-01-16 10:46:36 -0500776 mInCall = TelephonyManager.EXTRA_STATE_OFFHOOK
777 .equals(intent.getStringExtra(TelephonyManager.EXTRA_STATE));
Daniel Sandlere96ffb12010-03-11 13:38:06 -0500778 updateNotificationPulse();
Dianne Hackborn80a4af22012-08-27 19:18:31 -0700779 } else if (action.equals(Intent.ACTION_USER_STOPPED)) {
780 int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
781 if (userHandle >= 0) {
Julia Reynoldsb5e44b72016-08-16 15:00:25 -0400782 cancelAllNotificationsInt(MY_UID, MY_PID, null, null, 0, 0, true, userHandle,
Julia Reynoldsef37f282016-02-12 09:11:27 -0500783 REASON_USER_STOPPED, null);
Dianne Hackborn80a4af22012-08-27 19:18:31 -0700784 }
Rubin Xue95057a2016-04-01 16:49:25 +0100785 } else if (action.equals(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)) {
Rubin Xu7eadc1b2016-02-01 16:13:45 +0000786 int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
Rubin Xue95057a2016-04-01 16:49:25 +0100787 if (userHandle >= 0) {
Julia Reynoldsb5e44b72016-08-16 15:00:25 -0400788 cancelAllNotificationsInt(MY_UID, MY_PID, null, null, 0, 0, true, userHandle,
Julia Reynoldsef37f282016-02-12 09:11:27 -0500789 REASON_PROFILE_TURNED_OFF, null);
Rubin Xu7eadc1b2016-02-01 16:13:45 +0000790 }
Mike Lockwood63b5ad92011-08-30 09:55:30 -0400791 } else if (action.equals(Intent.ACTION_USER_PRESENT)) {
792 // turn off LED when user passes through lock screen
793 mNotificationLight.turnOff();
Wei Liu97e56662016-03-04 10:52:33 -0800794 if (mStatusBar != null) {
795 mStatusBar.notificationLightOff();
796 }
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400797 } else if (action.equals(Intent.ACTION_USER_SWITCHED)) {
John Spurlock1b8b22b2015-05-20 09:47:13 -0400798 final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400799 // reload per-user settings
800 mSettingsObserver.update(null);
John Spurlockb408e8e2014-04-23 21:12:45 -0400801 mUserProfiles.updateCache(context);
Christoph Studerb53dfd42014-09-12 14:45:59 +0200802 // Refresh managed services
John Spurlock1b8b22b2015-05-20 09:47:13 -0400803 mConditionProviders.onUserSwitched(user);
804 mListeners.onUserSwitched(user);
Chris Wrene0ba7eb2016-03-04 17:30:43 -0500805 mRankerServices.onUserSwitched(user);
John Spurlock21258a32015-05-27 18:22:55 -0400806 mZenModeHelper.onUserSwitched(user);
Kenny Guy3a7c4a52014-03-03 18:24:03 +0000807 } else if (action.equals(Intent.ACTION_USER_ADDED)) {
John Spurlockb408e8e2014-04-23 21:12:45 -0400808 mUserProfiles.updateCache(context);
John Spurlock21258a32015-05-27 18:22:55 -0400809 } else if (action.equals(Intent.ACTION_USER_REMOVED)) {
810 final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
811 mZenModeHelper.onUserRemoved(user);
Julia Reynoldsa3dcaff2016-02-03 15:04:05 -0500812 } else if (action.equals(Intent.ACTION_USER_UNLOCKED)) {
813 final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
814 mConditionProviders.onUserUnlocked(user);
815 mListeners.onUserUnlocked(user);
Chris Wrene0ba7eb2016-03-04 17:30:43 -0500816 mRankerServices.onUserUnlocked(user);
Julia Reynoldsa3dcaff2016-02-03 15:04:05 -0500817 mZenModeHelper.onUserUnlocked(user);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800818 }
819 }
820 };
821
John Spurlock7c74f782015-06-04 13:01:42 -0400822 private final class SettingsObserver extends ContentObserver {
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400823 private final Uri NOTIFICATION_LIGHT_PULSE_URI
824 = Settings.System.getUriFor(Settings.System.NOTIFICATION_LIGHT_PULSE);
Chris Wrend4054312016-06-24 17:07:40 -0400825 private final Uri NOTIFICATION_SOUND_URI
826 = Settings.System.getUriFor(Settings.System.NOTIFICATION_SOUND);
Chris Wren763a9bb2016-05-31 17:14:12 -0400827 private final Uri NOTIFICATION_RATE_LIMIT_URI
828 = Settings.Global.getUriFor(Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE);
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400829
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700830 SettingsObserver(Handler handler) {
831 super(handler);
832 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800833
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700834 void observe() {
Adam Lesinski182f73f2013-12-05 16:48:06 -0800835 ContentResolver resolver = getContext().getContentResolver();
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400836 resolver.registerContentObserver(NOTIFICATION_LIGHT_PULSE_URI,
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700837 false, this, UserHandle.USER_ALL);
Chris Wrend4054312016-06-24 17:07:40 -0400838 resolver.registerContentObserver(NOTIFICATION_SOUND_URI,
839 false, this, UserHandle.USER_ALL);
Chris Wren763a9bb2016-05-31 17:14:12 -0400840 resolver.registerContentObserver(NOTIFICATION_RATE_LIMIT_URI,
841 false, this, UserHandle.USER_ALL);
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400842 update(null);
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700843 }
844
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400845 @Override public void onChange(boolean selfChange, Uri uri) {
846 update(uri);
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700847 }
848
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400849 public void update(Uri uri) {
Adam Lesinski182f73f2013-12-05 16:48:06 -0800850 ContentResolver resolver = getContext().getContentResolver();
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400851 if (uri == null || NOTIFICATION_LIGHT_PULSE_URI.equals(uri)) {
852 boolean pulseEnabled = Settings.System.getInt(resolver,
853 Settings.System.NOTIFICATION_LIGHT_PULSE, 0) != 0;
854 if (mNotificationPulseEnabled != pulseEnabled) {
855 mNotificationPulseEnabled = pulseEnabled;
856 updateNotificationPulse();
857 }
858 }
Chris Wren763a9bb2016-05-31 17:14:12 -0400859 if (uri == null || NOTIFICATION_RATE_LIMIT_URI.equals(uri)) {
860 mMaxPackageEnqueueRate = Settings.Global.getFloat(resolver,
861 Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE, mMaxPackageEnqueueRate);
862 }
Chris Wrend4054312016-06-24 17:07:40 -0400863 if (uri == null || NOTIFICATION_SOUND_URI.equals(uri)) {
864 mSystemNotificationSound = Settings.System.getString(resolver,
865 Settings.System.NOTIFICATION_SOUND);
866 }
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700867 }
868 }
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500869
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400870 private SettingsObserver mSettingsObserver;
John Spurlock056c5192014-04-20 21:52:01 -0400871 private ZenModeHelper mZenModeHelper;
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400872
John Spurlockcad57682014-07-26 17:09:56 -0400873 private final Runnable mBuzzBeepBlinked = new Runnable() {
874 @Override
875 public void run() {
Wei Liu97e56662016-03-04 10:52:33 -0800876 if (mStatusBar != null) {
877 mStatusBar.buzzBeepBlinked();
878 }
John Spurlockcad57682014-07-26 17:09:56 -0400879 }
880 };
881
Daniel Sandleredbb3802012-11-13 20:49:47 -0800882 static long[] getLongArray(Resources r, int resid, int maxlen, long[] def) {
883 int[] ar = r.getIntArray(resid);
884 if (ar == null) {
885 return def;
886 }
887 final int len = ar.length > maxlen ? maxlen : ar.length;
888 long[] out = new long[len];
889 for (int i=0; i<len; i++) {
890 out[i] = ar[i];
891 }
892 return out;
893 }
894
Jeff Brownb880d882014-02-10 19:47:07 -0800895 public NotificationManagerService(Context context) {
896 super(context);
897 }
898
Chris Wren93bb8b82016-03-29 14:35:05 -0400899 @VisibleForTesting
900 void setAudioManager(AudioManager audioMananger) {
901 mAudioManager = audioMananger;
902 }
903
904 @VisibleForTesting
905 void setVibrator(Vibrator vibrator) {
906 mVibrator = vibrator;
907 }
908
909 @VisibleForTesting
Julia Reynoldsb5e44b72016-08-16 15:00:25 -0400910 void setStatusBarManager(StatusBarManagerInternal statusBar) {
911 mStatusBar = statusBar;
912 }
913
914 @VisibleForTesting
915 void setLights(Light light) {
916 mNotificationLight = light;
917 mAttentionLight = light;
918 }
919
920 @VisibleForTesting
921 void setScreenOn(boolean on) {
922 mScreenOn = on;
923 }
924
925 @VisibleForTesting
926 void addNotification(NotificationRecord r) {
927 mNotificationList.add(r);
928 mNotificationsByKey.put(r.sbn.getKey(), r);
929 }
930
931 @VisibleForTesting
Chris Wren93bb8b82016-03-29 14:35:05 -0400932 void setSystemReady(boolean systemReady) {
933 mSystemReady = systemReady;
934 }
935
936 @VisibleForTesting
937 void setHandler(Handler handler) {
938 mHandler = handler;
939 }
940
Chris Wrend4054312016-06-24 17:07:40 -0400941 @VisibleForTesting
942 void setSystemNotificationSound(String systemNotificationSound) {
943 mSystemNotificationSound = systemNotificationSound;
944 }
945
Adam Lesinski182f73f2013-12-05 16:48:06 -0800946 @Override
947 public void onStart() {
Chris Wren54bbef42014-07-09 18:37:56 -0400948 Resources resources = getContext().getResources();
949
Chris Wren763a9bb2016-05-31 17:14:12 -0400950 mMaxPackageEnqueueRate = Settings.Global.getFloat(getContext().getContentResolver(),
951 Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE,
952 DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE);
953
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800954 mAm = ActivityManagerNative.getDefault();
Adam Lesinski182f73f2013-12-05 16:48:06 -0800955 mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
956 mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
Amith Yamasanif47e51e2015-04-17 10:02:15 -0700957 mAppUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
San Mehat3ee13172010-02-04 20:54:43 -0800958
Chris Wren0efdb882016-03-01 17:17:47 -0500959 // This is the package that contains the AOSP framework update.
960 mRankerServicePackageName = getContext().getPackageManager()
961 .getServicesSystemSharedLibraryPackageName();
962
Adam Lesinski182f73f2013-12-05 16:48:06 -0800963 mHandler = new WorkerHandler();
Chris Wrenf9536642014-04-17 10:01:54 -0400964 mRankingThread.start();
Chris Wren54bbef42014-07-09 18:37:56 -0400965 String[] extractorNames;
966 try {
967 extractorNames = resources.getStringArray(R.array.config_notificationSignalExtractors);
968 } catch (Resources.NotFoundException e) {
969 extractorNames = new String[0];
970 }
Chris Wren5eab2b72015-06-16 13:56:22 -0400971 mUsageStats = new NotificationUsageStats(getContext());
Chris Wren51017d02015-12-15 15:34:46 -0500972 mRankingHandler = new RankingHandlerWorker(mRankingThread.getLooper());
Chris Wren54bbef42014-07-09 18:37:56 -0400973 mRankingHelper = new RankingHelper(getContext(),
Chris Wren51017d02015-12-15 15:34:46 -0500974 mRankingHandler,
Chris Wren5eab2b72015-06-16 13:56:22 -0400975 mUsageStats,
Chris Wren54bbef42014-07-09 18:37:56 -0400976 extractorNames);
John Spurlockb2278d62015-04-07 12:47:12 -0400977 mConditionProviders = new ConditionProviders(getContext(), mHandler, mUserProfiles);
978 mZenModeHelper = new ZenModeHelper(getContext(), mHandler.getLooper(), mConditionProviders);
John Spurlock1c923a32014-04-27 16:42:29 -0400979 mZenModeHelper.addCallback(new ZenModeHelper.Callback() {
John Spurlock056c5192014-04-20 21:52:01 -0400980 @Override
981 public void onConfigChanged() {
982 savePolicyFile();
983 }
John Spurlockd8afe3c2014-08-01 14:04:07 -0400984
985 @Override
986 void onZenModeChanged() {
John Spurlock80774932015-05-07 17:38:50 -0400987 sendRegisteredOnlyBroadcast(NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED);
Jason Monka9927322015-12-13 16:22:37 -0500988 getContext().sendBroadcastAsUser(
Jason Monk63506742015-12-16 12:06:51 -0500989 new Intent(NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED_INTERNAL)
990 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT),
Jason Monka9927322015-12-13 16:22:37 -0500991 UserHandle.ALL, android.Manifest.permission.MANAGE_NOTIFICATIONS);
John Spurlockd8afe3c2014-08-01 14:04:07 -0400992 synchronized(mNotificationList) {
Christoph Studer85a384b2014-08-27 20:16:15 +0200993 updateInterruptionFilterLocked();
John Spurlockd8afe3c2014-08-01 14:04:07 -0400994 }
995 }
John Spurlock1fc476d2015-04-14 16:05:20 -0400996
997 @Override
998 void onPolicyChanged() {
John Spurlock80774932015-05-07 17:38:50 -0400999 sendRegisteredOnlyBroadcast(NotificationManager.ACTION_NOTIFICATION_POLICY_CHANGED);
1000 }
John Spurlock056c5192014-04-20 21:52:01 -04001001 });
1002 final File systemDir = new File(Environment.getDataDirectory(), "system");
1003 mPolicyFile = new AtomicFile(new File(systemDir, "notification_policy.xml"));
Daniel Sandler4a900ac2013-01-30 14:04:10 -05001004
Chris Wrenacf424a2016-03-15 12:48:55 -04001005 syncBlockDb();
Daniel Sandler0da673f2012-04-11 12:33:16 -04001006
Chris Wren0efdb882016-03-01 17:17:47 -05001007 // This is a MangedServices object that keeps track of the listeners.
John Spurlock7340fc82014-04-24 18:50:12 -04001008 mListeners = new NotificationListeners();
Chris Wren0efdb882016-03-01 17:17:47 -05001009
1010 // This is a MangedServices object that keeps track of the ranker.
1011 mRankerServices = new NotificationRankers();
1012 // Find the updatable ranker and register it.
Julia Reynolds1c9bd422016-03-15 09:25:56 -04001013 mRankerServices.registerRanker();
Chris Wren0efdb882016-03-01 17:17:47 -05001014
Adam Lesinski182f73f2013-12-05 16:48:06 -08001015 mStatusBar = getLocalService(StatusBarManagerInternal.class);
Wei Liu97e56662016-03-04 10:52:33 -08001016 if (mStatusBar != null) {
1017 mStatusBar.setNotificationDelegate(mNotificationDelegate);
1018 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001019
Adam Lesinski182f73f2013-12-05 16:48:06 -08001020 final LightsManager lights = getLocalService(LightsManager.class);
1021 mNotificationLight = lights.getLight(LightsManager.LIGHT_ID_NOTIFICATIONS);
1022 mAttentionLight = lights.getLight(LightsManager.LIGHT_ID_ATTENTION);
Mike Lockwood3cb67a32009-11-27 14:25:58 -05001023
Mike Lockwood670f9322010-01-20 12:13:36 -05001024 mDefaultNotificationColor = resources.getColor(
Scott Greenwald9a05b312013-06-28 00:37:54 -04001025 R.color.config_defaultNotificationColor);
Mike Lockwood670f9322010-01-20 12:13:36 -05001026 mDefaultNotificationLedOn = resources.getInteger(
Scott Greenwald9a05b312013-06-28 00:37:54 -04001027 R.integer.config_defaultNotificationLedOn);
Mike Lockwood670f9322010-01-20 12:13:36 -05001028 mDefaultNotificationLedOff = resources.getInteger(
Scott Greenwald9a05b312013-06-28 00:37:54 -04001029 R.integer.config_defaultNotificationLedOff);
Mike Lockwood670f9322010-01-20 12:13:36 -05001030
Daniel Sandleredbb3802012-11-13 20:49:47 -08001031 mDefaultVibrationPattern = getLongArray(resources,
Scott Greenwald9a05b312013-06-28 00:37:54 -04001032 R.array.config_defaultNotificationVibePattern,
Daniel Sandleredbb3802012-11-13 20:49:47 -08001033 VIBRATE_PATTERN_MAXLEN,
1034 DEFAULT_VIBRATE_PATTERN);
1035
1036 mFallbackVibrationPattern = getLongArray(resources,
Scott Greenwald9a05b312013-06-28 00:37:54 -04001037 R.array.config_notificationFallbackVibePattern,
Daniel Sandleredbb3802012-11-13 20:49:47 -08001038 VIBRATE_PATTERN_MAXLEN,
1039 DEFAULT_VIBRATE_PATTERN);
1040
Chris Wren5116a822014-06-04 15:59:50 -04001041 mUseAttentionLight = resources.getBoolean(R.bool.config_useAttentionLight);
1042
Joe Onorato39f5b6a2009-07-23 12:29:19 -04001043 // Don't start allowing notifications until the setup wizard has run once.
1044 // After that, including subsequent boots, init with notifications turned on.
1045 // This works on the first boot because the setup wizard will toggle this
1046 // flag at least once and we'll go back to 0 after that.
Adam Lesinski182f73f2013-12-05 16:48:06 -08001047 if (0 == Settings.Global.getInt(getContext().getContentResolver(),
Jeff Brownbf6f6f92012-09-25 15:03:20 -07001048 Settings.Global.DEVICE_PROVISIONED, 0)) {
John Spurlockd8afe3c2014-08-01 14:04:07 -04001049 mDisableNotificationEffects = true;
Joe Onorato39f5b6a2009-07-23 12:29:19 -04001050 }
John Spurlockb2278d62015-04-07 12:47:12 -04001051 mZenModeHelper.initZenMode();
John Spurlockf3701772015-02-12 13:29:37 -05001052 mInterruptionFilter = mZenModeHelper.getZenModeListenerInterruptionFilter();
Joe Onorato39f5b6a2009-07-23 12:29:19 -04001053
John Spurlockb408e8e2014-04-23 21:12:45 -04001054 mUserProfiles.updateCache(getContext());
John Spurlock32fe4c62014-10-02 12:16:02 -04001055 listenForCallState();
Kenny Guya263e4e2014-03-03 18:24:03 +00001056
Mike Lockwood35e16bf2010-11-30 19:53:36 -05001057 // register for various Intents
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001058 IntentFilter filter = new IntentFilter();
Mike Lockwoodc22404a2009-12-02 11:15:02 -05001059 filter.addAction(Intent.ACTION_SCREEN_ON);
1060 filter.addAction(Intent.ACTION_SCREEN_OFF);
Daniel Sandlere96ffb12010-03-11 13:38:06 -05001061 filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
Mike Lockwood63b5ad92011-08-30 09:55:30 -04001062 filter.addAction(Intent.ACTION_USER_PRESENT);
Dianne Hackborn80a4af22012-08-27 19:18:31 -07001063 filter.addAction(Intent.ACTION_USER_STOPPED);
Daniel Sandler4b749ef2013-03-18 21:53:04 -04001064 filter.addAction(Intent.ACTION_USER_SWITCHED);
Kenny Guy3a7c4a52014-03-03 18:24:03 +00001065 filter.addAction(Intent.ACTION_USER_ADDED);
John Spurlock21258a32015-05-27 18:22:55 -04001066 filter.addAction(Intent.ACTION_USER_REMOVED);
Julia Reynoldsa3dcaff2016-02-03 15:04:05 -05001067 filter.addAction(Intent.ACTION_USER_UNLOCKED);
Rubin Xue95057a2016-04-01 16:49:25 +01001068 filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001069 getContext().registerReceiver(mIntentReceiver, filter);
Kenny Guy70058402014-10-28 20:45:06 +00001070
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08001071 IntentFilter pkgFilter = new IntentFilter();
Chris Wren3da73022013-05-10 14:41:21 -04001072 pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08001073 pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
Daniel Sandleraac0eb02011-08-06 22:51:56 -04001074 pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08001075 pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
1076 pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
1077 pkgFilter.addDataScheme("package");
Kenny Guy70058402014-10-28 20:45:06 +00001078 getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, pkgFilter, null,
1079 null);
1080
Andrei Stingaceanu0122f6512016-01-22 15:33:03 +00001081 IntentFilter suspendedPkgFilter = new IntentFilter();
1082 suspendedPkgFilter.addAction(Intent.ACTION_PACKAGES_SUSPENDED);
1083 getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL,
1084 suspendedPkgFilter, null, null);
1085
Suchi Amalapurapub56ae202010-02-04 22:51:07 -08001086 IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
Kenny Guy70058402014-10-28 20:45:06 +00001087 getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, sdFilter, null,
1088 null);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001089
Daniel Sandler4b749ef2013-03-18 21:53:04 -04001090 mSettingsObserver = new SettingsObserver(mHandler);
Scott Greenwald9a05b312013-06-28 00:37:54 -04001091
Griff Hazen9f637d12014-06-10 11:13:51 -07001092 mArchive = new Archive(resources.getInteger(
1093 R.integer.config_notificationServiceArchiveSize));
1094
Adam Lesinski182f73f2013-12-05 16:48:06 -08001095 publishBinderService(Context.NOTIFICATION_SERVICE, mService);
1096 publishLocalService(NotificationManagerInternal.class, mInternalService);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001097 }
1098
John Spurlocke7a835b2015-05-13 10:47:05 -04001099 private void sendRegisteredOnlyBroadcast(String action) {
1100 getContext().sendBroadcastAsUser(new Intent(action)
1101 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), UserHandle.ALL, null);
1102 }
1103
Daniel Sandler4a900ac2013-01-30 14:04:10 -05001104 /**
Chris Wrenacf424a2016-03-15 12:48:55 -04001105 * Make sure the XML config and the the AppOps system agree about blocks.
Daniel Sandler4a900ac2013-01-30 14:04:10 -05001106 */
Chris Wrenacf424a2016-03-15 12:48:55 -04001107 private void syncBlockDb() {
John Spurlock056c5192014-04-20 21:52:01 -04001108 loadPolicyFile();
Daniel Sandler4a900ac2013-01-30 14:04:10 -05001109
Chris Wrenacf424a2016-03-15 12:48:55 -04001110 // sync bans from ranker into app opps
1111 Map<Integer, String> packageBans = mRankingHelper.getPackageBans();
1112 for(Entry<Integer, String> ban : packageBans.entrySet()) {
1113 final int uid = ban.getKey();
1114 final String packageName = ban.getValue();
1115 setNotificationsEnabledForPackageImpl(packageName, uid, false);
1116 }
1117
1118 // sync bans from app opps into ranker
1119 packageBans.clear();
1120 for (UserInfo user : UserManager.get(getContext()).getUsers()) {
1121 final int userId = user.getUserHandle().getIdentifier();
1122 final PackageManager packageManager = getContext().getPackageManager();
1123 List<PackageInfo> packages = packageManager.getInstalledPackagesAsUser(0, userId);
1124 final int packageCount = packages.size();
1125 for (int p = 0; p < packageCount; p++) {
1126 final String packageName = packages.get(p).packageName;
1127 try {
1128 final int uid = packageManager.getPackageUidAsUser(packageName, userId);
1129 if (!checkNotificationOp(packageName, uid)) {
1130 packageBans.put(uid, packageName);
1131 }
1132 } catch (NameNotFoundException e) {
1133 // forget you
1134 }
Daniel Sandler4a900ac2013-01-30 14:04:10 -05001135 }
1136 }
Chris Wrenacf424a2016-03-15 12:48:55 -04001137 for (Entry<Integer, String> ban : packageBans.entrySet()) {
1138 mRankingHelper.setImportance(ban.getValue(), ban.getKey(), IMPORTANCE_NONE);
1139 }
1140
1141 savePolicyFile();
Daniel Sandler4a900ac2013-01-30 14:04:10 -05001142 }
1143
Adam Lesinski182f73f2013-12-05 16:48:06 -08001144 @Override
1145 public void onBootPhase(int phase) {
1146 if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
1147 // no beeping until we're basically done booting
1148 mSystemReady = true;
Jeff Sharkey098d5802012-04-26 17:30:34 -07001149
Adam Lesinski182f73f2013-12-05 16:48:06 -08001150 // Grab our optional AudioService
1151 mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
John Spurlockcdb57ae2015-02-11 19:04:11 -05001152 mAudioManagerInternal = getLocalService(AudioManagerInternal.class);
Svetoslav Ganovaa076532016-08-01 19:16:43 -07001153 mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
John Spurlock661f2cf2014-11-17 10:29:10 -05001154 mZenModeHelper.onSystemReady();
Adam Lesinskia6db4ab2014-03-24 12:31:45 -07001155 } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
1156 // This observer will force an update when observe is called, causing us to
1157 // bind to listener services.
1158 mSettingsObserver.observe();
John Spurlockb408e8e2014-04-23 21:12:45 -04001159 mListeners.onBootPhaseAppsCanStart();
Chris Wrene0ba7eb2016-03-04 17:30:43 -05001160 mRankerServices.onBootPhaseAppsCanStart();
John Spurlock7340fc82014-04-24 18:50:12 -04001161 mConditionProviders.onBootPhaseAppsCanStart();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001162 }
1163 }
1164
Adam Lesinski182f73f2013-12-05 16:48:06 -08001165 void setNotificationsEnabledForPackageImpl(String pkg, int uid, boolean enabled) {
1166 Slog.v(TAG, (enabled?"en":"dis") + "abling notifications for " + pkg);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001167
Adam Lesinski182f73f2013-12-05 16:48:06 -08001168 mAppOps.setMode(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg,
1169 enabled ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001170
Adam Lesinski182f73f2013-12-05 16:48:06 -08001171 // Now, cancel any outstanding notifications that are part of a just-disabled app
1172 if (ENABLE_BLOCKED_NOTIFICATIONS && !enabled) {
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001173 cancelAllNotificationsInt(MY_UID, MY_PID, pkg, null, 0, 0, true,
1174 UserHandle.getUserId(uid), REASON_PACKAGE_BANNED, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001175 }
1176 }
1177
John Spurlockd8afe3c2014-08-01 14:04:07 -04001178 private void updateListenerHintsLocked() {
Bryce Lee7219ada2016-04-08 10:54:23 -07001179 final int hints = calculateHints();
John Spurlockd8afe3c2014-08-01 14:04:07 -04001180 if (hints == mListenerHints) return;
Bryce Lee7219ada2016-04-08 10:54:23 -07001181 ZenLog.traceListenerHintsChanged(mListenerHints, hints, mEffectsSuppressors.size());
John Spurlockd8afe3c2014-08-01 14:04:07 -04001182 mListenerHints = hints;
1183 scheduleListenerHintsChanged(hints);
John Spurlock1fa865f2014-07-21 14:56:39 -04001184 }
1185
John Spurlockb4782522014-08-22 14:54:46 -04001186 private void updateEffectsSuppressorLocked() {
Bryce Lee7219ada2016-04-08 10:54:23 -07001187 final long updatedSuppressedEffects = calculateSuppressedEffects();
1188 if (updatedSuppressedEffects == mZenModeHelper.getSuppressedEffects()) return;
1189 final List<ComponentName> suppressors = getSuppressors();
1190 ZenLog.traceEffectsSuppressorChanged(mEffectsSuppressors, suppressors, updatedSuppressedEffects);
1191 mEffectsSuppressors = suppressors;
1192 mZenModeHelper.setSuppressedEffects(updatedSuppressedEffects);
John Spurlocke7a835b2015-05-13 10:47:05 -04001193 sendRegisteredOnlyBroadcast(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED);
John Spurlockb4782522014-08-22 14:54:46 -04001194 }
1195
Bryce Lee7219ada2016-04-08 10:54:23 -07001196 private ArrayList<ComponentName> getSuppressors() {
1197 ArrayList<ComponentName> names = new ArrayList<ComponentName>();
1198 for (int i = mListenersDisablingEffects.size() - 1; i >= 0; --i) {
1199 ArraySet<ManagedServiceInfo> serviceInfoList = mListenersDisablingEffects.valueAt(i);
1200
1201 for (ManagedServiceInfo info : serviceInfoList) {
1202 names.add(info.component);
1203 }
1204 }
1205
1206 return names;
1207 }
1208
1209 private boolean removeDisabledHints(ManagedServiceInfo info) {
1210 return removeDisabledHints(info, 0);
1211 }
1212
1213 private boolean removeDisabledHints(ManagedServiceInfo info, int hints) {
1214 boolean removed = false;
1215
1216 for (int i = mListenersDisablingEffects.size() - 1; i >= 0; --i) {
1217 final int hint = mListenersDisablingEffects.keyAt(i);
1218 final ArraySet<ManagedServiceInfo> listeners =
1219 mListenersDisablingEffects.valueAt(i);
1220
1221 if (hints == 0 || (hint & hints) == hint) {
1222 removed = removed || listeners.remove(info);
1223 }
1224 }
1225
1226 return removed;
1227 }
1228
1229 private void addDisabledHints(ManagedServiceInfo info, int hints) {
1230 if ((hints & HINT_HOST_DISABLE_EFFECTS) != 0) {
1231 addDisabledHint(info, HINT_HOST_DISABLE_EFFECTS);
1232 }
1233
1234 if ((hints & HINT_HOST_DISABLE_NOTIFICATION_EFFECTS) != 0) {
1235 addDisabledHint(info, HINT_HOST_DISABLE_NOTIFICATION_EFFECTS);
1236 }
1237
1238 if ((hints & HINT_HOST_DISABLE_CALL_EFFECTS) != 0) {
1239 addDisabledHint(info, HINT_HOST_DISABLE_CALL_EFFECTS);
1240 }
1241 }
1242
1243 private void addDisabledHint(ManagedServiceInfo info, int hint) {
1244 if (mListenersDisablingEffects.indexOfKey(hint) < 0) {
1245 mListenersDisablingEffects.put(hint, new ArraySet<ManagedServiceInfo>());
1246 }
1247
1248 ArraySet<ManagedServiceInfo> hintListeners = mListenersDisablingEffects.get(hint);
1249 hintListeners.add(info);
1250 }
1251
1252 private int calculateHints() {
1253 int hints = 0;
1254 for (int i = mListenersDisablingEffects.size() - 1; i >= 0; --i) {
1255 int hint = mListenersDisablingEffects.keyAt(i);
1256 ArraySet<ManagedServiceInfo> serviceInfoList = mListenersDisablingEffects.valueAt(i);
1257
1258 if (!serviceInfoList.isEmpty()) {
1259 hints |= hint;
1260 }
1261 }
1262
1263 return hints;
1264 }
1265
1266 private long calculateSuppressedEffects() {
1267 int hints = calculateHints();
1268 long suppressedEffects = 0;
1269
1270 if ((hints & HINT_HOST_DISABLE_EFFECTS) != 0) {
1271 suppressedEffects |= ZenModeHelper.SUPPRESSED_EFFECT_ALL;
1272 }
1273
1274 if ((hints & HINT_HOST_DISABLE_NOTIFICATION_EFFECTS) != 0) {
1275 suppressedEffects |= ZenModeHelper.SUPPRESSED_EFFECT_NOTIFICATIONS;
1276 }
1277
1278 if ((hints & HINT_HOST_DISABLE_CALL_EFFECTS) != 0) {
1279 suppressedEffects |= ZenModeHelper.SUPPRESSED_EFFECT_CALLS;
1280 }
1281
1282 return suppressedEffects;
1283 }
1284
Christoph Studer85a384b2014-08-27 20:16:15 +02001285 private void updateInterruptionFilterLocked() {
1286 int interruptionFilter = mZenModeHelper.getZenModeListenerInterruptionFilter();
1287 if (interruptionFilter == mInterruptionFilter) return;
1288 mInterruptionFilter = interruptionFilter;
1289 scheduleInterruptionFilterChanged(interruptionFilter);
1290 }
1291
Adam Lesinski182f73f2013-12-05 16:48:06 -08001292 private final IBinder mService = new INotificationManager.Stub() {
1293 // Toasts
1294 // ============================================================================
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001295
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001296 @Override
Adam Lesinski182f73f2013-12-05 16:48:06 -08001297 public void enqueueToast(String pkg, ITransientNotification callback, int duration)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001298 {
Adam Lesinski182f73f2013-12-05 16:48:06 -08001299 if (DBG) {
1300 Slog.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback
1301 + " duration=" + duration);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001302 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001303
1304 if (pkg == null || callback == null) {
1305 Slog.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback);
1306 return ;
1307 }
1308
John Spurlock7340fc82014-04-24 18:50:12 -04001309 final boolean isSystemToast = isCallerSystem() || ("android".equals(pkg));
Andrei Stingaceanu355b2322016-02-12 16:43:51 +00001310 final boolean isPackageSuspended =
1311 isPackageSuspendedForUser(pkg, Binder.getCallingUid());
Adam Lesinski182f73f2013-12-05 16:48:06 -08001312
Andrei Stingaceanu0122f6512016-01-22 15:33:03 +00001313 if (ENABLE_BLOCKED_TOASTS && (!noteNotificationOp(pkg, Binder.getCallingUid())
Andrei Stingaceanu355b2322016-02-12 16:43:51 +00001314 || isPackageSuspended)) {
Adam Lesinski182f73f2013-12-05 16:48:06 -08001315 if (!isSystemToast) {
Andrei Stingaceanu0122f6512016-01-22 15:33:03 +00001316 Slog.e(TAG, "Suppressing toast from package " + pkg
Andrei Stingaceanu355b2322016-02-12 16:43:51 +00001317 + (isPackageSuspended
Andrei Stingaceanu0122f6512016-01-22 15:33:03 +00001318 ? " due to package suspended by administrator."
1319 : " by user request."));
Adam Lesinski182f73f2013-12-05 16:48:06 -08001320 return;
1321 }
1322 }
1323
1324 synchronized (mToastQueue) {
1325 int callingPid = Binder.getCallingPid();
1326 long callingId = Binder.clearCallingIdentity();
1327 try {
1328 ToastRecord record;
1329 int index = indexOfToastLocked(pkg, callback);
1330 // If it's already in the queue, we update it in place, we don't
1331 // move it to the end of the queue.
1332 if (index >= 0) {
1333 record = mToastQueue.get(index);
1334 record.update(duration);
1335 } else {
1336 // Limit the number of toasts that any given package except the android
1337 // package can enqueue. Prevents DOS attacks and deals with leaks.
1338 if (!isSystemToast) {
1339 int count = 0;
1340 final int N = mToastQueue.size();
1341 for (int i=0; i<N; i++) {
1342 final ToastRecord r = mToastQueue.get(i);
1343 if (r.pkg.equals(pkg)) {
1344 count++;
1345 if (count >= MAX_PACKAGE_NOTIFICATIONS) {
1346 Slog.e(TAG, "Package has already posted " + count
1347 + " toasts. Not showing more. Package=" + pkg);
1348 return;
1349 }
1350 }
1351 }
1352 }
1353
Svetoslav Ganovaa076532016-08-01 19:16:43 -07001354 Binder token = new Binder();
1355 mWindowManagerInternal.addWindowToken(token,
1356 WindowManager.LayoutParams.TYPE_TOAST);
1357 record = new ToastRecord(callingPid, pkg, callback, duration, token);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001358 mToastQueue.add(record);
1359 index = mToastQueue.size() - 1;
Svetoslav Ganovaa076532016-08-01 19:16:43 -07001360 keepProcessAliveIfNeededLocked(callingPid);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001361 }
1362 // If it's at index 0, it's the current toast. It doesn't matter if it's
1363 // new or just been updated. Call back and tell it to show itself.
1364 // If the callback fails, this will remove it from the list, so don't
1365 // assume that it's valid after this.
1366 if (index == 0) {
1367 showNextToastLocked();
1368 }
1369 } finally {
1370 Binder.restoreCallingIdentity(callingId);
1371 }
1372 }
1373 }
1374
1375 @Override
1376 public void cancelToast(String pkg, ITransientNotification callback) {
1377 Slog.i(TAG, "cancelToast pkg=" + pkg + " callback=" + callback);
1378
1379 if (pkg == null || callback == null) {
1380 Slog.e(TAG, "Not cancelling notification. pkg=" + pkg + " callback=" + callback);
1381 return ;
1382 }
1383
1384 synchronized (mToastQueue) {
1385 long callingId = Binder.clearCallingIdentity();
1386 try {
1387 int index = indexOfToastLocked(pkg, callback);
1388 if (index >= 0) {
1389 cancelToastLocked(index);
1390 } else {
1391 Slog.w(TAG, "Toast already cancelled. pkg=" + pkg
1392 + " callback=" + callback);
1393 }
1394 } finally {
1395 Binder.restoreCallingIdentity(callingId);
1396 }
1397 }
1398 }
1399
1400 @Override
Christoph Studer8fd7f1e2014-04-11 17:35:05 -04001401 public void enqueueNotificationWithTag(String pkg, String opPkg, String tag, int id,
Adam Lesinski182f73f2013-12-05 16:48:06 -08001402 Notification notification, int[] idOut, int userId) throws RemoteException {
Christoph Studer8fd7f1e2014-04-11 17:35:05 -04001403 enqueueNotificationInternal(pkg, opPkg, Binder.getCallingUid(),
Adam Lesinski182f73f2013-12-05 16:48:06 -08001404 Binder.getCallingPid(), tag, id, notification, idOut, userId);
1405 }
1406
1407 @Override
1408 public void cancelNotificationWithTag(String pkg, String tag, int id, int userId) {
John Spurlock7340fc82014-04-24 18:50:12 -04001409 checkCallerIsSystemOrSameApp(pkg);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001410 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1411 Binder.getCallingUid(), userId, true, false, "cancelNotificationWithTag", pkg);
Julia Reynoldse46bb372016-03-17 11:05:58 -04001412 // Don't allow client applications to cancel foreground service notis or autobundled
1413 // summaries.
John Spurlocke6a7d932014-03-13 12:29:00 -04001414 cancelNotification(Binder.getCallingUid(), Binder.getCallingPid(), pkg, tag, id, 0,
Julia Reynoldse46bb372016-03-17 11:05:58 -04001415 (Binder.getCallingUid() == Process.SYSTEM_UID
1416 ? 0 : Notification.FLAG_FOREGROUND_SERVICE)
1417 | (Binder.getCallingUid() == Process.SYSTEM_UID
1418 ? 0 : Notification.FLAG_AUTOGROUP_SUMMARY), false, userId,
Chris Wren9fa689f2015-11-20 16:44:53 -05001419 REASON_APP_CANCEL, null);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001420 }
1421
1422 @Override
1423 public void cancelAllNotifications(String pkg, int userId) {
John Spurlock7340fc82014-04-24 18:50:12 -04001424 checkCallerIsSystemOrSameApp(pkg);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001425
1426 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1427 Binder.getCallingUid(), userId, true, false, "cancelAllNotifications", pkg);
1428
1429 // Calling from user space, don't allow the canceling of actively
1430 // running foreground services.
John Spurlocke6a7d932014-03-13 12:29:00 -04001431 cancelAllNotificationsInt(Binder.getCallingUid(), Binder.getCallingPid(),
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001432 pkg, null, 0, Notification.FLAG_FOREGROUND_SERVICE, true, userId,
Julia Reynoldsef37f282016-02-12 09:11:27 -05001433 REASON_APP_CANCEL_ALL, null);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001434 }
1435
1436 @Override
1437 public void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) {
John Spurlock7340fc82014-04-24 18:50:12 -04001438 checkCallerIsSystem();
Adam Lesinski182f73f2013-12-05 16:48:06 -08001439
1440 setNotificationsEnabledForPackageImpl(pkg, uid, enabled);
Chris Wrenacf424a2016-03-15 12:48:55 -04001441 mRankingHelper.setEnabled(pkg, uid, enabled);
1442 savePolicyFile();
Adam Lesinski182f73f2013-12-05 16:48:06 -08001443 }
1444
1445 /**
1446 * Use this when you just want to know if notifications are OK for this package.
1447 */
1448 @Override
Julia Reynolds81afbcd2016-02-09 14:54:08 -05001449 public boolean areNotificationsEnabled(String pkg) {
1450 return areNotificationsEnabledForPackage(pkg, Binder.getCallingUid());
1451 }
1452
1453 /**
1454 * Use this when you just want to know if notifications are OK for this package.
1455 */
1456 @Override
Adam Lesinski182f73f2013-12-05 16:48:06 -08001457 public boolean areNotificationsEnabledForPackage(String pkg, int uid) {
Julia Reynolds81afbcd2016-02-09 14:54:08 -05001458 checkCallerIsSystemOrSameApp(pkg);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001459 return (mAppOps.checkOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
Andrei Stingaceanu355b2322016-02-12 16:43:51 +00001460 == AppOpsManager.MODE_ALLOWED) && !isPackageSuspendedForUser(pkg, uid);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001461 }
1462
Chris Wren54bbef42014-07-09 18:37:56 -04001463 @Override
Julia Reynoldsef37f282016-02-12 09:11:27 -05001464 public void setPriority(String pkg, int uid, int priority) {
Julia Reynoldsdd3e86b2016-02-02 10:24:30 -05001465 checkCallerIsSystem();
Julia Reynoldsef37f282016-02-12 09:11:27 -05001466 mRankingHelper.setPriority(pkg, uid, priority);
Chris Wren54bbef42014-07-09 18:37:56 -04001467 savePolicyFile();
1468 }
1469
1470 @Override
Julia Reynoldsef37f282016-02-12 09:11:27 -05001471 public int getPriority(String pkg, int uid) {
Chris Wren54bbef42014-07-09 18:37:56 -04001472 checkCallerIsSystem();
Julia Reynoldsef37f282016-02-12 09:11:27 -05001473 return mRankingHelper.getPriority(pkg, uid);
Chris Wren54bbef42014-07-09 18:37:56 -04001474 }
1475
Chris Wren3ad4e3a2014-09-02 17:23:51 -04001476 @Override
Julia Reynoldsef37f282016-02-12 09:11:27 -05001477 public void setVisibilityOverride(String pkg, int uid, int visibility) {
Chris Wren3ad4e3a2014-09-02 17:23:51 -04001478 checkCallerIsSystem();
Julia Reynoldsef37f282016-02-12 09:11:27 -05001479 mRankingHelper.setVisibilityOverride(pkg, uid, visibility);
Chris Wren3ad4e3a2014-09-02 17:23:51 -04001480 savePolicyFile();
1481 }
1482
1483 @Override
Julia Reynoldsef37f282016-02-12 09:11:27 -05001484 public int getVisibilityOverride(String pkg, int uid) {
Chris Wren3ad4e3a2014-09-02 17:23:51 -04001485 checkCallerIsSystem();
Julia Reynoldsef37f282016-02-12 09:11:27 -05001486 return mRankingHelper.getVisibilityOverride(pkg, uid);
Chris Wren3ad4e3a2014-09-02 17:23:51 -04001487 }
1488
Julia Reynolds5d25ee72015-11-20 15:38:20 -05001489 @Override
Julia Reynolds0edb50c2016-02-26 14:08:25 -05001490 public void setImportance(String pkg, int uid, int importance) {
Julia Reynoldsead00aa2015-12-07 08:23:48 -05001491 enforceSystemOrSystemUI("Caller not system or systemui");
Julia Reynoldsef37f282016-02-12 09:11:27 -05001492 setNotificationsEnabledForPackageImpl(pkg, uid,
1493 importance != NotificationListenerService.Ranking.IMPORTANCE_NONE);
1494 mRankingHelper.setImportance(pkg, uid, importance);
Julia Reynolds5d25ee72015-11-20 15:38:20 -05001495 savePolicyFile();
1496 }
1497
1498 @Override
Julia Reynoldsef37f282016-02-12 09:11:27 -05001499 public int getPackageImportance(String pkg) {
Julia Reynolds81afbcd2016-02-09 14:54:08 -05001500 checkCallerIsSystemOrSameApp(pkg);
Julia Reynoldsef37f282016-02-12 09:11:27 -05001501 return mRankingHelper.getImportance(pkg, Binder.getCallingUid());
Julia Reynolds81afbcd2016-02-09 14:54:08 -05001502 }
1503
1504 @Override
Julia Reynoldsef37f282016-02-12 09:11:27 -05001505 public int getImportance(String pkg, int uid) {
Julia Reynoldscac88622016-03-03 09:28:19 -05001506 enforceSystemOrSystemUI("Caller not system or systemui");
Julia Reynoldsef37f282016-02-12 09:11:27 -05001507 return mRankingHelper.getImportance(pkg, uid);
Julia Reynoldsbe8fdee2015-12-18 09:04:34 -05001508 }
1509
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04001510 @Override
1511 public void createNotificationChannel(String pkg, NotificationChannel channel) {
1512 Preconditions.checkNotNull(channel);
1513 Preconditions.checkNotNull(channel.getId());
1514 Preconditions.checkNotNull(channel.getName());
1515 checkCallerIsSystemOrSameApp(pkg);
1516 mRankingHelper.createNotificationChannel(pkg, Binder.getCallingUid(), channel);
1517 savePolicyFile();
1518 }
1519
1520 @Override
1521 public void updateNotificationChannel(String pkg, NotificationChannel channel) {
1522 Preconditions.checkNotNull(channel);
1523 Preconditions.checkNotNull(channel.getId());
1524 checkCallerIsSystemOrSameApp(pkg);
1525 if (channel.getImportance() == NotificationManager.IMPORTANCE_NONE) {
1526 // cancel
1527 cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channel.getId(), 0, 0, true,
1528 UserHandle.getUserId(Binder.getCallingUid()), REASON_CHANNEL_BANNED, null);
1529 }
1530 mRankingHelper.updateNotificationChannel(Binder.getCallingUid(), pkg,
1531 Binder.getCallingUid(), channel);
1532 savePolicyFile();
1533 }
1534
1535 @Override
1536 public NotificationChannel getNotificationChannel(String pkg, String channelId) {
1537 Preconditions.checkNotNull(channelId);
1538 checkCallerIsSystemOrSameApp(pkg);
1539 return mRankingHelper.getNotificationChannel(pkg, Binder.getCallingUid(), channelId);
1540 }
1541
1542 @Override
1543 public NotificationChannel getNotificationChannelForPackage(String pkg, int uid,
1544 String channelId) {
1545 Preconditions.checkNotNull(channelId);
1546 checkCallerIsSystem();
1547 return mRankingHelper.getNotificationChannel(pkg, uid, channelId);
1548 }
1549
1550 @Override
1551 public void deleteNotificationChannel(String pkg, String channelId) {
1552 Preconditions.checkNotNull(channelId);
1553 checkCallerIsSystemOrSameApp(pkg);
1554 if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(channelId)) {
1555 throw new IllegalArgumentException("Cannot delete default channel");
1556 }
1557 cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channelId, 0, 0, true,
1558 UserHandle.getUserId(Binder.getCallingUid()), REASON_CHANNEL_BANNED, null);
1559 mRankingHelper.deleteNotificationChannel(pkg, Binder.getCallingUid(), channelId);
1560 savePolicyFile();
1561 }
1562
1563 @Override
1564 public void updateNotificationChannelForPackage(String pkg, int uid,
1565 NotificationChannel channel) {
1566 Preconditions.checkNotNull(channel);
1567 Preconditions.checkNotNull(channel.getId());
1568 checkCallerIsSystem();
1569 if (channel.getImportance() == NotificationManager.IMPORTANCE_NONE) {
1570 // cancel
1571 cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channel.getId(), 0, 0, true,
1572 UserHandle.getUserId(Binder.getCallingUid()), REASON_CHANNEL_BANNED, null);
1573 }
1574 mRankingHelper.updateNotificationChannel(Binder.getCallingUid(), pkg, uid, channel);
1575 savePolicyFile();
1576 }
1577
1578 @Override
1579 public ParceledListSlice<NotificationChannel> getNotificationChannelsForPackage(String pkg,
1580 int uid) {
1581 checkCallerIsSystem();
1582 return mRankingHelper.getNotificationChannels(pkg, uid);
1583 }
1584
1585 @Override
1586 public ParceledListSlice<NotificationChannel> getNotificationChannels(String pkg) {
1587 checkCallerIsSystemOrSameApp(pkg);
1588 return mRankingHelper.getNotificationChannels(pkg, Binder.getCallingUid());
1589 }
1590
Adam Lesinski182f73f2013-12-05 16:48:06 -08001591 /**
1592 * System-only API for getting a list of current (i.e. not cleared) notifications.
1593 *
1594 * Requires ACCESS_NOTIFICATIONS which is signature|system.
Chris Wrenf9536642014-04-17 10:01:54 -04001595 * @returns A list of all the notifications, in natural order.
Adam Lesinski182f73f2013-12-05 16:48:06 -08001596 */
1597 @Override
1598 public StatusBarNotification[] getActiveNotifications(String callingPkg) {
1599 // enforce() will ensure the calling uid has the correct permission
1600 getContext().enforceCallingOrSelfPermission(
1601 android.Manifest.permission.ACCESS_NOTIFICATIONS,
1602 "NotificationManagerService.getActiveNotifications");
1603
1604 StatusBarNotification[] tmp = null;
1605 int uid = Binder.getCallingUid();
1606
1607 // noteOp will check to make sure the callingPkg matches the uid
1608 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
1609 == AppOpsManager.MODE_ALLOWED) {
1610 synchronized (mNotificationList) {
1611 tmp = new StatusBarNotification[mNotificationList.size()];
1612 final int N = mNotificationList.size();
1613 for (int i=0; i<N; i++) {
1614 tmp[i] = mNotificationList.get(i).sbn;
1615 }
1616 }
1617 }
1618 return tmp;
1619 }
1620
1621 /**
Dan Sandler994349c2015-04-15 11:02:54 -04001622 * Public API for getting a list of current notifications for the calling package/uid.
1623 *
1624 * @returns A list of all the package's notifications, in natural order.
1625 */
1626 @Override
1627 public ParceledListSlice<StatusBarNotification> getAppActiveNotifications(String pkg,
1628 int incomingUserId) {
1629 checkCallerIsSystemOrSameApp(pkg);
1630 int userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1631 Binder.getCallingUid(), incomingUserId, true, false,
1632 "getAppActiveNotifications", pkg);
1633
Erik Wolsheimer2242b4d2015-11-24 13:22:04 -08001634 final ArrayList<StatusBarNotification> list
1635 = new ArrayList<StatusBarNotification>(mNotificationList.size());
Dan Sandler994349c2015-04-15 11:02:54 -04001636
1637 synchronized (mNotificationList) {
Erik Wolsheimer2242b4d2015-11-24 13:22:04 -08001638 final int N = mNotificationList.size();
Dan Sandler994349c2015-04-15 11:02:54 -04001639 for (int i = 0; i < N; i++) {
1640 final StatusBarNotification sbn = mNotificationList.get(i).sbn;
Julia Reynoldse46bb372016-03-17 11:05:58 -04001641 if (sbn.getPackageName().equals(pkg) && sbn.getUserId() == userId
1642 && (sbn.getNotification().flags
Julia Reynolds4c4ad592016-04-12 11:16:37 -04001643 & Notification.FLAG_AUTOGROUP_SUMMARY) == 0) {
Dan Sandler994349c2015-04-15 11:02:54 -04001644 // We could pass back a cloneLight() but clients might get confused and
1645 // try to send this thing back to notify() again, which would not work
1646 // very well.
1647 final StatusBarNotification sbnOut = new StatusBarNotification(
1648 sbn.getPackageName(),
1649 sbn.getOpPkg(),
1650 sbn.getId(), sbn.getTag(), sbn.getUid(), sbn.getInitialPid(),
1651 0, // hide score from apps
1652 sbn.getNotification().clone(),
1653 sbn.getUser(), sbn.getPostTime());
1654 list.add(sbnOut);
1655 }
1656 }
1657 }
1658
1659 return new ParceledListSlice<StatusBarNotification>(list);
1660 }
1661
1662 /**
Adam Lesinski182f73f2013-12-05 16:48:06 -08001663 * System-only API for getting a list of recent (cleared, no longer shown) notifications.
1664 *
1665 * Requires ACCESS_NOTIFICATIONS which is signature|system.
1666 */
1667 @Override
1668 public StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count) {
1669 // enforce() will ensure the calling uid has the correct permission
1670 getContext().enforceCallingOrSelfPermission(
1671 android.Manifest.permission.ACCESS_NOTIFICATIONS,
1672 "NotificationManagerService.getHistoricalNotifications");
1673
1674 StatusBarNotification[] tmp = null;
1675 int uid = Binder.getCallingUid();
1676
1677 // noteOp will check to make sure the callingPkg matches the uid
1678 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
1679 == AppOpsManager.MODE_ALLOWED) {
1680 synchronized (mArchive) {
1681 tmp = mArchive.getArray(count);
1682 }
1683 }
1684 return tmp;
1685 }
1686
1687 /**
1688 * Register a listener binder directly with the notification manager.
1689 *
1690 * Only works with system callers. Apps should extend
1691 * {@link android.service.notification.NotificationListenerService}.
1692 */
1693 @Override
1694 public void registerListener(final INotificationListener listener,
Chris Wren0efdb882016-03-01 17:17:47 -05001695 final ComponentName component, final int userid) {
Christoph Studer3e144d32014-05-22 16:48:40 +02001696 enforceSystemOrSystemUI("INotificationManager.registerListener");
Chris Wren0efdb882016-03-01 17:17:47 -05001697 mListeners.registerService(listener, component, userid);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001698 }
1699
1700 /**
1701 * Remove a listener binder directly
1702 */
1703 @Override
Chris Wrene0ba7eb2016-03-04 17:30:43 -05001704 public void unregisterListener(INotificationListener token, int userid) {
Chris Wrenb7c81092016-03-10 11:41:10 -05001705 mListeners.unregisterService(token, userid);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001706 }
1707
1708 /**
1709 * Allow an INotificationListener to simulate a "clear all" operation.
1710 *
1711 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onClearAllNotifications}
1712 *
1713 * @param token The binder for the listener, to check that the caller is allowed
1714 */
1715 @Override
John Spurlocka4294292014-03-24 18:02:32 -04001716 public void cancelNotificationsFromListener(INotificationListener token, String[] keys) {
John Spurlocke6a7d932014-03-13 12:29:00 -04001717 final int callingUid = Binder.getCallingUid();
1718 final int callingPid = Binder.getCallingPid();
Adam Lesinski182f73f2013-12-05 16:48:06 -08001719 long identity = Binder.clearCallingIdentity();
1720 try {
Adam Lesinskie8240262014-03-26 16:01:00 -07001721 synchronized (mNotificationList) {
John Spurlock7340fc82014-04-24 18:50:12 -04001722 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
John Spurlocka4294292014-03-24 18:02:32 -04001723 if (keys != null) {
1724 final int N = keys.length;
1725 for (int i = 0; i < N; i++) {
1726 NotificationRecord r = mNotificationsByKey.get(keys[i]);
Griff Hazen335e1f02014-09-11 14:49:31 -07001727 if (r == null) continue;
Kenny Guya263e4e2014-03-03 18:24:03 +00001728 final int userId = r.sbn.getUserId();
1729 if (userId != info.userid && userId != UserHandle.USER_ALL &&
John Spurlockb408e8e2014-04-23 21:12:45 -04001730 !mUserProfiles.isCurrentProfile(userId)) {
Kenny Guya263e4e2014-03-03 18:24:03 +00001731 throw new SecurityException("Disallowed call from listener: "
John Spurlock7340fc82014-04-24 18:50:12 -04001732 + info.service);
Kenny Guya263e4e2014-03-03 18:24:03 +00001733 }
Griff Hazen335e1f02014-09-11 14:49:31 -07001734 cancelNotificationFromListenerLocked(info, callingUid, callingPid,
1735 r.sbn.getPackageName(), r.sbn.getTag(), r.sbn.getId(),
1736 userId);
John Spurlocka4294292014-03-24 18:02:32 -04001737 }
1738 } else {
1739 cancelAllLocked(callingUid, callingPid, info.userid,
Kenny Guya263e4e2014-03-03 18:24:03 +00001740 REASON_LISTENER_CANCEL_ALL, info, info.supportsProfiles());
John Spurlocka4294292014-03-24 18:02:32 -04001741 }
Adam Lesinskie8240262014-03-26 16:01:00 -07001742 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001743 } finally {
1744 Binder.restoreCallingIdentity(identity);
1745 }
1746 }
1747
Chris Wrenab41eec2016-01-04 18:01:27 -05001748 /**
1749 * Handle request from an approved listener to re-enable itself.
1750 *
1751 * @param component The componenet to be re-enabled, caller must match package.
1752 */
1753 @Override
1754 public void requestBindListener(ComponentName component) {
1755 checkCallerIsSystemOrSameApp(component.getPackageName());
1756 long identity = Binder.clearCallingIdentity();
1757 try {
Julia Reynoldse46bb372016-03-17 11:05:58 -04001758 ManagedServices manager =
1759 mRankerServices.isComponentEnabledForCurrentProfiles(component)
Chris Wrene0ba7eb2016-03-04 17:30:43 -05001760 ? mRankerServices
Chris Wrenab41eec2016-01-04 18:01:27 -05001761 : mListeners;
1762 manager.setComponentState(component, true);
1763 } finally {
1764 Binder.restoreCallingIdentity(identity);
1765 }
1766 }
1767
1768 @Override
1769 public void requestUnbindListener(INotificationListener token) {
1770 long identity = Binder.clearCallingIdentity();
1771 try {
1772 // allow bound services to disable themselves
1773 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1774 info.getOwner().setComponentState(info.component, false);
1775 } finally {
1776 Binder.restoreCallingIdentity(identity);
1777 }
1778 }
1779
Amith Yamasanif47e51e2015-04-17 10:02:15 -07001780 @Override
1781 public void setNotificationsShownFromListener(INotificationListener token, String[] keys) {
Amith Yamasanif47e51e2015-04-17 10:02:15 -07001782 long identity = Binder.clearCallingIdentity();
1783 try {
1784 synchronized (mNotificationList) {
1785 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1786 if (keys != null) {
1787 final int N = keys.length;
1788 for (int i = 0; i < N; i++) {
1789 NotificationRecord r = mNotificationsByKey.get(keys[i]);
1790 if (r == null) continue;
1791 final int userId = r.sbn.getUserId();
1792 if (userId != info.userid && userId != UserHandle.USER_ALL &&
1793 !mUserProfiles.isCurrentProfile(userId)) {
1794 throw new SecurityException("Disallowed call from listener: "
1795 + info.service);
1796 }
1797 if (!r.isSeen()) {
1798 if (DBG) Slog.d(TAG, "Marking notification as seen " + keys[i]);
1799 mAppUsageStats.reportEvent(r.sbn.getPackageName(),
Xiaohui Chenddbe4ca2015-08-13 16:20:56 -07001800 userId == UserHandle.USER_ALL ? UserHandle.USER_SYSTEM
Amith Yamasanif47e51e2015-04-17 10:02:15 -07001801 : userId,
Adam Lesinskic8e87292015-06-10 15:33:45 -07001802 UsageEvents.Event.USER_INTERACTION);
Amith Yamasanif47e51e2015-04-17 10:02:15 -07001803 r.setSeen();
1804 }
1805 }
1806 }
1807 }
1808 } finally {
1809 Binder.restoreCallingIdentity(identity);
1810 }
1811 }
1812
John Spurlock7340fc82014-04-24 18:50:12 -04001813 private void cancelNotificationFromListenerLocked(ManagedServiceInfo info,
Kenny Guya263e4e2014-03-03 18:24:03 +00001814 int callingUid, int callingPid, String pkg, String tag, int id, int userId) {
John Spurlocka4294292014-03-24 18:02:32 -04001815 cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
1816 Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
1817 true,
Kenny Guya263e4e2014-03-03 18:24:03 +00001818 userId, REASON_LISTENER_CANCEL, info);
John Spurlocka4294292014-03-24 18:02:32 -04001819 }
1820
Adam Lesinski182f73f2013-12-05 16:48:06 -08001821 /**
1822 * Allow an INotificationListener to simulate clearing (dismissing) a single notification.
1823 *
1824 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onNotificationClear}
1825 *
1826 * @param token The binder for the listener, to check that the caller is allowed
1827 */
1828 @Override
1829 public void cancelNotificationFromListener(INotificationListener token, String pkg,
1830 String tag, int id) {
John Spurlocke6a7d932014-03-13 12:29:00 -04001831 final int callingUid = Binder.getCallingUid();
1832 final int callingPid = Binder.getCallingPid();
Adam Lesinski182f73f2013-12-05 16:48:06 -08001833 long identity = Binder.clearCallingIdentity();
1834 try {
Adam Lesinskie8240262014-03-26 16:01:00 -07001835 synchronized (mNotificationList) {
John Spurlock7340fc82014-04-24 18:50:12 -04001836 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
Kenny Guya263e4e2014-03-03 18:24:03 +00001837 if (info.supportsProfiles()) {
1838 Log.e(TAG, "Ignoring deprecated cancelNotification(pkg, tag, id) "
1839 + "from " + info.component
1840 + " use cancelNotification(key) instead.");
1841 } else {
1842 cancelNotificationFromListenerLocked(info, callingUid, callingPid,
1843 pkg, tag, id, info.userid);
1844 }
Adam Lesinskie8240262014-03-26 16:01:00 -07001845 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001846 } finally {
1847 Binder.restoreCallingIdentity(identity);
1848 }
1849 }
1850
1851 /**
1852 * Allow an INotificationListener to request the list of outstanding notifications seen by
1853 * the current user. Useful when starting up, after which point the listener callbacks
1854 * should be used.
1855 *
1856 * @param token The binder for the listener, to check that the caller is allowed
Dan Sandlerea75fdd2014-08-12 12:29:19 -04001857 * @param keys An array of notification keys to fetch, or null to fetch everything
Chris Wrenf9536642014-04-17 10:01:54 -04001858 * @returns The return value will contain the notifications specified in keys, in that
1859 * order, or if keys is null, all the notifications, in natural order.
Adam Lesinski182f73f2013-12-05 16:48:06 -08001860 */
1861 @Override
Christoph Studercee44ba2014-05-20 18:36:43 +02001862 public ParceledListSlice<StatusBarNotification> getActiveNotificationsFromListener(
Christoph Studerb82bc782014-08-20 14:29:43 +02001863 INotificationListener token, String[] keys, int trim) {
Adam Lesinski182f73f2013-12-05 16:48:06 -08001864 synchronized (mNotificationList) {
John Spurlock7340fc82014-04-24 18:50:12 -04001865 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
Dan Sandlerea75fdd2014-08-12 12:29:19 -04001866 final boolean getKeys = keys != null;
1867 final int N = getKeys ? keys.length : mNotificationList.size();
Christoph Studerb82bc782014-08-20 14:29:43 +02001868 final ArrayList<StatusBarNotification> list
1869 = new ArrayList<StatusBarNotification>(N);
Christoph Studercee44ba2014-05-20 18:36:43 +02001870 for (int i=0; i<N; i++) {
Dan Sandlerea75fdd2014-08-12 12:29:19 -04001871 final NotificationRecord r = getKeys
1872 ? mNotificationsByKey.get(keys[i])
1873 : mNotificationList.get(i);
Christoph Studerb82bc782014-08-20 14:29:43 +02001874 if (r == null) continue;
1875 StatusBarNotification sbn = r.sbn;
1876 if (!isVisibleToListener(sbn, info)) continue;
1877 StatusBarNotification sbnToSend =
1878 (trim == TRIM_FULL) ? sbn : sbn.cloneLight();
1879 list.add(sbnToSend);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001880 }
Christoph Studercee44ba2014-05-20 18:36:43 +02001881 return new ParceledListSlice<StatusBarNotification>(list);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001882 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001883 }
1884
1885 @Override
John Spurlockd8afe3c2014-08-01 14:04:07 -04001886 public void requestHintsFromListener(INotificationListener token, int hints) {
1887 final long identity = Binder.clearCallingIdentity();
1888 try {
1889 synchronized (mNotificationList) {
1890 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
Bryce Lee7219ada2016-04-08 10:54:23 -07001891 final int disableEffectsMask = HINT_HOST_DISABLE_EFFECTS
1892 | HINT_HOST_DISABLE_NOTIFICATION_EFFECTS
1893 | HINT_HOST_DISABLE_CALL_EFFECTS;
1894 final boolean disableEffects = (hints & disableEffectsMask) != 0;
John Spurlockd8afe3c2014-08-01 14:04:07 -04001895 if (disableEffects) {
Bryce Lee7219ada2016-04-08 10:54:23 -07001896 addDisabledHints(info, hints);
John Spurlockd8afe3c2014-08-01 14:04:07 -04001897 } else {
Bryce Lee7219ada2016-04-08 10:54:23 -07001898 removeDisabledHints(info, hints);
John Spurlockd8afe3c2014-08-01 14:04:07 -04001899 }
John Spurlockd8afe3c2014-08-01 14:04:07 -04001900 updateListenerHintsLocked();
John Spurlockb4782522014-08-22 14:54:46 -04001901 updateEffectsSuppressorLocked();
John Spurlock1fa865f2014-07-21 14:56:39 -04001902 }
John Spurlockd8afe3c2014-08-01 14:04:07 -04001903 } finally {
1904 Binder.restoreCallingIdentity(identity);
John Spurlock1fa865f2014-07-21 14:56:39 -04001905 }
1906 }
1907
1908 @Override
John Spurlockd8afe3c2014-08-01 14:04:07 -04001909 public int getHintsFromListener(INotificationListener token) {
John Spurlock1fa865f2014-07-21 14:56:39 -04001910 synchronized (mNotificationList) {
John Spurlockd8afe3c2014-08-01 14:04:07 -04001911 return mListenerHints;
John Spurlock1fa865f2014-07-21 14:56:39 -04001912 }
1913 }
1914
1915 @Override
Christoph Studer85a384b2014-08-27 20:16:15 +02001916 public void requestInterruptionFilterFromListener(INotificationListener token,
1917 int interruptionFilter) throws RemoteException {
1918 final long identity = Binder.clearCallingIdentity();
1919 try {
1920 synchronized (mNotificationList) {
John Spurlock661f2cf2014-11-17 10:29:10 -05001921 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1922 mZenModeHelper.requestFromListener(info.component, interruptionFilter);
Christoph Studer85a384b2014-08-27 20:16:15 +02001923 updateInterruptionFilterLocked();
1924 }
1925 } finally {
1926 Binder.restoreCallingIdentity(identity);
1927 }
1928 }
1929
1930 @Override
1931 public int getInterruptionFilterFromListener(INotificationListener token)
1932 throws RemoteException {
1933 synchronized (mNotificationLight) {
1934 return mInterruptionFilter;
1935 }
1936 }
1937
1938 @Override
Christoph Studerb82bc782014-08-20 14:29:43 +02001939 public void setOnNotificationPostedTrimFromListener(INotificationListener token, int trim)
1940 throws RemoteException {
1941 synchronized (mNotificationList) {
1942 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1943 if (info == null) return;
1944 mListeners.setOnNotificationPostedTrimLocked(info, trim);
1945 }
1946 }
1947
1948 @Override
John Spurlockb2278d62015-04-07 12:47:12 -04001949 public int getZenMode() {
1950 return mZenModeHelper.getZenMode();
1951 }
1952
1953 @Override
John Spurlock056c5192014-04-20 21:52:01 -04001954 public ZenModeConfig getZenModeConfig() {
John Spurlockcdb57ae2015-02-11 19:04:11 -05001955 enforceSystemOrSystemUIOrVolume("INotificationManager.getZenModeConfig");
John Spurlock056c5192014-04-20 21:52:01 -04001956 return mZenModeHelper.getConfig();
1957 }
1958
1959 @Override
John Spurlockb2278d62015-04-07 12:47:12 -04001960 public void setZenMode(int mode, Uri conditionId, String reason) throws RemoteException {
John Spurlockcdb57ae2015-02-11 19:04:11 -05001961 enforceSystemOrSystemUIOrVolume("INotificationManager.setZenMode");
1962 final long identity = Binder.clearCallingIdentity();
1963 try {
Julia Reynolds44ad6ff2016-07-06 09:47:45 -04001964 mZenModeHelper.setManualZenMode(mode, conditionId, null, reason);
John Spurlockcdb57ae2015-02-11 19:04:11 -05001965 } finally {
1966 Binder.restoreCallingIdentity(identity);
1967 }
1968 }
1969
1970 @Override
Julia Reynolds361e82d32016-02-26 18:19:49 -05001971 public List<ZenModeConfig.ZenRule> getZenRules() throws RemoteException {
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04001972 enforcePolicyAccess(Binder.getCallingUid(), "getAutomaticZenRules");
Julia Reynolds361e82d32016-02-26 18:19:49 -05001973 return mZenModeHelper.getZenRules();
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04001974 }
1975
1976 @Override
Julia Reynolds4fe98d62015-10-06 16:23:41 -04001977 public AutomaticZenRule getAutomaticZenRule(String id) throws RemoteException {
1978 Preconditions.checkNotNull(id, "Id is null");
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04001979 enforcePolicyAccess(Binder.getCallingUid(), "getAutomaticZenRule");
Julia Reynolds4fe98d62015-10-06 16:23:41 -04001980 return mZenModeHelper.getAutomaticZenRule(id);
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04001981 }
1982
1983 @Override
Julia Reynolds361e82d32016-02-26 18:19:49 -05001984 public String addAutomaticZenRule(AutomaticZenRule automaticZenRule)
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04001985 throws RemoteException {
1986 Preconditions.checkNotNull(automaticZenRule, "automaticZenRule is null");
1987 Preconditions.checkNotNull(automaticZenRule.getName(), "Name is null");
1988 Preconditions.checkNotNull(automaticZenRule.getOwner(), "Owner is null");
1989 Preconditions.checkNotNull(automaticZenRule.getConditionId(), "ConditionId is null");
Julia Reynolds4fe98d62015-10-06 16:23:41 -04001990 enforcePolicyAccess(Binder.getCallingUid(), "addAutomaticZenRule");
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04001991
Julia Reynolds4fe98d62015-10-06 16:23:41 -04001992 return mZenModeHelper.addAutomaticZenRule(automaticZenRule,
1993 "addAutomaticZenRule");
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04001994 }
1995
1996 @Override
Julia Reynolds361e82d32016-02-26 18:19:49 -05001997 public boolean updateAutomaticZenRule(String id, AutomaticZenRule automaticZenRule)
Julia Reynolds4fe98d62015-10-06 16:23:41 -04001998 throws RemoteException {
1999 Preconditions.checkNotNull(automaticZenRule, "automaticZenRule is null");
2000 Preconditions.checkNotNull(automaticZenRule.getName(), "Name is null");
2001 Preconditions.checkNotNull(automaticZenRule.getOwner(), "Owner is null");
2002 Preconditions.checkNotNull(automaticZenRule.getConditionId(), "ConditionId is null");
2003 enforcePolicyAccess(Binder.getCallingUid(), "updateAutomaticZenRule");
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04002004
Julia Reynolds361e82d32016-02-26 18:19:49 -05002005 return mZenModeHelper.updateAutomaticZenRule(id, automaticZenRule,
Julia Reynolds4fe98d62015-10-06 16:23:41 -04002006 "updateAutomaticZenRule");
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04002007 }
2008
2009 @Override
Julia Reynolds4fe98d62015-10-06 16:23:41 -04002010 public boolean removeAutomaticZenRule(String id) throws RemoteException {
2011 Preconditions.checkNotNull(id, "Id is null");
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04002012 // Verify that they can modify zen rules.
2013 enforcePolicyAccess(Binder.getCallingUid(), "removeAutomaticZenRule");
2014
Julia Reynolds4fe98d62015-10-06 16:23:41 -04002015 return mZenModeHelper.removeAutomaticZenRule(id, "removeAutomaticZenRule");
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04002016 }
2017
2018 @Override
Julia Reynoldsc8e54e82015-11-30 16:43:05 -05002019 public boolean removeAutomaticZenRules(String packageName) throws RemoteException {
2020 Preconditions.checkNotNull(packageName, "Package name is null");
2021 enforceSystemOrSystemUI("removeAutomaticZenRules");
2022
2023 return mZenModeHelper.removeAutomaticZenRules(packageName, "removeAutomaticZenRules");
2024 }
2025
2026 @Override
Julia Reynolds43b70cd2016-01-14 15:05:34 -05002027 public int getRuleInstanceCount(ComponentName owner) throws RemoteException {
2028 Preconditions.checkNotNull(owner, "Owner is null");
2029 enforceSystemOrSystemUI("getRuleInstanceCount");
2030
2031 return mZenModeHelper.getCurrentInstanceCount(owner);
2032 }
2033
2034 @Override
John Spurlock80774932015-05-07 17:38:50 -04002035 public void setInterruptionFilter(String pkg, int filter) throws RemoteException {
2036 enforcePolicyAccess(pkg, "setInterruptionFilter");
2037 final int zen = NotificationManager.zenModeFromInterruptionFilter(filter, -1);
2038 if (zen == -1) throw new IllegalArgumentException("Invalid filter: " + filter);
2039 final long identity = Binder.clearCallingIdentity();
2040 try {
Julia Reynolds44ad6ff2016-07-06 09:47:45 -04002041 mZenModeHelper.setManualZenMode(zen, null, pkg, "setInterruptionFilter");
John Spurlock80774932015-05-07 17:38:50 -04002042 } finally {
2043 Binder.restoreCallingIdentity(identity);
2044 }
2045 }
2046
2047 @Override
John Spurlocka7d92b12015-05-13 14:48:02 -04002048 public void notifyConditions(final String pkg, IConditionProvider provider,
2049 final Condition[] conditions) {
John Spurlocke77bb362014-04-26 10:24:59 -04002050 final ManagedServiceInfo info = mConditionProviders.checkServiceToken(provider);
2051 checkCallerIsSystemOrSameApp(pkg);
John Spurlocka7d92b12015-05-13 14:48:02 -04002052 mHandler.post(new Runnable() {
2053 @Override
2054 public void run() {
2055 mConditionProviders.notifyConditions(pkg, info, conditions);
2056 }
2057 });
John Spurlocke77bb362014-04-26 10:24:59 -04002058 }
2059
Julia Reynolds38e6ca42016-08-08 08:38:09 -04002060 @Override
2061 public void requestUnbindProvider(IConditionProvider provider) {
2062 long identity = Binder.clearCallingIdentity();
2063 try {
2064 // allow bound services to disable themselves
2065 final ManagedServiceInfo info = mConditionProviders.checkServiceToken(provider);
2066 info.getOwner().setComponentState(info.component, false);
2067 } finally {
2068 Binder.restoreCallingIdentity(identity);
2069 }
2070 }
2071
2072 @Override
2073 public void requestBindProvider(ComponentName component) {
2074 checkCallerIsSystemOrSameApp(component.getPackageName());
2075 long identity = Binder.clearCallingIdentity();
2076 try {
2077 mConditionProviders.setComponentState(component, true);
2078 } finally {
2079 Binder.restoreCallingIdentity(identity);
2080 }
2081 }
2082
John Spurlockcdb57ae2015-02-11 19:04:11 -05002083 private void enforceSystemOrSystemUIOrVolume(String message) {
2084 if (mAudioManagerInternal != null) {
2085 final int vcuid = mAudioManagerInternal.getVolumeControllerUid();
2086 if (vcuid > 0 && Binder.getCallingUid() == vcuid) {
2087 return;
2088 }
2089 }
2090 enforceSystemOrSystemUI(message);
2091 }
2092
John Spurlocke77bb362014-04-26 10:24:59 -04002093 private void enforceSystemOrSystemUI(String message) {
2094 if (isCallerSystem()) return;
2095 getContext().enforceCallingPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
2096 message);
John Spurlock7340fc82014-04-24 18:50:12 -04002097 }
2098
Julia Reynolds48034f82016-03-09 10:15:16 -05002099 private void enforceSystemOrSystemUIOrSamePackage(String pkg, String message) {
2100 try {
2101 checkCallerIsSystemOrSameApp(pkg);
2102 } catch (SecurityException e) {
2103 getContext().enforceCallingPermission(
2104 android.Manifest.permission.STATUS_BAR_SERVICE,
2105 message);
2106 }
2107 }
2108
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04002109 private void enforcePolicyAccess(int uid, String method) {
2110 if (PackageManager.PERMISSION_GRANTED == getContext().checkCallingPermission(
2111 android.Manifest.permission.MANAGE_NOTIFICATIONS)) {
2112 return;
2113 }
2114 boolean accessAllowed = false;
2115 String[] packages = getContext().getPackageManager().getPackagesForUid(uid);
2116 final int packageCount = packages.length;
2117 for (int i = 0; i < packageCount; i++) {
2118 if (checkPolicyAccess(packages[i])) {
2119 accessAllowed = true;
2120 }
2121 }
2122 if (!accessAllowed) {
2123 Slog.w(TAG, "Notification policy access denied calling " + method);
2124 throw new SecurityException("Notification policy access denied");
2125 }
2126 }
2127
John Spurlock80774932015-05-07 17:38:50 -04002128 private void enforcePolicyAccess(String pkg, String method) {
Julia Reynolds6ee26172015-09-28 11:34:48 -04002129 if (PackageManager.PERMISSION_GRANTED == getContext().checkCallingPermission(
2130 android.Manifest.permission.MANAGE_NOTIFICATIONS)) {
2131 return;
2132 }
Julia Reynolds0cd1b782016-06-29 08:43:00 -04002133 checkCallerIsSameApp(pkg);
John Spurlock80774932015-05-07 17:38:50 -04002134 if (!checkPolicyAccess(pkg)) {
2135 Slog.w(TAG, "Notification policy access denied calling " + method);
2136 throw new SecurityException("Notification policy access denied");
John Spurlock1fc476d2015-04-14 16:05:20 -04002137 }
2138 }
2139
John Spurlock80774932015-05-07 17:38:50 -04002140 private boolean checkPackagePolicyAccess(String pkg) {
John Spurlock7c74f782015-06-04 13:01:42 -04002141 return mPolicyAccess.isPackageGranted(pkg);
John Spurlock80774932015-05-07 17:38:50 -04002142 }
2143
2144 private boolean checkPolicyAccess(String pkg) {
Julia Reynolds0867b3a2016-03-30 17:29:54 -04002145 try {
2146 int uid = getContext().getPackageManager().getPackageUidAsUser(
2147 pkg, UserHandle.getCallingUserId());
2148 if (PackageManager.PERMISSION_GRANTED == ActivityManager.checkComponentPermission(
2149 android.Manifest.permission.MANAGE_NOTIFICATIONS, uid,
2150 -1, true)) {
2151 return true;
2152 }
2153 } catch (NameNotFoundException e) {
2154 return false;
Julia Reynoldsa2d01022016-03-18 15:03:43 -04002155 }
John Spurlock80774932015-05-07 17:38:50 -04002156 return checkPackagePolicyAccess(pkg) || mListeners.isComponentEnabledForPackage(pkg);
John Spurlock1fc476d2015-04-14 16:05:20 -04002157 }
2158
John Spurlock7340fc82014-04-24 18:50:12 -04002159 @Override
Adam Lesinski182f73f2013-12-05 16:48:06 -08002160 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2161 if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
2162 != PackageManager.PERMISSION_GRANTED) {
John Spurlock2b122f42014-08-27 16:29:47 -04002163 pw.println("Permission Denial: can't dump NotificationManager from pid="
Adam Lesinski182f73f2013-12-05 16:48:06 -08002164 + Binder.getCallingPid()
2165 + ", uid=" + Binder.getCallingUid());
2166 return;
2167 }
2168
Chris Wrene4b38802015-07-07 15:54:19 -04002169 final DumpFilter filter = DumpFilter.parseFromArguments(args);
2170 if (filter != null && filter.stats) {
2171 dumpJson(pw, filter);
2172 } else {
2173 dumpImpl(pw, filter);
2174 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08002175 }
John Spurlockb4782522014-08-22 14:54:46 -04002176
2177 @Override
2178 public ComponentName getEffectsSuppressor() {
John Spurlockcdb57ae2015-02-11 19:04:11 -05002179 enforceSystemOrSystemUIOrVolume("INotificationManager.getEffectsSuppressor");
Bryce Leeba3d8952016-04-12 12:39:15 -07002180 return !mEffectsSuppressors.isEmpty() ? mEffectsSuppressors.get(0) : null;
John Spurlockb4782522014-08-22 14:54:46 -04002181 }
John Spurlock2b122f42014-08-27 16:29:47 -04002182
2183 @Override
2184 public boolean matchesCallFilter(Bundle extras) {
2185 enforceSystemOrSystemUI("INotificationManager.matchesCallFilter");
Christoph Studer12aeda82014-09-23 19:08:56 +02002186 return mZenModeHelper.matchesCallFilter(
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07002187 Binder.getCallingUserHandle(),
Christoph Studer12aeda82014-09-23 19:08:56 +02002188 extras,
2189 mRankingHelper.findExtractor(ValidateNotificationPeople.class),
2190 MATCHES_CALL_FILTER_CONTACTS_TIMEOUT_MS,
2191 MATCHES_CALL_FILTER_TIMEOUT_AFFINITY);
John Spurlock2b122f42014-08-27 16:29:47 -04002192 }
John Spurlock530052a2014-11-30 16:26:19 -05002193
2194 @Override
2195 public boolean isSystemConditionProviderEnabled(String path) {
John Spurlockcdb57ae2015-02-11 19:04:11 -05002196 enforceSystemOrSystemUIOrVolume("INotificationManager.isSystemConditionProviderEnabled");
John Spurlockb2278d62015-04-07 12:47:12 -04002197 return mConditionProviders.isSystemProviderEnabled(path);
John Spurlock530052a2014-11-30 16:26:19 -05002198 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08002199
Christopher Tatef9767d62015-04-08 14:35:43 -07002200 // Backup/restore interface
2201 @Override
2202 public byte[] getBackupPayload(int user) {
John Spurlock35ef0a62015-05-28 11:24:10 -04002203 if (DBG) Slog.d(TAG, "getBackupPayload u=" + user);
Xiaohui Chenddbe4ca2015-08-13 16:20:56 -07002204 //TODO: http://b/22388012
2205 if (user != UserHandle.USER_SYSTEM) {
John Spurlock35ef0a62015-05-28 11:24:10 -04002206 Slog.w(TAG, "getBackupPayload: cannot backup policy for user " + user);
2207 return null;
2208 }
2209 final ByteArrayOutputStream baos = new ByteArrayOutputStream();
2210 try {
2211 writePolicyXml(baos, true /*forBackup*/);
2212 return baos.toByteArray();
2213 } catch (IOException e) {
2214 Slog.w(TAG, "getBackupPayload: error writing payload for user " + user, e);
2215 }
Christopher Tatef9767d62015-04-08 14:35:43 -07002216 return null;
2217 }
2218
2219 @Override
2220 public void applyRestore(byte[] payload, int user) {
John Spurlock35ef0a62015-05-28 11:24:10 -04002221 if (DBG) Slog.d(TAG, "applyRestore u=" + user + " payload="
2222 + (payload != null ? new String(payload, StandardCharsets.UTF_8) : null));
2223 if (payload == null) {
2224 Slog.w(TAG, "applyRestore: no payload to restore for user " + user);
2225 return;
2226 }
Xiaohui Chenddbe4ca2015-08-13 16:20:56 -07002227 //TODO: http://b/22388012
2228 if (user != UserHandle.USER_SYSTEM) {
John Spurlock35ef0a62015-05-28 11:24:10 -04002229 Slog.w(TAG, "applyRestore: cannot restore policy for user " + user);
2230 return;
2231 }
2232 final ByteArrayInputStream bais = new ByteArrayInputStream(payload);
2233 try {
2234 readPolicyXml(bais, true /*forRestore*/);
2235 savePolicyFile();
2236 } catch (NumberFormatException | XmlPullParserException | IOException e) {
2237 Slog.w(TAG, "applyRestore: error reading payload", e);
2238 }
Christopher Tatef9767d62015-04-08 14:35:43 -07002239 }
2240
John Spurlock1fc476d2015-04-14 16:05:20 -04002241 @Override
John Spurlock80774932015-05-07 17:38:50 -04002242 public boolean isNotificationPolicyAccessGranted(String pkg) {
2243 return checkPolicyAccess(pkg);
John Spurlock1fc476d2015-04-14 16:05:20 -04002244 }
2245
2246 @Override
Julia Reynolds48034f82016-03-09 10:15:16 -05002247 public boolean isNotificationPolicyAccessGrantedForPackage(String pkg) {;
2248 enforceSystemOrSystemUIOrSamePackage(pkg,
2249 "request policy access status for another package");
Julia Reynoldsa2d01022016-03-18 15:03:43 -04002250 return checkPolicyAccess(pkg);
John Spurlock80774932015-05-07 17:38:50 -04002251 }
2252
2253 @Override
2254 public String[] getPackagesRequestingNotificationPolicyAccess()
2255 throws RemoteException {
2256 enforceSystemOrSystemUI("request policy access packages");
2257 final long identity = Binder.clearCallingIdentity();
2258 try {
John Spurlock7c74f782015-06-04 13:01:42 -04002259 return mPolicyAccess.getRequestingPackages();
John Spurlock80774932015-05-07 17:38:50 -04002260 } finally {
2261 Binder.restoreCallingIdentity(identity);
2262 }
2263 }
2264
2265 @Override
2266 public void setNotificationPolicyAccessGranted(String pkg, boolean granted)
2267 throws RemoteException {
2268 enforceSystemOrSystemUI("grant notification policy access");
2269 final long identity = Binder.clearCallingIdentity();
2270 try {
2271 synchronized (mNotificationList) {
2272 mPolicyAccess.put(pkg, granted);
2273 }
2274 } finally {
2275 Binder.restoreCallingIdentity(identity);
2276 }
2277 }
2278
2279 @Override
2280 public Policy getNotificationPolicy(String pkg) {
2281 enforcePolicyAccess(pkg, "getNotificationPolicy");
John Spurlock1fc476d2015-04-14 16:05:20 -04002282 final long identity = Binder.clearCallingIdentity();
2283 try {
2284 return mZenModeHelper.getNotificationPolicy();
2285 } finally {
2286 Binder.restoreCallingIdentity(identity);
2287 }
2288 }
2289
2290 @Override
John Spurlock80774932015-05-07 17:38:50 -04002291 public void setNotificationPolicy(String pkg, Policy policy) {
2292 enforcePolicyAccess(pkg, "setNotificationPolicy");
John Spurlock1fc476d2015-04-14 16:05:20 -04002293 final long identity = Binder.clearCallingIdentity();
2294 try {
2295 mZenModeHelper.setNotificationPolicy(policy);
2296 } finally {
2297 Binder.restoreCallingIdentity(identity);
2298 }
2299 }
Chris Wren51017d02015-12-15 15:34:46 -05002300
2301 @Override
Julia Reynoldse46bb372016-03-17 11:05:58 -04002302 public void applyAdjustmentFromRankerService(INotificationListener token,
2303 Adjustment adjustment) throws RemoteException {
Chris Wren51017d02015-12-15 15:34:46 -05002304 final long identity = Binder.clearCallingIdentity();
2305 try {
2306 synchronized (mNotificationList) {
Chris Wrene0ba7eb2016-03-04 17:30:43 -05002307 mRankerServices.checkServiceTokenLocked(token);
Julia Reynoldse46bb372016-03-17 11:05:58 -04002308 applyAdjustmentLocked(adjustment);
Chris Wren51017d02015-12-15 15:34:46 -05002309 }
Julia Reynoldse46bb372016-03-17 11:05:58 -04002310 maybeAddAutobundleSummary(adjustment);
2311 mRankingHandler.requestSort();
2312 } finally {
2313 Binder.restoreCallingIdentity(identity);
2314 }
2315 }
2316
2317 @Override
2318 public void applyAdjustmentsFromRankerService(INotificationListener token,
2319 List<Adjustment> adjustments) throws RemoteException {
2320
2321 final long identity = Binder.clearCallingIdentity();
2322 try {
2323 synchronized (mNotificationList) {
2324 mRankerServices.checkServiceTokenLocked(token);
2325 for (Adjustment adjustment : adjustments) {
2326 applyAdjustmentLocked(adjustment);
2327 }
2328 }
2329 for (Adjustment adjustment : adjustments) {
2330 maybeAddAutobundleSummary(adjustment);
2331 }
2332 mRankingHandler.requestSort();
Chris Wren51017d02015-12-15 15:34:46 -05002333 } finally {
2334 Binder.restoreCallingIdentity(identity);
2335 }
2336 }
John Spurlock1fc476d2015-04-14 16:05:20 -04002337 };
John Spurlocka4294292014-03-24 18:02:32 -04002338
Julia Reynoldse46bb372016-03-17 11:05:58 -04002339 private void applyAdjustmentLocked(Adjustment adjustment) {
2340 maybeClearAutobundleSummaryLocked(adjustment);
2341 NotificationRecord n = mNotificationsByKey.get(adjustment.getKey());
2342 if (n == null) {
2343 return;
2344 }
2345 if (adjustment.getImportance() != IMPORTANCE_NONE) {
2346 n.setImportance(adjustment.getImportance(), adjustment.getExplanation());
2347 }
2348 if (adjustment.getSignals() != null) {
2349 Bundle.setDefusable(adjustment.getSignals(), true);
Julia Reynoldsb1ebc3b2016-04-15 15:12:36 -04002350 final String autoGroupKey = adjustment.getSignals().getString(
2351 Adjustment.GROUP_KEY_OVERRIDE_KEY, null);
2352 if (autoGroupKey == null) {
2353 EventLogTags.writeNotificationUnautogrouped(adjustment.getKey());
2354 } else {
2355 EventLogTags.writeNotificationAutogrouped(adjustment.getKey());
2356 }
2357 n.sbn.setOverrideGroupKey(autoGroupKey);
Julia Reynoldse46bb372016-03-17 11:05:58 -04002358 }
2359 }
2360
2361 // Clears the 'fake' auto-bunding summary.
2362 private void maybeClearAutobundleSummaryLocked(Adjustment adjustment) {
Julia Reynoldsdbf44812016-04-14 08:54:45 -04002363 if (adjustment.getSignals() != null) {
2364 Bundle.setDefusable(adjustment.getSignals(), true);
2365 if (adjustment.getSignals().containsKey(Adjustment.NEEDS_AUTOGROUPING_KEY)
Julia Reynoldse46bb372016-03-17 11:05:58 -04002366 && !adjustment.getSignals().getBoolean(Adjustment.NEEDS_AUTOGROUPING_KEY, false)) {
Julia Reynoldseae43fb2016-05-09 12:42:58 -04002367 ArrayMap<String, String> summaries =
2368 mAutobundledSummaries.get(adjustment.getUser());
2369 if (summaries != null && summaries.containsKey(adjustment.getPackage())) {
Julia Reynoldsdbf44812016-04-14 08:54:45 -04002370 // Clear summary.
2371 final NotificationRecord removed = mNotificationsByKey.get(
Julia Reynoldseae43fb2016-05-09 12:42:58 -04002372 summaries.remove(adjustment.getPackage()));
Julia Reynoldsdbf44812016-04-14 08:54:45 -04002373 if (removed != null) {
2374 mNotificationList.remove(removed);
2375 cancelNotificationLocked(removed, false, REASON_UNAUTOBUNDLED);
2376 }
Julia Reynoldse46bb372016-03-17 11:05:58 -04002377 }
2378 }
2379 }
2380 }
2381
2382 // Posts a 'fake' summary for a package that has exceeded the solo-notification limit.
2383 private void maybeAddAutobundleSummary(Adjustment adjustment) {
Julia Reynoldsdbf44812016-04-14 08:54:45 -04002384 if (adjustment.getSignals() != null) {
2385 Bundle.setDefusable(adjustment.getSignals(), true);
2386 if (adjustment.getSignals().getBoolean(Adjustment.NEEDS_AUTOGROUPING_KEY, false)) {
2387 final String newAutoBundleKey =
2388 adjustment.getSignals().getString(Adjustment.GROUP_KEY_OVERRIDE_KEY, null);
2389 int userId = -1;
2390 NotificationRecord summaryRecord = null;
2391 synchronized (mNotificationList) {
Selim Cinek5b03ce92016-05-18 15:16:58 -07002392 NotificationRecord notificationRecord =
2393 mNotificationsByKey.get(adjustment.getKey());
2394 if (notificationRecord == null) {
2395 // The notification could have been cancelled again already. A successive
2396 // adjustment will post a summary if needed.
2397 return;
2398 }
2399 final StatusBarNotification adjustedSbn = notificationRecord.sbn;
Julia Reynoldseae43fb2016-05-09 12:42:58 -04002400 userId = adjustedSbn.getUser().getIdentifier();
2401 ArrayMap<String, String> summaries = mAutobundledSummaries.get(userId);
2402 if (summaries == null) {
2403 summaries = new ArrayMap<>();
2404 }
2405 mAutobundledSummaries.put(userId, summaries);
2406 if (!summaries.containsKey(adjustment.getPackage())
Julia Reynoldsdbf44812016-04-14 08:54:45 -04002407 && newAutoBundleKey != null) {
2408 // Add summary
Julia Reynoldsdbf44812016-04-14 08:54:45 -04002409 final ApplicationInfo appInfo =
2410 adjustedSbn.getNotification().extras.getParcelable(
2411 Notification.EXTRA_BUILDER_APPLICATION_INFO);
2412 final Bundle extras = new Bundle();
2413 extras.putParcelable(Notification.EXTRA_BUILDER_APPLICATION_INFO, appInfo);
2414 final Notification summaryNotification =
2415 new Notification.Builder(getContext()).setSmallIcon(
2416 adjustedSbn.getNotification().getSmallIcon())
2417 .setGroupSummary(true)
2418 .setGroup(newAutoBundleKey)
2419 .setFlag(Notification.FLAG_AUTOGROUP_SUMMARY, true)
2420 .setFlag(Notification.FLAG_GROUP_SUMMARY, true)
Julia Reynolds3011fd42016-05-20 10:25:43 -04002421 .setColor(adjustedSbn.getNotification().color)
Julia Reynolds29607002016-07-06 10:45:05 -04002422 .setLocalOnly(true)
Julia Reynoldsdbf44812016-04-14 08:54:45 -04002423 .build();
2424 summaryNotification.extras.putAll(extras);
Julia Reynolds5bba3e72016-04-15 15:13:54 -04002425 Intent appIntent = getContext().getPackageManager()
2426 .getLaunchIntentForPackage(adjustment.getPackage());
2427 if (appIntent != null) {
2428 summaryNotification.contentIntent = PendingIntent.getActivityAsUser(
2429 getContext(), 0, appIntent, 0, null,
Julia Reynoldseae43fb2016-05-09 12:42:58 -04002430 UserHandle.of(userId));
Julia Reynolds5bba3e72016-04-15 15:13:54 -04002431 }
Julia Reynoldsdbf44812016-04-14 08:54:45 -04002432 final StatusBarNotification summarySbn =
2433 new StatusBarNotification(adjustedSbn.getPackageName(),
2434 adjustedSbn.getOpPkg(),
2435 Integer.MAX_VALUE, Adjustment.GROUP_KEY_OVERRIDE_KEY,
2436 adjustedSbn.getUid(), adjustedSbn.getInitialPid(),
2437 summaryNotification, adjustedSbn.getUser(),
2438 newAutoBundleKey,
2439 System.currentTimeMillis());
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04002440 summaryRecord = new NotificationRecord(getContext(), summarySbn,
2441 mRankingHelper.getNotificationChannel(adjustedSbn.getPackageName(),
2442 adjustedSbn.getUid(),
2443 adjustedSbn.getNotification().getNotificationChannel()));
Julia Reynoldseae43fb2016-05-09 12:42:58 -04002444 summaries.put(adjustment.getPackage(), summarySbn.getKey());
Julia Reynoldsdbf44812016-04-14 08:54:45 -04002445 }
Julia Reynoldse46bb372016-03-17 11:05:58 -04002446 }
Julia Reynoldsdbf44812016-04-14 08:54:45 -04002447 if (summaryRecord != null) {
2448 mHandler.post(new EnqueueNotificationRunnable(userId, summaryRecord));
2449 }
Julia Reynoldse46bb372016-03-17 11:05:58 -04002450 }
2451 }
2452 }
2453
John Spurlock32fe4c62014-10-02 12:16:02 -04002454 private String disableNotificationEffects(NotificationRecord record) {
2455 if (mDisableNotificationEffects) {
2456 return "booleanState";
2457 }
2458 if ((mListenerHints & HINT_HOST_DISABLE_EFFECTS) != 0) {
2459 return "listenerHints";
2460 }
2461 if (mCallState != TelephonyManager.CALL_STATE_IDLE && !mZenModeHelper.isCall(record)) {
2462 return "callState";
2463 }
2464 return null;
Chris Wrene4b38802015-07-07 15:54:19 -04002465 };
2466
2467 private void dumpJson(PrintWriter pw, DumpFilter filter) {
2468 JSONObject dump = new JSONObject();
2469 try {
2470 dump.put("service", "Notification Manager");
Chris Wrenacf424a2016-03-15 12:48:55 -04002471 dump.put("bans", mRankingHelper.dumpBansJson(filter));
2472 dump.put("ranking", mRankingHelper.dumpJson(filter));
Chris Wrene4b38802015-07-07 15:54:19 -04002473 dump.put("stats", mUsageStats.dumpJson(filter));
2474 } catch (JSONException e) {
2475 e.printStackTrace();
2476 }
2477 pw.println(dump);
John Spurlock1fa865f2014-07-21 14:56:39 -04002478 }
2479
John Spurlock25e2d242014-06-27 13:58:23 -04002480 void dumpImpl(PrintWriter pw, DumpFilter filter) {
2481 pw.print("Current Notification Manager state");
Dan Sandlera1770312015-07-10 13:59:29 -04002482 if (filter.filtered) {
John Spurlock50806fc2014-07-15 10:22:02 -04002483 pw.print(" (filtered to "); pw.print(filter); pw.print(")");
John Spurlock25e2d242014-06-27 13:58:23 -04002484 }
2485 pw.println(':');
Adam Lesinski182f73f2013-12-05 16:48:06 -08002486 int N;
Julia Reynoldse6b53e62015-07-31 09:25:10 -04002487 final boolean zenOnly = filter.filtered && filter.zen;
Adam Lesinski182f73f2013-12-05 16:48:06 -08002488
John Spurlock50806fc2014-07-15 10:22:02 -04002489 if (!zenOnly) {
2490 synchronized (mToastQueue) {
2491 N = mToastQueue.size();
2492 if (N > 0) {
2493 pw.println(" Toast Queue:");
2494 for (int i=0; i<N; i++) {
2495 mToastQueue.get(i).dump(pw, " ", filter);
2496 }
2497 pw.println(" ");
Adam Lesinski182f73f2013-12-05 16:48:06 -08002498 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08002499 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08002500 }
2501
2502 synchronized (mNotificationList) {
John Spurlock50806fc2014-07-15 10:22:02 -04002503 if (!zenOnly) {
2504 N = mNotificationList.size();
John Spurlock25e2d242014-06-27 13:58:23 -04002505 if (N > 0) {
John Spurlock50806fc2014-07-15 10:22:02 -04002506 pw.println(" Notification List:");
John Spurlock25e2d242014-06-27 13:58:23 -04002507 for (int i=0; i<N; i++) {
John Spurlock50806fc2014-07-15 10:22:02 -04002508 final NotificationRecord nr = mNotificationList.get(i);
Julia Reynoldse6b53e62015-07-31 09:25:10 -04002509 if (filter.filtered && !filter.matches(nr.sbn)) continue;
Dan Sandlera1770312015-07-10 13:59:29 -04002510 nr.dump(pw, " ", getContext(), filter.redact);
John Spurlock25e2d242014-06-27 13:58:23 -04002511 }
2512 pw.println(" ");
Adam Lesinski182f73f2013-12-05 16:48:06 -08002513 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08002514
Julia Reynoldse6b53e62015-07-31 09:25:10 -04002515 if (!filter.filtered) {
John Spurlock50806fc2014-07-15 10:22:02 -04002516 N = mLights.size();
2517 if (N > 0) {
2518 pw.println(" Lights List:");
2519 for (int i=0; i<N; i++) {
Chris Wren6054e612014-11-25 17:16:46 -05002520 if (i == N - 1) {
2521 pw.print(" > ");
2522 } else {
2523 pw.print(" ");
2524 }
2525 pw.println(mLights.get(i));
John Spurlock50806fc2014-07-15 10:22:02 -04002526 }
2527 pw.println(" ");
2528 }
John Spurlockcb566aa2014-08-03 22:58:28 -04002529 pw.println(" mUseAttentionLight=" + mUseAttentionLight);
2530 pw.println(" mNotificationPulseEnabled=" + mNotificationPulseEnabled);
Chris Wren6054e612014-11-25 17:16:46 -05002531 pw.println(" mSoundNotificationKey=" + mSoundNotificationKey);
2532 pw.println(" mVibrateNotificationKey=" + mVibrateNotificationKey);
John Spurlockd8afe3c2014-08-01 14:04:07 -04002533 pw.println(" mDisableNotificationEffects=" + mDisableNotificationEffects);
John Spurlock32fe4c62014-10-02 12:16:02 -04002534 pw.println(" mCallState=" + callStateToString(mCallState));
John Spurlock50806fc2014-07-15 10:22:02 -04002535 pw.println(" mSystemReady=" + mSystemReady);
Chris Wren763a9bb2016-05-31 17:14:12 -04002536 pw.println(" mMaxPackageEnqueueRate=" + mMaxPackageEnqueueRate);
John Spurlock50806fc2014-07-15 10:22:02 -04002537 }
2538 pw.println(" mArchive=" + mArchive.toString());
2539 Iterator<StatusBarNotification> iter = mArchive.descendingIterator();
2540 int i=0;
2541 while (iter.hasNext()) {
2542 final StatusBarNotification sbn = iter.next();
2543 if (filter != null && !filter.matches(sbn)) continue;
2544 pw.println(" " + sbn);
2545 if (++i >= 5) {
2546 if (iter.hasNext()) pw.println(" ...");
2547 break;
2548 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08002549 }
2550 }
2551
John Spurlock50806fc2014-07-15 10:22:02 -04002552 if (!zenOnly) {
2553 pw.println("\n Usage Stats:");
2554 mUsageStats.dump(pw, " ", filter);
2555 }
Christoph Studer546bec82014-03-14 12:17:12 +01002556
Julia Reynoldse6b53e62015-07-31 09:25:10 -04002557 if (!filter.filtered || zenOnly) {
John Spurlock25e2d242014-06-27 13:58:23 -04002558 pw.println("\n Zen Mode:");
John Spurlockf3701772015-02-12 13:29:37 -05002559 pw.print(" mInterruptionFilter="); pw.println(mInterruptionFilter);
John Spurlock25e2d242014-06-27 13:58:23 -04002560 mZenModeHelper.dump(pw, " ");
John Spurlock6ae82a72014-07-16 16:23:01 -04002561
2562 pw.println("\n Zen Log:");
2563 ZenLog.dump(pw, " ");
John Spurlock25e2d242014-06-27 13:58:23 -04002564 }
John Spurlocke77bb362014-04-26 10:24:59 -04002565
John Spurlock50806fc2014-07-15 10:22:02 -04002566 if (!zenOnly) {
2567 pw.println("\n Ranking Config:");
2568 mRankingHelper.dump(pw, " ", filter);
Chris Wren54bbef42014-07-09 18:37:56 -04002569
John Spurlock50806fc2014-07-15 10:22:02 -04002570 pw.println("\n Notification listeners:");
2571 mListeners.dump(pw, filter);
John Spurlockd8afe3c2014-08-01 14:04:07 -04002572 pw.print(" mListenerHints: "); pw.println(mListenerHints);
2573 pw.print(" mListenersDisablingEffects: (");
2574 N = mListenersDisablingEffects.size();
John Spurlock1fa865f2014-07-21 14:56:39 -04002575 for (int i = 0; i < N; i++) {
Bryce Lee7219ada2016-04-08 10:54:23 -07002576 final int hint = mListenersDisablingEffects.keyAt(i);
2577 if (i > 0) pw.print(';');
2578 pw.print("hint[" + hint + "]:");
2579
2580 final ArraySet<ManagedServiceInfo> listeners =
2581 mListenersDisablingEffects.valueAt(i);
2582 final int listenerSize = listeners.size();
2583
2584 for (int j = 0; j < listenerSize; j++) {
2585 if (i > 0) pw.print(',');
2586 final ManagedServiceInfo listener = listeners.valueAt(i);
2587 pw.print(listener.component);
2588 }
John Spurlock1fa865f2014-07-21 14:56:39 -04002589 }
2590 pw.println(')');
Chris Wren0efdb882016-03-01 17:17:47 -05002591 pw.println("\n mRankerServicePackageName: " + mRankerServicePackageName);
Chris Wrene0ba7eb2016-03-04 17:30:43 -05002592 pw.println("\n Notification ranker services:");
2593 mRankerServices.dump(pw, filter);
John Spurlock50806fc2014-07-15 10:22:02 -04002594 }
John Spurlock80774932015-05-07 17:38:50 -04002595 pw.println("\n Policy access:");
2596 pw.print(" mPolicyAccess: "); pw.println(mPolicyAccess);
John Spurlocke77bb362014-04-26 10:24:59 -04002597
2598 pw.println("\n Condition providers:");
John Spurlock25e2d242014-06-27 13:58:23 -04002599 mConditionProviders.dump(pw, filter);
Christoph Studer265c1052014-07-23 17:14:33 +02002600
2601 pw.println("\n Group summaries:");
2602 for (Entry<String, NotificationRecord> entry : mSummaryByGroupKey.entrySet()) {
2603 NotificationRecord r = entry.getValue();
2604 pw.println(" " + entry.getKey() + " -> " + r.getKey());
2605 if (mNotificationsByKey.get(r.getKey()) != r) {
2606 pw.println("!!!!!!LEAK: Record not found in mNotificationsByKey.");
Dan Sandlera1770312015-07-10 13:59:29 -04002607 r.dump(pw, " ", getContext(), filter.redact);
Christoph Studer265c1052014-07-23 17:14:33 +02002608 }
2609 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002610 }
2611 }
2612
Adam Lesinski182f73f2013-12-05 16:48:06 -08002613 /**
2614 * The private API only accessible to the system process.
2615 */
2616 private final NotificationManagerInternal mInternalService = new NotificationManagerInternal() {
2617 @Override
Christoph Studer8fd7f1e2014-04-11 17:35:05 -04002618 public void enqueueNotification(String pkg, String opPkg, int callingUid, int callingPid,
Adam Lesinski182f73f2013-12-05 16:48:06 -08002619 String tag, int id, Notification notification, int[] idReceived, int userId) {
Christoph Studer8fd7f1e2014-04-11 17:35:05 -04002620 enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, notification,
Adam Lesinski182f73f2013-12-05 16:48:06 -08002621 idReceived, userId);
2622 }
Christoph Studer365e4c32014-09-18 20:35:36 +02002623
2624 @Override
2625 public void removeForegroundServiceFlagFromNotification(String pkg, int notificationId,
2626 int userId) {
2627 checkCallerIsSystem();
2628 synchronized (mNotificationList) {
2629 int i = indexOfNotificationLocked(pkg, null, notificationId, userId);
2630 if (i < 0) {
2631 Log.d(TAG, "stripForegroundServiceFlag: Could not find notification with "
2632 + "pkg=" + pkg + " / id=" + notificationId + " / userId=" + userId);
2633 return;
2634 }
2635 NotificationRecord r = mNotificationList.get(i);
2636 StatusBarNotification sbn = r.sbn;
2637 // NoMan adds flags FLAG_NO_CLEAR and FLAG_ONGOING_EVENT when it sees
2638 // FLAG_FOREGROUND_SERVICE. Hence it's not enough to remove FLAG_FOREGROUND_SERVICE,
2639 // we have to revert to the flags we received initially *and* force remove
2640 // FLAG_FOREGROUND_SERVICE.
2641 sbn.getNotification().flags =
2642 (r.mOriginalFlags & ~Notification.FLAG_FOREGROUND_SERVICE);
2643 mRankingHelper.sort(mNotificationList);
2644 mListeners.notifyPostedLocked(sbn, sbn /* oldSbn */);
2645 }
2646 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08002647 };
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002648
Christoph Studer8fd7f1e2014-04-11 17:35:05 -04002649 void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,
Scott Greenwald9b05c612013-06-25 23:44:05 -04002650 final int callingPid, final String tag, final int id, final Notification notification,
Adam Lesinski182f73f2013-12-05 16:48:06 -08002651 int[] idOut, int incomingUserId) {
Daniel Sandler0da673f2012-04-11 12:33:16 -04002652 if (DBG) {
Adam Lesinski182f73f2013-12-05 16:48:06 -08002653 Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id
2654 + " notification=" + notification);
Daniel Sandler0da673f2012-04-11 12:33:16 -04002655 }
John Spurlock7340fc82014-04-24 18:50:12 -04002656 checkCallerIsSystemOrSameApp(pkg);
2657 final boolean isSystemNotification = isUidSystem(callingUid) || ("android".equals(pkg));
Justin Koh38156c52014-06-04 13:57:49 -07002658 final boolean isNotificationFromListener = mListeners.isListenerPackage(pkg);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08002659
Scott Greenwald9b05c612013-06-25 23:44:05 -04002660 final int userId = ActivityManager.handleIncomingUser(callingPid,
2661 callingUid, incomingUserId, true, false, "enqueueNotification", pkg);
Jeff Sharkey65c4a2b2012-09-25 17:22:27 -07002662 final UserHandle user = new UserHandle(userId);
Dianne Hackborn41203752012-08-31 14:05:51 -07002663
Julia Reynoldse46bb372016-03-17 11:05:58 -04002664 // Fix the notification as best we can.
2665 try {
Jeff Sharkey012bc7b2016-04-11 16:30:27 -06002666 final ApplicationInfo ai = getContext().getPackageManager().getApplicationInfoAsUser(
2667 pkg, PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
2668 (userId == UserHandle.USER_ALL) ? UserHandle.USER_SYSTEM : userId);
2669 Notification.addFieldsFromContext(ai, userId, notification);
Julia Reynoldse46bb372016-03-17 11:05:58 -04002670 } catch (NameNotFoundException e) {
2671 Slog.e(TAG, "Cannot create a context for sending app", e);
2672 return;
2673 }
2674
Chris Wren888b7a82016-06-17 15:47:19 -04002675 mUsageStats.registerEnqueuedByApp(pkg);
2676
Chris Wrena61f1792016-08-04 11:24:42 -04002677
2678 if (pkg == null || notification == null) {
2679 throw new IllegalArgumentException("null not allowed: pkg=" + pkg
2680 + " id=" + id + " notification=" + notification);
2681 }
2682 final StatusBarNotification n = new StatusBarNotification(
2683 pkg, opPkg, id, tag, callingUid, callingPid, 0, notification,
2684 user);
2685
Joe Onoratobd73d012010-06-04 11:44:54 -07002686 // Limit the number of notifications that any given package except the android
Justin Koh38156c52014-06-04 13:57:49 -07002687 // package or a registered listener can enqueue. Prevents DOS attacks and deals with leaks.
2688 if (!isSystemNotification && !isNotificationFromListener) {
Joe Onoratobd73d012010-06-04 11:44:54 -07002689 synchronized (mNotificationList) {
Chris Wrena61f1792016-08-04 11:24:42 -04002690 if(mNotificationsByKey.get(n.getKey()) != null) {
2691 // this is an update, rate limit updates only
2692 final float appEnqueueRate = mUsageStats.getAppEnqueueRate(pkg);
2693 if (appEnqueueRate > mMaxPackageEnqueueRate) {
2694 mUsageStats.registerOverRateQuota(pkg);
2695 final long now = SystemClock.elapsedRealtime();
2696 if ((now - mLastOverRateLogTime) > MIN_PACKAGE_OVERRATE_LOG_INTERVAL) {
2697 Slog.e(TAG, "Package enqueue rate is " + appEnqueueRate
2698 + ". Shedding events. package=" + pkg);
2699 mLastOverRateLogTime = now;
2700 }
2701 return;
Chris Wrenc8673a82016-05-17 17:11:29 -04002702 }
Chris Wrenc8673a82016-05-17 17:11:29 -04002703 }
2704
Joe Onoratobd73d012010-06-04 11:44:54 -07002705 int count = 0;
2706 final int N = mNotificationList.size();
2707 for (int i=0; i<N; i++) {
2708 final NotificationRecord r = mNotificationList.get(i);
Daniel Sandler4f91efd2013-04-25 16:38:41 -04002709 if (r.sbn.getPackageName().equals(pkg) && r.sbn.getUserId() == userId) {
Vladimir Marko2526f332013-09-11 11:13:55 +01002710 if (r.sbn.getId() == id && TextUtils.equals(r.sbn.getTag(), tag)) {
2711 break; // Allow updating existing notification
2712 }
Joe Onoratobd73d012010-06-04 11:44:54 -07002713 count++;
2714 if (count >= MAX_PACKAGE_NOTIFICATIONS) {
Chris Wrenc8673a82016-05-17 17:11:29 -04002715 mUsageStats.registerOverCountQuota(pkg);
Joe Onoratobd73d012010-06-04 11:44:54 -07002716 Slog.e(TAG, "Package has already posted " + count
2717 + " notifications. Not showing more. package=" + pkg);
2718 return;
2719 }
2720 }
2721 }
2722 }
2723 }
2724
Felipe Lemedd85da62016-06-28 11:29:54 -07002725 // Whitelist pending intents.
2726 if (notification.allPendingIntents != null) {
2727 final int intentCount = notification.allPendingIntents.size();
2728 if (intentCount > 0) {
2729 final ActivityManagerInternal am = LocalServices
2730 .getService(ActivityManagerInternal.class);
2731 final long duration = LocalServices.getService(
2732 DeviceIdleController.LocalService.class).getNotificationWhitelistDuration();
2733 for (int i = 0; i < intentCount; i++) {
2734 PendingIntent pendingIntent = notification.allPendingIntents.valueAt(i);
2735 if (pendingIntent != null) {
2736 am.setPendingIntentWhitelistDuration(pendingIntent.getTarget(), duration);
2737 }
2738 }
2739 }
2740 }
Felipe Lemea1b79bf2016-05-24 13:06:54 -07002741
Chris Wren47633422016-01-22 09:56:59 -05002742 // Sanitize inputs
2743 notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN,
2744 Notification.PRIORITY_MAX);
Daniel Sandler0da673f2012-04-11 12:33:16 -04002745
Chris Wren47633422016-01-22 09:56:59 -05002746 // setup local book-keeping
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04002747 final NotificationRecord r = new NotificationRecord(getContext(), n,
2748 mRankingHelper.getNotificationChannel(pkg, callingUid,
2749 n.getNotification().getNotificationChannel()));
Chris Wren47633422016-01-22 09:56:59 -05002750 mHandler.post(new EnqueueNotificationRunnable(userId, r));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002751
2752 idOut[0] = id;
2753 }
2754
Chris Wren47633422016-01-22 09:56:59 -05002755 private class EnqueueNotificationRunnable implements Runnable {
2756 private final NotificationRecord r;
2757 private final int userId;
2758
2759 EnqueueNotificationRunnable(int userId, NotificationRecord r) {
2760 this.userId = userId;
2761 this.r = r;
2762 };
2763
2764 @Override
2765 public void run() {
2766
2767 synchronized (mNotificationList) {
2768 final StatusBarNotification n = r.sbn;
Chris Wren1ac52a92016-02-24 14:54:52 -05002769 if (DBG) Slog.d(TAG, "EnqueueNotificationRunnable.run for: " + n.getKey());
Chris Wren47633422016-01-22 09:56:59 -05002770 NotificationRecord old = mNotificationsByKey.get(n.getKey());
2771 if (old != null) {
2772 // Retain ranking information from previous record
2773 r.copyRankingInformation(old);
2774 }
2775
2776 final int callingUid = n.getUid();
2777 final int callingPid = n.getInitialPid();
2778 final Notification notification = n.getNotification();
2779 final String pkg = n.getPackageName();
2780 final int id = n.getId();
2781 final String tag = n.getTag();
2782 final boolean isSystemNotification = isUidSystem(callingUid) ||
2783 ("android".equals(pkg));
2784
2785 // Handle grouped notifications and bail out early if we
2786 // can to avoid extracting signals.
2787 handleGroupedNotificationLocked(r, old, callingUid, callingPid);
Chris Wren47633422016-01-22 09:56:59 -05002788
2789 // This conditional is a dirty hack to limit the logging done on
2790 // behalf of the download manager without affecting other apps.
2791 if (!pkg.equals("com.android.providers.downloads")
2792 || Log.isLoggable("DownloadManager", Log.VERBOSE)) {
2793 int enqueueStatus = EVENTLOG_ENQUEUE_STATUS_NEW;
Chris Wren8ab776c2016-04-11 16:48:24 -04002794 if (old != null) {
Chris Wren47633422016-01-22 09:56:59 -05002795 enqueueStatus = EVENTLOG_ENQUEUE_STATUS_UPDATE;
2796 }
2797 EventLogTags.writeNotificationEnqueue(callingUid, callingPid,
2798 pkg, id, tag, userId, notification.toString(),
2799 enqueueStatus);
2800 }
2801
Chris Wren47633422016-01-22 09:56:59 -05002802 mRankingHelper.extractSignals(r);
2803
Andrei Stingaceanu355b2322016-02-12 16:43:51 +00002804 final boolean isPackageSuspended = isPackageSuspendedForUser(pkg, callingUid);
Chris Wren47633422016-01-22 09:56:59 -05002805
Julia Reynoldsef37f282016-02-12 09:11:27 -05002806 // blocked apps
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04002807 if (r.getImportance() == NotificationManager.IMPORTANCE_NONE
2808 || r.getChannel().getImportance() == NotificationManager.IMPORTANCE_NONE
Andrei Stingaceanu355b2322016-02-12 16:43:51 +00002809 || !noteNotificationOp(pkg, callingUid) || isPackageSuspended) {
Chris Wren47633422016-01-22 09:56:59 -05002810 if (!isSystemNotification) {
Andrei Stingaceanu355b2322016-02-12 16:43:51 +00002811 if (isPackageSuspended) {
Andrei Stingaceanu0122f6512016-01-22 15:33:03 +00002812 Slog.e(TAG, "Suppressing notification from package due to package "
2813 + "suspended by administrator.");
2814 mUsageStats.registerSuspendedByAdmin(r);
2815 } else {
2816 Slog.e(TAG, "Suppressing notification from package by user request.");
2817 mUsageStats.registerBlocked(r);
2818 }
Chris Wren47633422016-01-22 09:56:59 -05002819 return;
2820 }
2821 }
2822
Chris Wrene0ba7eb2016-03-04 17:30:43 -05002823 // tell the ranker service about the notification
2824 if (mRankerServices.isEnabled()) {
2825 mRankerServices.onNotificationEnqueued(r);
Chris Wren47633422016-01-22 09:56:59 -05002826 // TODO delay the code below here for 100ms or until there is an answer
2827 }
2828
2829
2830 int index = indexOfNotificationLocked(n.getKey());
2831 if (index < 0) {
2832 mNotificationList.add(r);
2833 mUsageStats.registerPostedByApp(r);
2834 } else {
2835 old = mNotificationList.get(index);
2836 mNotificationList.set(index, r);
2837 mUsageStats.registerUpdatedByApp(r, old);
2838 // Make sure we don't lose the foreground service state.
2839 notification.flags |=
2840 old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;
2841 r.isUpdate = true;
2842 }
2843
2844 mNotificationsByKey.put(n.getKey(), r);
2845
2846 // Ensure if this is a foreground service that the proper additional
2847 // flags are set.
2848 if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
2849 notification.flags |= Notification.FLAG_ONGOING_EVENT
2850 | Notification.FLAG_NO_CLEAR;
2851 }
2852
2853 applyZenModeLocked(r);
2854 mRankingHelper.sort(mNotificationList);
2855
2856 if (notification.getSmallIcon() != null) {
2857 StatusBarNotification oldSbn = (old != null) ? old.sbn : null;
2858 mListeners.notifyPostedLocked(n, oldSbn);
2859 } else {
2860 Slog.e(TAG, "Not posting notification without small icon: " + notification);
2861 if (old != null && !old.isCanceled) {
2862 mListeners.notifyRemovedLocked(n);
2863 }
2864 // ATTENTION: in a future release we will bail out here
2865 // so that we do not play sounds, show lights, etc. for invalid
2866 // notifications
2867 Slog.e(TAG, "WARNING: In a future release this will crash the app: "
2868 + n.getPackageName());
2869 }
2870
2871 buzzBeepBlinkLocked(r);
2872 }
2873 }
2874 }
2875
Christoph Studer265c1052014-07-23 17:14:33 +02002876 /**
2877 * Ensures that grouped notification receive their special treatment.
2878 *
2879 * <p>Cancels group children if the new notification causes a group to lose
2880 * its summary.</p>
2881 *
2882 * <p>Updates mSummaryByGroupKey.</p>
2883 */
2884 private void handleGroupedNotificationLocked(NotificationRecord r, NotificationRecord old,
2885 int callingUid, int callingPid) {
2886 StatusBarNotification sbn = r.sbn;
2887 Notification n = sbn.getNotification();
Selim Cinek5b03ce92016-05-18 15:16:58 -07002888 if (n.isGroupSummary() && !sbn.isAppGroup()) {
2889 // notifications without a group shouldn't be a summary, otherwise autobundling can
2890 // lead to bugs
2891 n.flags &= ~Notification.FLAG_GROUP_SUMMARY;
2892 }
2893
Christoph Studer265c1052014-07-23 17:14:33 +02002894 String group = sbn.getGroupKey();
2895 boolean isSummary = n.isGroupSummary();
2896
2897 Notification oldN = old != null ? old.sbn.getNotification() : null;
2898 String oldGroup = old != null ? old.sbn.getGroupKey() : null;
2899 boolean oldIsSummary = old != null && oldN.isGroupSummary();
2900
2901 if (oldIsSummary) {
2902 NotificationRecord removedSummary = mSummaryByGroupKey.remove(oldGroup);
2903 if (removedSummary != old) {
2904 String removedKey =
2905 removedSummary != null ? removedSummary.getKey() : "<null>";
2906 Slog.w(TAG, "Removed summary didn't match old notification: old=" + old.getKey() +
2907 ", removed=" + removedKey);
2908 }
2909 }
2910 if (isSummary) {
2911 mSummaryByGroupKey.put(group, r);
2912 }
2913
2914 // Clear out group children of the old notification if the update
2915 // causes the group summary to go away. This happens when the old
2916 // notification was a summary and the new one isn't, or when the old
2917 // notification was a summary and its group key changed.
2918 if (oldIsSummary && (!isSummary || !oldGroup.equals(group))) {
2919 cancelGroupChildrenLocked(old, callingUid, callingPid, null,
Selim Cinekce87a8a2016-06-20 15:40:07 -07002920 REASON_GROUP_SUMMARY_CANCELED, false /* sendDelete */);
Christoph Studer265c1052014-07-23 17:14:33 +02002921 }
2922 }
2923
Chris Wren93bb8b82016-03-29 14:35:05 -04002924 @VisibleForTesting
2925 void buzzBeepBlinkLocked(NotificationRecord record) {
Chris Wren82ba59d2015-06-05 11:23:44 -04002926 boolean buzz = false;
2927 boolean beep = false;
2928 boolean blink = false;
2929
Chris Wrena3446562014-06-03 18:11:47 -04002930 final Notification notification = record.sbn.getNotification();
Chris Wren93bb8b82016-03-29 14:35:05 -04002931 final String key = record.getKey();
Chris Wrena3446562014-06-03 18:11:47 -04002932
2933 // Should this notification make noise, vibe, or use the LED?
Julia Reynoldsf0f629f2016-02-25 09:34:04 -05002934 final boolean aboveThreshold = record.getImportance() >= IMPORTANCE_DEFAULT;
Chris Wrence00a232014-11-21 16:25:19 -05002935 final boolean canInterrupt = aboveThreshold && !record.isIntercepted();
Chris Wrena3446562014-06-03 18:11:47 -04002936 if (DBG || record.isIntercepted())
2937 Slog.v(TAG,
2938 "pkg=" + record.sbn.getPackageName() + " canInterrupt=" + canInterrupt +
2939 " intercept=" + record.isIntercepted()
2940 );
2941
2942 final int currentUser;
2943 final long token = Binder.clearCallingIdentity();
2944 try {
2945 currentUser = ActivityManager.getCurrentUser();
2946 } finally {
2947 Binder.restoreCallingIdentity(token);
2948 }
2949
2950 // If we're not supposed to beep, vibrate, etc. then don't.
John Spurlock32fe4c62014-10-02 12:16:02 -04002951 final String disableEffects = disableNotificationEffects(record);
2952 if (disableEffects != null) {
2953 ZenLog.traceDisableEffects(record, disableEffects);
2954 }
Chris Wren93bb8b82016-03-29 14:35:05 -04002955
2956 // Remember if this notification already owns the notification channels.
2957 boolean wasBeep = key != null && key.equals(mSoundNotificationKey);
2958 boolean wasBuzz = key != null && key.equals(mVibrateNotificationKey);
2959
2960 // These are set inside the conditional if the notification is allowed to make noise.
2961 boolean hasValidVibrate = false;
2962 boolean hasValidSound = false;
John Spurlock32fe4c62014-10-02 12:16:02 -04002963 if (disableEffects == null
Chris Wrena3446562014-06-03 18:11:47 -04002964 && (record.getUserId() == UserHandle.USER_ALL ||
2965 record.getUserId() == currentUser ||
2966 mUserProfiles.isCurrentProfile(record.getUserId()))
2967 && canInterrupt
2968 && mSystemReady
2969 && mAudioManager != null) {
2970 if (DBG) Slog.v(TAG, "Interrupting!");
2971
Chris Wrena3446562014-06-03 18:11:47 -04002972 // should we use the default notification sound? (indicated either by
2973 // DEFAULT_SOUND or because notification.sound is pointing at
2974 // Settings.System.NOTIFICATION_SOUND)
2975 final boolean useDefaultSound =
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04002976 (notification.defaults & Notification.DEFAULT_SOUND) != 0
2977 || Settings.System.DEFAULT_NOTIFICATION_URI.equals(notification.sound);
Chris Wrena3446562014-06-03 18:11:47 -04002978
2979 Uri soundUri = null;
Chris Wrena3446562014-06-03 18:11:47 -04002980 if (useDefaultSound) {
2981 soundUri = Settings.System.DEFAULT_NOTIFICATION_URI;
2982
2983 // check to see if the default notification sound is silent
Chris Wrend4054312016-06-24 17:07:40 -04002984 hasValidSound = mSystemNotificationSound != null;
Chris Wrena3446562014-06-03 18:11:47 -04002985 } else if (notification.sound != null) {
2986 soundUri = notification.sound;
2987 hasValidSound = (soundUri != null);
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04002988 } else if (record.getChannel().getDefaultRingtone() != null) {
2989 soundUri = record.getChannel().getDefaultRingtone();
2990 hasValidSound = (soundUri != null);
Chris Wrena3446562014-06-03 18:11:47 -04002991 }
2992
Chris Wrena3446562014-06-03 18:11:47 -04002993 // Does the notification want to specify its own vibration?
2994 final boolean hasCustomVibrate = notification.vibrate != null;
2995
2996 // new in 4.2: if there was supposed to be a sound and we're in vibrate
2997 // mode, and no other vibration is specified, we fall back to vibration
2998 final boolean convertSoundToVibration =
Chris Wren93bb8b82016-03-29 14:35:05 -04002999 !hasCustomVibrate
3000 && hasValidSound
3001 && (mAudioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_VIBRATE);
Chris Wrena3446562014-06-03 18:11:47 -04003002
3003 // The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback.
3004 final boolean useDefaultVibrate =
3005 (notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
3006
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04003007 final boolean hasChannelVibration = record.getChannel().shouldVibrate();
3008
Chris Wren93bb8b82016-03-29 14:35:05 -04003009 hasValidVibrate = useDefaultVibrate || convertSoundToVibration ||
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04003010 hasCustomVibrate || hasChannelVibration;
Chris Wrena3446562014-06-03 18:11:47 -04003011
Chris Wren93bb8b82016-03-29 14:35:05 -04003012 // We can alert, and we're allowed to alert, but if the developer asked us to only do
3013 // it once, and we already have, then don't.
3014 if (!(record.isUpdate
3015 && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0)) {
3016
3017 sendAccessibilityEvent(notification, record.sbn.getPackageName());
3018
3019 if (hasValidSound) {
3020 boolean looping =
3021 (notification.flags & Notification.FLAG_INSISTENT) != 0;
3022 AudioAttributes audioAttributes = audioAttributesForNotification(notification);
3023 mSoundNotificationKey = key;
3024 // do not play notifications if stream volume is 0 (typically because
3025 // ringer mode is silent) or if there is a user of exclusive audio focus
3026 if ((mAudioManager.getStreamVolume(
3027 AudioAttributes.toLegacyStreamType(audioAttributes)) != 0)
3028 && !mAudioManager.isAudioFocusExclusive()) {
3029 final long identity = Binder.clearCallingIdentity();
3030 try {
3031 final IRingtonePlayer player =
3032 mAudioManager.getRingtonePlayer();
3033 if (player != null) {
3034 if (DBG) Slog.v(TAG, "Playing sound " + soundUri
3035 + " with attributes " + audioAttributes);
3036 player.playAsync(soundUri, record.sbn.getUser(), looping,
3037 audioAttributes);
3038 beep = true;
3039 }
3040 } catch (RemoteException e) {
3041 } finally {
3042 Binder.restoreCallingIdentity(identity);
3043 }
Chris Wrena3446562014-06-03 18:11:47 -04003044 }
Chris Wren93bb8b82016-03-29 14:35:05 -04003045 }
Chris Wren93bb8b82016-03-29 14:35:05 -04003046 if (hasValidVibrate && !(mAudioManager.getRingerModeInternal()
3047 == AudioManager.RINGER_MODE_SILENT)) {
3048 mVibrateNotificationKey = key;
3049
3050 if (useDefaultVibrate || convertSoundToVibration) {
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04003051 playNonCustomVibration(record, useDefaultVibrate);
3052 } else if (notification.vibrate != null && notification.vibrate.length > 1) {
Chris Wren93bb8b82016-03-29 14:35:05 -04003053 // If you want your own vibration pattern, you need the VIBRATE
3054 // permission
3055 mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
3056 notification.vibrate,
3057 ((notification.flags & Notification.FLAG_INSISTENT) != 0)
3058 ? 0: -1, audioAttributesForNotification(notification));
3059 buzz = true;
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04003060 } else if (hasChannelVibration) {
3061 playNonCustomVibration(record, useDefaultVibrate);
Chris Wren93bb8b82016-03-29 14:35:05 -04003062 }
Chris Wrena3446562014-06-03 18:11:47 -04003063 }
3064 }
Chris Wren93bb8b82016-03-29 14:35:05 -04003065
3066 }
3067 // If a notification is updated to remove the actively playing sound or vibrate,
3068 // cancel that feedback now
3069 if (wasBeep && !hasValidSound) {
3070 clearSoundLocked();
3071 }
3072 if (wasBuzz && !hasValidVibrate) {
3073 clearVibrateLocked();
Chris Wrena3446562014-06-03 18:11:47 -04003074 }
3075
3076 // light
3077 // release the light
Chris Wren93bb8b82016-03-29 14:35:05 -04003078 boolean wasShowLights = mLights.remove(key);
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04003079 if (shouldShowLights(record) && aboveThreshold
Julia Reynoldsf612869ae2015-11-05 16:48:55 -05003080 && ((record.getSuppressedVisualEffects()
Julia Reynoldsd5607292016-02-05 15:25:58 -05003081 & NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_OFF) == 0)) {
Chris Wren93bb8b82016-03-29 14:35:05 -04003082 mLights.add(key);
Chris Wrena3446562014-06-03 18:11:47 -04003083 updateLightsLocked();
Chris Wren5116a822014-06-04 15:59:50 -04003084 if (mUseAttentionLight) {
3085 mAttentionLight.pulse();
3086 }
Chris Wren82ba59d2015-06-05 11:23:44 -04003087 blink = true;
Chris Wrena3446562014-06-03 18:11:47 -04003088 } else if (wasShowLights) {
3089 updateLightsLocked();
3090 }
Chris Wren82ba59d2015-06-05 11:23:44 -04003091 if (buzz || beep || blink) {
Julia Reynolds61721582016-01-05 08:35:25 -05003092 if (((record.getSuppressedVisualEffects()
Julia Reynoldsd5607292016-02-05 15:25:58 -05003093 & NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_OFF) != 0)) {
Julia Reynolds61721582016-01-05 08:35:25 -05003094 if (DBG) Slog.v(TAG, "Suppressed SystemUI from triggering screen on");
3095 } else {
Chris Wren93bb8b82016-03-29 14:35:05 -04003096 EventLogTags.writeNotificationAlert(key,
Julia Reynolds61721582016-01-05 08:35:25 -05003097 buzz ? 1 : 0, beep ? 1 : 0, blink ? 1 : 0);
3098 mHandler.post(mBuzzBeepBlinked);
3099 }
John Spurlockcad57682014-07-26 17:09:56 -04003100 }
Chris Wrena3446562014-06-03 18:11:47 -04003101 }
3102
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04003103 private boolean shouldShowLights(final NotificationRecord record) {
3104 return record.getChannel().shouldShowLights()
3105 || (record.getNotification().flags & Notification.FLAG_SHOW_LIGHTS) != 0;
3106 }
3107
3108 private boolean playNonCustomVibration(final NotificationRecord record,
3109 boolean useDefaultVibrate) {
3110 // Escalate privileges so we can use the vibrator even if the
3111 // notifying app does not have the VIBRATE permission.
3112 long identity = Binder.clearCallingIdentity();
3113 try {
3114 mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
3115 useDefaultVibrate ? mDefaultVibrationPattern
3116 : mFallbackVibrationPattern,
3117 ((record.getNotification().flags & Notification.FLAG_INSISTENT) != 0)
3118 ? 0: -1, audioAttributesForNotification(record.getNotification()));
3119 return true;
3120 } finally{
3121 Binder.restoreCallingIdentity(identity);
3122 }
3123 }
3124
John Spurlock7b414672014-07-18 13:02:39 -04003125 private static AudioAttributes audioAttributesForNotification(Notification n) {
Marco Nelissen1c066302014-11-18 10:48:12 -08003126 if (n.audioAttributes != null
3127 && !Notification.AUDIO_ATTRIBUTES_DEFAULT.equals(n.audioAttributes)) {
3128 // the audio attributes are set and different from the default, use them
John Spurlockbfa5dc42014-07-28 23:30:45 -04003129 return n.audioAttributes;
Jean-Michel Triviceb79bc2014-09-05 11:09:14 -07003130 } else if (n.audioStreamType >= 0 && n.audioStreamType < AudioSystem.getNumStreamTypes()) {
3131 // the stream type is valid, use it
3132 return new AudioAttributes.Builder()
3133 .setInternalLegacyStreamType(n.audioStreamType)
3134 .build();
3135 } else if (n.audioStreamType == AudioSystem.STREAM_DEFAULT) {
3136 return Notification.AUDIO_ATTRIBUTES_DEFAULT;
3137 } else {
3138 Log.w(TAG, String.format("Invalid stream type: %d", n.audioStreamType));
3139 return Notification.AUDIO_ATTRIBUTES_DEFAULT;
John Spurlockbfa5dc42014-07-28 23:30:45 -04003140 }
John Spurlock7b414672014-07-18 13:02:39 -04003141 }
3142
Adam Lesinski182f73f2013-12-05 16:48:06 -08003143 void showNextToastLocked() {
3144 ToastRecord record = mToastQueue.get(0);
3145 while (record != null) {
3146 if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback);
3147 try {
Svetoslav Ganovaa076532016-08-01 19:16:43 -07003148 record.callback.show(record.token);
Adam Lesinski182f73f2013-12-05 16:48:06 -08003149 scheduleTimeoutLocked(record);
3150 return;
3151 } catch (RemoteException e) {
3152 Slog.w(TAG, "Object died trying to show notification " + record.callback
3153 + " in package " + record.pkg);
3154 // remove it from the list and let the process die
3155 int index = mToastQueue.indexOf(record);
3156 if (index >= 0) {
3157 mToastQueue.remove(index);
3158 }
Svetoslav Ganovaa076532016-08-01 19:16:43 -07003159 keepProcessAliveIfNeededLocked(record.pid);
Adam Lesinski182f73f2013-12-05 16:48:06 -08003160 if (mToastQueue.size() > 0) {
3161 record = mToastQueue.get(0);
3162 } else {
3163 record = null;
3164 }
3165 }
3166 }
3167 }
3168
3169 void cancelToastLocked(int index) {
3170 ToastRecord record = mToastQueue.get(index);
3171 try {
3172 record.callback.hide();
3173 } catch (RemoteException e) {
3174 Slog.w(TAG, "Object died trying to hide notification " + record.callback
3175 + " in package " + record.pkg);
3176 // don't worry about this, we're about to remove it from
3177 // the list anyway
3178 }
Svetoslav Ganovaa076532016-08-01 19:16:43 -07003179
3180 ToastRecord lastToast = mToastQueue.remove(index);
3181 mWindowManagerInternal.removeWindowToken(lastToast.token, true);
3182
3183 keepProcessAliveIfNeededLocked(record.pid);
Adam Lesinski182f73f2013-12-05 16:48:06 -08003184 if (mToastQueue.size() > 0) {
3185 // Show the next one. If the callback fails, this will remove
3186 // it from the list, so don't assume that the list hasn't changed
3187 // after this point.
3188 showNextToastLocked();
3189 }
3190 }
3191
3192 private void scheduleTimeoutLocked(ToastRecord r)
3193 {
3194 mHandler.removeCallbacksAndMessages(r);
3195 Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r);
3196 long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY;
3197 mHandler.sendMessageDelayed(m, delay);
3198 }
3199
3200 private void handleTimeout(ToastRecord record)
3201 {
3202 if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback);
3203 synchronized (mToastQueue) {
3204 int index = indexOfToastLocked(record.pkg, record.callback);
3205 if (index >= 0) {
3206 cancelToastLocked(index);
3207 }
3208 }
3209 }
3210
3211 // lock on mToastQueue
3212 int indexOfToastLocked(String pkg, ITransientNotification callback)
3213 {
3214 IBinder cbak = callback.asBinder();
3215 ArrayList<ToastRecord> list = mToastQueue;
3216 int len = list.size();
3217 for (int i=0; i<len; i++) {
3218 ToastRecord r = list.get(i);
3219 if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) {
3220 return i;
3221 }
3222 }
3223 return -1;
3224 }
3225
3226 // lock on mToastQueue
Svetoslav Ganovaa076532016-08-01 19:16:43 -07003227 void keepProcessAliveIfNeededLocked(int pid)
Adam Lesinski182f73f2013-12-05 16:48:06 -08003228 {
3229 int toastCount = 0; // toasts from this pid
3230 ArrayList<ToastRecord> list = mToastQueue;
3231 int N = list.size();
3232 for (int i=0; i<N; i++) {
3233 ToastRecord r = list.get(i);
3234 if (r.pid == pid) {
3235 toastCount++;
3236 }
3237 }
3238 try {
3239 mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0);
3240 } catch (RemoteException e) {
3241 // Shouldn't happen.
3242 }
3243 }
3244
Chris Wrenf9536642014-04-17 10:01:54 -04003245 private void handleRankingReconsideration(Message message) {
Chris Wren470c1ac2014-05-21 15:28:10 -04003246 if (!(message.obj instanceof RankingReconsideration)) return;
3247 RankingReconsideration recon = (RankingReconsideration) message.obj;
3248 recon.run();
Chris Wren333a61c2014-05-28 16:40:57 -04003249 boolean changed;
Chris Wren470c1ac2014-05-21 15:28:10 -04003250 synchronized (mNotificationList) {
3251 final NotificationRecord record = mNotificationsByKey.get(recon.getKey());
3252 if (record == null) {
3253 return;
Chris Wrenf9536642014-04-17 10:01:54 -04003254 }
Chris Wren333a61c2014-05-28 16:40:57 -04003255 int indexBefore = findNotificationRecordIndexLocked(record);
3256 boolean interceptBefore = record.isIntercepted();
Chris Wren3ad4e3a2014-09-02 17:23:51 -04003257 int visibilityBefore = record.getPackageVisibilityOverride();
Chris Wren470c1ac2014-05-21 15:28:10 -04003258 recon.applyChangesLocked(record);
Chris Wren333a61c2014-05-28 16:40:57 -04003259 applyZenModeLocked(record);
Chris Wren54bbef42014-07-09 18:37:56 -04003260 mRankingHelper.sort(mNotificationList);
Chris Wren333a61c2014-05-28 16:40:57 -04003261 int indexAfter = findNotificationRecordIndexLocked(record);
3262 boolean interceptAfter = record.isIntercepted();
Chris Wren3ad4e3a2014-09-02 17:23:51 -04003263 int visibilityAfter = record.getPackageVisibilityOverride();
3264 changed = indexBefore != indexAfter || interceptBefore != interceptAfter
3265 || visibilityBefore != visibilityAfter;
Chris Wrena3446562014-06-03 18:11:47 -04003266 if (interceptBefore && !interceptAfter) {
3267 buzzBeepBlinkLocked(record);
3268 }
Chris Wrenf9536642014-04-17 10:01:54 -04003269 }
Chris Wren333a61c2014-05-28 16:40:57 -04003270 if (changed) {
Chris Wren470c1ac2014-05-21 15:28:10 -04003271 scheduleSendRankingUpdate();
3272 }
3273 }
3274
Chris Wren51017d02015-12-15 15:34:46 -05003275 private void handleRankingSort() {
Chris Wren54bbef42014-07-09 18:37:56 -04003276 synchronized (mNotificationList) {
3277 final int N = mNotificationList.size();
3278 ArrayList<String> orderBefore = new ArrayList<String>(N);
Julia Reynoldse46bb372016-03-17 11:05:58 -04003279 ArrayList<String> groupOverrideBefore = new ArrayList<>(N);
Chris Wren3ad4e3a2014-09-02 17:23:51 -04003280 int[] visibilities = new int[N];
Julia Reynoldse46bb372016-03-17 11:05:58 -04003281 int[] importances = new int[N];
Chris Wren54bbef42014-07-09 18:37:56 -04003282 for (int i = 0; i < N; i++) {
3283 final NotificationRecord r = mNotificationList.get(i);
3284 orderBefore.add(r.getKey());
Julia Reynoldse46bb372016-03-17 11:05:58 -04003285 groupOverrideBefore.add(r.sbn.getGroupKey());
Chris Wren3ad4e3a2014-09-02 17:23:51 -04003286 visibilities[i] = r.getPackageVisibilityOverride();
Julia Reynolds69766692016-02-01 15:35:08 -05003287 importances[i] = r.getImportance();
Chris Wren54bbef42014-07-09 18:37:56 -04003288 mRankingHelper.extractSignals(r);
3289 }
Chris Wren19a02b02015-12-22 10:34:22 -05003290 mRankingHelper.sort(mNotificationList);
Chris Wren54bbef42014-07-09 18:37:56 -04003291 for (int i = 0; i < N; i++) {
Chris Wren3ad4e3a2014-09-02 17:23:51 -04003292 final NotificationRecord r = mNotificationList.get(i);
3293 if (!orderBefore.get(i).equals(r.getKey())
Julia Reynolds69766692016-02-01 15:35:08 -05003294 || visibilities[i] != r.getPackageVisibilityOverride()
Julia Reynoldse46bb372016-03-17 11:05:58 -04003295 || importances[i] != r.getImportance()
3296 || !groupOverrideBefore.get(i).equals(r.sbn.getGroupKey())) {
Chris Wren54bbef42014-07-09 18:37:56 -04003297 scheduleSendRankingUpdate();
3298 return;
3299 }
3300 }
3301 }
3302 }
3303
Julia Reynoldsc6b371b2016-06-14 08:31:03 -04003304 private void recordCallerLocked(NotificationRecord record) {
3305 if (mZenModeHelper.isCall(record)) {
3306 mZenModeHelper.recordCaller(record);
3307 }
3308 }
3309
Christoph Studerd5092bc2014-07-03 17:47:58 +02003310 // let zen mode evaluate this record
Chris Wren333a61c2014-05-28 16:40:57 -04003311 private void applyZenModeLocked(NotificationRecord record) {
Christoph Studerd5092bc2014-07-03 17:47:58 +02003312 record.setIntercepted(mZenModeHelper.shouldIntercept(record));
Julia Reynoldsf612869ae2015-11-05 16:48:55 -05003313 if (record.isIntercepted()) {
Julia Reynoldsd5607292016-02-05 15:25:58 -05003314 int suppressed = (mZenModeHelper.shouldSuppressWhenScreenOff()
3315 ? SUPPRESSED_EFFECT_SCREEN_OFF : 0)
3316 | (mZenModeHelper.shouldSuppressWhenScreenOn()
3317 ? SUPPRESSED_EFFECT_SCREEN_ON : 0);
Julia Reynoldsf612869ae2015-11-05 16:48:55 -05003318 record.setSuppressedVisualEffects(suppressed);
3319 }
Chris Wren333a61c2014-05-28 16:40:57 -04003320 }
3321
Chris Wren470c1ac2014-05-21 15:28:10 -04003322 // lock on mNotificationList
3323 private int findNotificationRecordIndexLocked(NotificationRecord target) {
Chris Wren54bbef42014-07-09 18:37:56 -04003324 return mRankingHelper.indexOf(mNotificationList, target);
Chris Wrenf9536642014-04-17 10:01:54 -04003325 }
3326
3327 private void scheduleSendRankingUpdate() {
Chris Wren52020492016-04-06 11:12:02 -04003328 if (!mHandler.hasMessages(MESSAGE_SEND_RANKING_UPDATE)) {
3329 Message m = Message.obtain(mHandler, MESSAGE_SEND_RANKING_UPDATE);
3330 mHandler.sendMessage(m);
3331 }
Chris Wrenf9536642014-04-17 10:01:54 -04003332 }
3333
3334 private void handleSendRankingUpdate() {
3335 synchronized (mNotificationList) {
Chris Wren333a61c2014-05-28 16:40:57 -04003336 mListeners.notifyRankingUpdateLocked();
Chris Wrenf9536642014-04-17 10:01:54 -04003337 }
3338 }
3339
John Spurlockd8afe3c2014-08-01 14:04:07 -04003340 private void scheduleListenerHintsChanged(int state) {
3341 mHandler.removeMessages(MESSAGE_LISTENER_HINTS_CHANGED);
3342 mHandler.obtainMessage(MESSAGE_LISTENER_HINTS_CHANGED, state, 0).sendToTarget();
John Spurlock1fa865f2014-07-21 14:56:39 -04003343 }
3344
Christoph Studer85a384b2014-08-27 20:16:15 +02003345 private void scheduleInterruptionFilterChanged(int listenerInterruptionFilter) {
3346 mHandler.removeMessages(MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED);
3347 mHandler.obtainMessage(
3348 MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED,
3349 listenerInterruptionFilter,
3350 0).sendToTarget();
3351 }
3352
John Spurlockd8afe3c2014-08-01 14:04:07 -04003353 private void handleListenerHintsChanged(int hints) {
John Spurlock1fa865f2014-07-21 14:56:39 -04003354 synchronized (mNotificationList) {
John Spurlockd8afe3c2014-08-01 14:04:07 -04003355 mListeners.notifyListenerHintsChangedLocked(hints);
John Spurlock1fa865f2014-07-21 14:56:39 -04003356 }
3357 }
3358
Christoph Studer85a384b2014-08-27 20:16:15 +02003359 private void handleListenerInterruptionFilterChanged(int interruptionFilter) {
3360 synchronized (mNotificationList) {
3361 mListeners.notifyInterruptionFilterChanged(interruptionFilter);
3362 }
3363 }
3364
Adam Lesinski182f73f2013-12-05 16:48:06 -08003365 private final class WorkerHandler extends Handler
3366 {
3367 @Override
3368 public void handleMessage(Message msg)
3369 {
3370 switch (msg.what)
3371 {
3372 case MESSAGE_TIMEOUT:
3373 handleTimeout((ToastRecord)msg.obj);
3374 break;
John Spurlock056c5192014-04-20 21:52:01 -04003375 case MESSAGE_SAVE_POLICY_FILE:
3376 handleSavePolicyFile();
3377 break;
Chris Wrenf9536642014-04-17 10:01:54 -04003378 case MESSAGE_SEND_RANKING_UPDATE:
3379 handleSendRankingUpdate();
3380 break;
John Spurlockd8afe3c2014-08-01 14:04:07 -04003381 case MESSAGE_LISTENER_HINTS_CHANGED:
3382 handleListenerHintsChanged(msg.arg1);
John Spurlock1fa865f2014-07-21 14:56:39 -04003383 break;
Christoph Studer85a384b2014-08-27 20:16:15 +02003384 case MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED:
3385 handleListenerInterruptionFilterChanged(msg.arg1);
3386 break;
Chris Wrenf9536642014-04-17 10:01:54 -04003387 }
3388 }
3389
3390 }
3391
Chris Wren51017d02015-12-15 15:34:46 -05003392 private final class RankingHandlerWorker extends Handler implements RankingHandler
Chris Wrenf9536642014-04-17 10:01:54 -04003393 {
Chris Wren51017d02015-12-15 15:34:46 -05003394 public RankingHandlerWorker(Looper looper) {
Chris Wrenf9536642014-04-17 10:01:54 -04003395 super(looper);
3396 }
3397
3398 @Override
3399 public void handleMessage(Message msg) {
3400 switch (msg.what) {
3401 case MESSAGE_RECONSIDER_RANKING:
3402 handleRankingReconsideration(msg);
3403 break;
Chris Wren51017d02015-12-15 15:34:46 -05003404 case MESSAGE_RANKING_SORT:
3405 handleRankingSort();
Chris Wren54bbef42014-07-09 18:37:56 -04003406 break;
Adam Lesinski182f73f2013-12-05 16:48:06 -08003407 }
3408 }
Chris Wren51017d02015-12-15 15:34:46 -05003409
3410 public void requestSort() {
3411 removeMessages(MESSAGE_RANKING_SORT);
3412 sendEmptyMessage(MESSAGE_RANKING_SORT);
3413 }
3414
3415 public void requestReconsideration(RankingReconsideration recon) {
3416 Message m = Message.obtain(this,
3417 NotificationManagerService.MESSAGE_RECONSIDER_RANKING, recon);
3418 long delay = recon.getDelay(TimeUnit.MILLISECONDS);
3419 sendMessageDelayed(m, delay);
3420 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08003421 }
3422
Adam Lesinski182f73f2013-12-05 16:48:06 -08003423 // Notifications
3424 // ============================================================================
3425 static int clamp(int x, int low, int high) {
3426 return (x < low) ? low : ((x > high) ? high : x);
3427 }
3428
3429 void sendAccessibilityEvent(Notification notification, CharSequence packageName) {
3430 AccessibilityManager manager = AccessibilityManager.getInstance(getContext());
svetoslavganov75986cf2009-05-14 22:28:01 -07003431 if (!manager.isEnabled()) {
3432 return;
3433 }
3434
3435 AccessibilityEvent event =
3436 AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
3437 event.setPackageName(packageName);
3438 event.setClassName(Notification.class.getName());
3439 event.setParcelableData(notification);
3440 CharSequence tickerText = notification.tickerText;
3441 if (!TextUtils.isEmpty(tickerText)) {
3442 event.getText().add(tickerText);
3443 }
3444
3445 manager.sendAccessibilityEvent(event);
3446 }
3447
Christoph Studer546bec82014-03-14 12:17:12 +01003448 private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason) {
Julia Reynoldsc6b371b2016-06-14 08:31:03 -04003449
3450 // Record caller.
3451 recordCallerLocked(r);
3452
Joe Onorato46439ce2010-11-19 13:56:21 -08003453 // tell the app
3454 if (sendDelete) {
Daniel Sandlerfde19b12013-01-17 00:21:05 -05003455 if (r.getNotification().deleteIntent != null) {
Joe Onorato46439ce2010-11-19 13:56:21 -08003456 try {
Daniel Sandlerfde19b12013-01-17 00:21:05 -05003457 r.getNotification().deleteIntent.send();
Joe Onorato46439ce2010-11-19 13:56:21 -08003458 } catch (PendingIntent.CanceledException ex) {
3459 // do nothing - there's no relevant way to recover, and
3460 // no reason to let this propagate
Daniel Sandler4f91efd2013-04-25 16:38:41 -04003461 Slog.w(TAG, "canceled PendingIntent for " + r.sbn.getPackageName(), ex);
Joe Onorato46439ce2010-11-19 13:56:21 -08003462 }
3463 }
3464 }
3465
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003466 // status bar
Dan Sandlerd63f9322015-05-06 15:18:49 -04003467 if (r.getNotification().getSmallIcon() != null) {
Christoph Studer71f18fd2014-05-20 17:02:04 +02003468 r.isCanceled = true;
Chris Wren333a61c2014-05-28 16:40:57 -04003469 mListeners.notifyRemovedLocked(r.sbn);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003470 }
3471
Chris Wren6054e612014-11-25 17:16:46 -05003472 final String canceledKey = r.getKey();
3473
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003474 // sound
Chris Wren6054e612014-11-25 17:16:46 -05003475 if (canceledKey.equals(mSoundNotificationKey)) {
3476 mSoundNotificationKey = null;
Jeff Sharkey098d5802012-04-26 17:30:34 -07003477 final long identity = Binder.clearCallingIdentity();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003478 try {
Adam Lesinski182f73f2013-12-05 16:48:06 -08003479 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
Jeff Sharkey098d5802012-04-26 17:30:34 -07003480 if (player != null) {
3481 player.stopAsync();
3482 }
3483 } catch (RemoteException e) {
3484 } finally {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003485 Binder.restoreCallingIdentity(identity);
3486 }
3487 }
3488
3489 // vibrate
Chris Wren6054e612014-11-25 17:16:46 -05003490 if (canceledKey.equals(mVibrateNotificationKey)) {
3491 mVibrateNotificationKey = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003492 long identity = Binder.clearCallingIdentity();
3493 try {
3494 mVibrator.cancel();
3495 }
3496 finally {
3497 Binder.restoreCallingIdentity(identity);
3498 }
3499 }
3500
3501 // light
Chris Wren6054e612014-11-25 17:16:46 -05003502 mLights.remove(canceledKey);
Daniel Sandler23d7c702013-03-07 16:32:06 -05003503
Christoph Studer546bec82014-03-14 12:17:12 +01003504 // Record usage stats
Julia Reynoldse46bb372016-03-17 11:05:58 -04003505 // TODO: add unbundling stats?
Christoph Studer546bec82014-03-14 12:17:12 +01003506 switch (reason) {
3507 case REASON_DELEGATE_CANCEL:
3508 case REASON_DELEGATE_CANCEL_ALL:
3509 case REASON_LISTENER_CANCEL:
3510 case REASON_LISTENER_CANCEL_ALL:
3511 mUsageStats.registerDismissedByUser(r);
3512 break;
Chris Wren9fa689f2015-11-20 16:44:53 -05003513 case REASON_APP_CANCEL:
3514 case REASON_APP_CANCEL_ALL:
Christoph Studer546bec82014-03-14 12:17:12 +01003515 mUsageStats.registerRemovedByApp(r);
3516 break;
Christoph Studer546bec82014-03-14 12:17:12 +01003517 }
3518
Christoph Studercef37cf2014-07-25 14:18:17 +02003519 mNotificationsByKey.remove(r.sbn.getKey());
Christoph Studer265c1052014-07-23 17:14:33 +02003520 String groupKey = r.getGroupKey();
3521 NotificationRecord groupSummary = mSummaryByGroupKey.get(groupKey);
3522 if (groupSummary != null && groupSummary.getKey().equals(r.getKey())) {
3523 mSummaryByGroupKey.remove(groupKey);
3524 }
Julia Reynoldseae43fb2016-05-09 12:42:58 -04003525 final ArrayMap<String, String> summaries = mAutobundledSummaries.get(r.sbn.getUserId());
3526 if (summaries != null && r.sbn.getKey().equals(summaries.get(r.sbn.getPackageName()))) {
3527 summaries.remove(r.sbn.getPackageName());
Julia Reynoldse46bb372016-03-17 11:05:58 -04003528 }
Christoph Studercef37cf2014-07-25 14:18:17 +02003529
Daniel Sandler23d7c702013-03-07 16:32:06 -05003530 // Save it for users of getHistoricalNotifications()
3531 mArchive.record(r.sbn);
Christoph Studer81e5b5f2014-10-22 17:19:56 +02003532
Chris Wren6650e572015-05-15 17:19:25 -04003533 final long now = System.currentTimeMillis();
Chris Wrene6ddb8a2015-05-27 15:21:00 -04003534 EventLogTags.writeNotificationCanceled(canceledKey, reason,
3535 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003536 }
3537
3538 /**
Dianne Hackbornd8a43f62009-08-17 23:33:56 -07003539 * Cancels a notification ONLY if it has all of the {@code mustHaveFlags}
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003540 * and none of the {@code mustNotHaveFlags}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003541 */
John Spurlocke6a7d932014-03-13 12:29:00 -04003542 void cancelNotification(final int callingUid, final int callingPid,
3543 final String pkg, final String tag, final int id,
Svetoslav Ganov835835e2013-08-04 20:17:52 -07003544 final int mustHaveFlags, final int mustNotHaveFlags, final boolean sendDelete,
John Spurlock7340fc82014-04-24 18:50:12 -04003545 final int userId, final int reason, final ManagedServiceInfo listener) {
Svetoslav Ganov835835e2013-08-04 20:17:52 -07003546 // In enqueueNotificationInternal notifications are added by scheduling the
3547 // work on the worker handler. Hence, we also schedule the cancel on this
3548 // handler to avoid a scenario where an add notification call followed by a
3549 // remove notification call ends up in not removing the notification.
3550 mHandler.post(new Runnable() {
3551 @Override
3552 public void run() {
Christoph Studere4ef156b2014-07-04 18:41:57 +02003553 String listenerName = listener == null ? null : listener.component.toShortString();
Chris Wrenbddb5bc2015-03-04 08:47:46 -08003554 if (DBG) EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, id, tag,
3555 userId, mustHaveFlags, mustNotHaveFlags, reason, listenerName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003556
Svetoslav Ganov835835e2013-08-04 20:17:52 -07003557 synchronized (mNotificationList) {
3558 int index = indexOfNotificationLocked(pkg, tag, id, userId);
3559 if (index >= 0) {
3560 NotificationRecord r = mNotificationList.get(index);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003561
Christoph Studer546bec82014-03-14 12:17:12 +01003562 // Ideally we'd do this in the caller of this method. However, that would
3563 // require the caller to also find the notification.
3564 if (reason == REASON_DELEGATE_CLICK) {
3565 mUsageStats.registerClickedByUser(r);
3566 }
3567
Svetoslav Ganov835835e2013-08-04 20:17:52 -07003568 if ((r.getNotification().flags & mustHaveFlags) != mustHaveFlags) {
3569 return;
3570 }
3571 if ((r.getNotification().flags & mustNotHaveFlags) != 0) {
3572 return;
3573 }
3574
3575 mNotificationList.remove(index);
3576
Selim Cinek3f19f602016-05-02 18:01:56 -07003577 cancelNotificationLocked(r, sendDelete, reason);
Christoph Studer265c1052014-07-23 17:14:33 +02003578 cancelGroupChildrenLocked(r, callingUid, callingPid, listenerName,
Selim Cinekce87a8a2016-06-20 15:40:07 -07003579 REASON_GROUP_SUMMARY_CANCELED, sendDelete);
Svetoslav Ganov835835e2013-08-04 20:17:52 -07003580 updateLightsLocked();
3581 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003582 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003583 }
Svetoslav Ganov835835e2013-08-04 20:17:52 -07003584 });
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003585 }
3586
3587 /**
Daniel Sandler321e9c52012-10-12 10:59:26 -07003588 * Determine whether the userId applies to the notification in question, either because
3589 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard).
3590 */
3591 private boolean notificationMatchesUserId(NotificationRecord r, int userId) {
3592 return
3593 // looking for USER_ALL notifications? match everything
3594 userId == UserHandle.USER_ALL
3595 // a notification sent to USER_ALL matches any query
Daniel Sandlerfde19b12013-01-17 00:21:05 -05003596 || r.getUserId() == UserHandle.USER_ALL
Daniel Sandler321e9c52012-10-12 10:59:26 -07003597 // an exact user match
Daniel Sandlerfde19b12013-01-17 00:21:05 -05003598 || r.getUserId() == userId;
Daniel Sandler321e9c52012-10-12 10:59:26 -07003599 }
3600
3601 /**
Kenny Guy3a7c4a52014-03-03 18:24:03 +00003602 * Determine whether the userId applies to the notification in question, either because
3603 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard) or
Kenny Guy2a764942014-04-02 13:29:20 +01003604 * because it matches one of the users profiles.
Kenny Guy3a7c4a52014-03-03 18:24:03 +00003605 */
Kenny Guy2a764942014-04-02 13:29:20 +01003606 private boolean notificationMatchesCurrentProfiles(NotificationRecord r, int userId) {
Kenny Guya263e4e2014-03-03 18:24:03 +00003607 return notificationMatchesUserId(r, userId)
John Spurlockb408e8e2014-04-23 21:12:45 -04003608 || mUserProfiles.isCurrentProfile(r.getUserId());
Kenny Guy3a7c4a52014-03-03 18:24:03 +00003609 }
3610
3611 /**
Julia Reynoldsef37f282016-02-12 09:11:27 -05003612 * Cancels all notifications from a given package that have all of the
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003613 * {@code mustHaveFlags}.
3614 */
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04003615 boolean cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, String channelId,
3616 int mustHaveFlags, int mustNotHaveFlags, boolean doit, int userId, int reason,
John Spurlock7340fc82014-04-24 18:50:12 -04003617 ManagedServiceInfo listener) {
Christoph Studere4ef156b2014-07-04 18:41:57 +02003618 String listenerName = listener == null ? null : listener.component.toShortString();
John Spurlocke6a7d932014-03-13 12:29:00 -04003619 EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
3620 pkg, userId, mustHaveFlags, mustNotHaveFlags, reason,
Christoph Studere4ef156b2014-07-04 18:41:57 +02003621 listenerName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003622
3623 synchronized (mNotificationList) {
3624 final int N = mNotificationList.size();
Christoph Studere4ef156b2014-07-04 18:41:57 +02003625 ArrayList<NotificationRecord> canceledNotifications = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003626 for (int i = N-1; i >= 0; --i) {
3627 NotificationRecord r = mNotificationList.get(i);
Daniel Sandler321e9c52012-10-12 10:59:26 -07003628 if (!notificationMatchesUserId(r, userId)) {
Dianne Hackborn41203752012-08-31 14:05:51 -07003629 continue;
3630 }
Amith Yamasani5ec00e92012-11-07 16:58:30 -08003631 // Don't remove notifications to all, if there's no package name specified
Daniel Sandlerfde19b12013-01-17 00:21:05 -05003632 if (r.getUserId() == UserHandle.USER_ALL && pkg == null) {
Amith Yamasani5ec00e92012-11-07 16:58:30 -08003633 continue;
3634 }
Daniel Sandlerfde19b12013-01-17 00:21:05 -05003635 if ((r.getFlags() & mustHaveFlags) != mustHaveFlags) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003636 continue;
3637 }
Daniel Sandlerfde19b12013-01-17 00:21:05 -05003638 if ((r.getFlags() & mustNotHaveFlags) != 0) {
Dianne Hackbornd8a43f62009-08-17 23:33:56 -07003639 continue;
3640 }
Daniel Sandler4f91efd2013-04-25 16:38:41 -04003641 if (pkg != null && !r.sbn.getPackageName().equals(pkg)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003642 continue;
3643 }
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04003644 if (channelId == null || !channelId.equals(r.getChannel().getId())) {
3645 continue;
3646 }
Christoph Studere4ef156b2014-07-04 18:41:57 +02003647 if (canceledNotifications == null) {
3648 canceledNotifications = new ArrayList<>();
3649 }
3650 canceledNotifications.add(r);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08003651 if (!doit) {
3652 return true;
3653 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003654 mNotificationList.remove(i);
Christoph Studer546bec82014-03-14 12:17:12 +01003655 cancelNotificationLocked(r, false, reason);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003656 }
Christoph Studere4ef156b2014-07-04 18:41:57 +02003657 if (doit && canceledNotifications != null) {
3658 final int M = canceledNotifications.size();
3659 for (int i = 0; i < M; i++) {
3660 cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid,
Selim Cinekce87a8a2016-06-20 15:40:07 -07003661 listenerName, REASON_GROUP_SUMMARY_CANCELED, false /* sendDelete */);
Christoph Studere4ef156b2014-07-04 18:41:57 +02003662 }
3663 }
3664 if (canceledNotifications != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003665 updateLightsLocked();
3666 }
Christoph Studere4ef156b2014-07-04 18:41:57 +02003667 return canceledNotifications != null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003668 }
3669 }
3670
Adam Lesinski350159c2014-03-27 11:15:11 -07003671 void cancelAllLocked(int callingUid, int callingPid, int userId, int reason,
John Spurlock7340fc82014-04-24 18:50:12 -04003672 ManagedServiceInfo listener, boolean includeCurrentProfiles) {
Christoph Studere4ef156b2014-07-04 18:41:57 +02003673 String listenerName = listener == null ? null : listener.component.toShortString();
John Spurlocke6a7d932014-03-13 12:29:00 -04003674 EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
Christoph Studere4ef156b2014-07-04 18:41:57 +02003675 null, userId, 0, 0, reason, listenerName);
Christoph Studer546bec82014-03-14 12:17:12 +01003676
Christoph Studere4ef156b2014-07-04 18:41:57 +02003677 ArrayList<NotificationRecord> canceledNotifications = null;
Adam Lesinskie8240262014-03-26 16:01:00 -07003678 final int N = mNotificationList.size();
3679 for (int i=N-1; i>=0; i--) {
3680 NotificationRecord r = mNotificationList.get(i);
Kenny Guya263e4e2014-03-03 18:24:03 +00003681 if (includeCurrentProfiles) {
3682 if (!notificationMatchesCurrentProfiles(r, userId)) {
3683 continue;
3684 }
3685 } else {
3686 if (!notificationMatchesUserId(r, userId)) {
3687 continue;
3688 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003689 }
3690
Adam Lesinskie8240262014-03-26 16:01:00 -07003691 if ((r.getFlags() & (Notification.FLAG_ONGOING_EVENT
3692 | Notification.FLAG_NO_CLEAR)) == 0) {
3693 mNotificationList.remove(i);
Christoph Studer546bec82014-03-14 12:17:12 +01003694 cancelNotificationLocked(r, true, reason);
Christoph Studere4ef156b2014-07-04 18:41:57 +02003695 // Make a note so we can cancel children later.
3696 if (canceledNotifications == null) {
3697 canceledNotifications = new ArrayList<>();
3698 }
3699 canceledNotifications.add(r);
Adam Lesinskie8240262014-03-26 16:01:00 -07003700 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003701 }
Christoph Studere4ef156b2014-07-04 18:41:57 +02003702 int M = canceledNotifications != null ? canceledNotifications.size() : 0;
3703 for (int i = 0; i < M; i++) {
3704 cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid,
Selim Cinekce87a8a2016-06-20 15:40:07 -07003705 listenerName, REASON_GROUP_SUMMARY_CANCELED, false /* sendDelete */);
Christoph Studere4ef156b2014-07-04 18:41:57 +02003706 }
Adam Lesinskie8240262014-03-26 16:01:00 -07003707 updateLightsLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003708 }
3709
Christoph Studere4ef156b2014-07-04 18:41:57 +02003710 // Warning: The caller is responsible for invoking updateLightsLocked().
3711 private void cancelGroupChildrenLocked(NotificationRecord r, int callingUid, int callingPid,
Selim Cinekce87a8a2016-06-20 15:40:07 -07003712 String listenerName, int reason, boolean sendDelete) {
Christoph Studere4ef156b2014-07-04 18:41:57 +02003713 Notification n = r.getNotification();
Christoph Studer3f31f5d2014-07-31 16:55:32 +02003714 if (!n.isGroupSummary()) {
Christoph Studere4ef156b2014-07-04 18:41:57 +02003715 return;
3716 }
3717
3718 String pkg = r.sbn.getPackageName();
3719 int userId = r.getUserId();
3720
3721 if (pkg == null) {
3722 if (DBG) Log.e(TAG, "No package for group summary: " + r.getKey());
3723 return;
3724 }
3725
3726 final int N = mNotificationList.size();
3727 for (int i = N - 1; i >= 0; i--) {
3728 NotificationRecord childR = mNotificationList.get(i);
Christoph Studere4ef156b2014-07-04 18:41:57 +02003729 StatusBarNotification childSbn = childR.sbn;
Julia Reynoldse46bb372016-03-17 11:05:58 -04003730 if ((childSbn.isGroup() && !childSbn.getNotification().isGroupSummary()) &&
Christoph Studerc44caa92014-08-22 19:16:00 +02003731 childR.getGroupKey().equals(r.getGroupKey())) {
Christoph Studer265c1052014-07-23 17:14:33 +02003732 EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, childSbn.getId(),
3733 childSbn.getTag(), userId, 0, 0, reason, listenerName);
Christoph Studere4ef156b2014-07-04 18:41:57 +02003734 mNotificationList.remove(i);
Selim Cinekce87a8a2016-06-20 15:40:07 -07003735 cancelNotificationLocked(childR, sendDelete, reason);
Christoph Studere4ef156b2014-07-04 18:41:57 +02003736 }
3737 }
3738 }
3739
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003740 // lock on mNotificationList
Adam Lesinski182f73f2013-12-05 16:48:06 -08003741 void updateLightsLocked()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003742 {
The Android Open Source Project10592532009-03-18 17:39:46 -07003743 // handle notification lights
Chris Wren6054e612014-11-25 17:16:46 -05003744 NotificationRecord ledNotification = null;
3745 while (ledNotification == null && !mLights.isEmpty()) {
3746 final String owner = mLights.get(mLights.size() - 1);
3747 ledNotification = mNotificationsByKey.get(owner);
3748 if (ledNotification == null) {
3749 Slog.wtfStack(TAG, "LED Notification does not exist: " + owner);
3750 mLights.remove(owner);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003751 }
3752 }
Mike Lockwoodc22404a2009-12-02 11:15:02 -05003753
Mike Lockwood63b5ad92011-08-30 09:55:30 -04003754 // Don't flash while we are in a call or screen is on
Chris Wren6054e612014-11-25 17:16:46 -05003755 if (ledNotification == null || mInCall || mScreenOn) {
Mike Lockwood3cb67a32009-11-27 14:25:58 -05003756 mNotificationLight.turnOff();
Wei Liu97e56662016-03-04 10:52:33 -08003757 if (mStatusBar != null) {
3758 mStatusBar.notificationLightOff();
3759 }
The Android Open Source Project10592532009-03-18 17:39:46 -07003760 } else {
Chris Wren6054e612014-11-25 17:16:46 -05003761 final Notification ledno = ledNotification.sbn.getNotification();
Daniel Sandlerfde19b12013-01-17 00:21:05 -05003762 int ledARGB = ledno.ledARGB;
3763 int ledOnMS = ledno.ledOnMS;
3764 int ledOffMS = ledno.ledOffMS;
Julia Reynoldsb5e44b72016-08-16 15:00:25 -04003765 if ((ledno.defaults & Notification.DEFAULT_LIGHTS) != 0
3766 || (ledno.flags & Notification.FLAG_SHOW_LIGHTS) == 0) {
Mike Lockwood670f9322010-01-20 12:13:36 -05003767 ledARGB = mDefaultNotificationColor;
3768 ledOnMS = mDefaultNotificationLedOn;
3769 ledOffMS = mDefaultNotificationLedOff;
3770 }
3771 if (mNotificationPulseEnabled) {
3772 // pulse repeatedly
Adam Lesinski182f73f2013-12-05 16:48:06 -08003773 mNotificationLight.setFlashing(ledARGB, Light.LIGHT_FLASH_TIMED,
Mike Lockwood670f9322010-01-20 12:13:36 -05003774 ledOnMS, ledOffMS);
Mike Lockwood670f9322010-01-20 12:13:36 -05003775 }
Wei Liu97e56662016-03-04 10:52:33 -08003776 if (mStatusBar != null) {
3777 // let SystemUI make an independent decision
3778 mStatusBar.notificationLightPulse(ledARGB, ledOnMS, ledOffMS);
3779 }
The Android Open Source Project10592532009-03-18 17:39:46 -07003780 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003781 }
3782
3783 // lock on mNotificationList
Adam Lesinski182f73f2013-12-05 16:48:06 -08003784 int indexOfNotificationLocked(String pkg, String tag, int id, int userId)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003785 {
3786 ArrayList<NotificationRecord> list = mNotificationList;
3787 final int len = list.size();
3788 for (int i=0; i<len; i++) {
3789 NotificationRecord r = list.get(i);
Vladimir Marko2526f332013-09-11 11:13:55 +01003790 if (notificationMatchesUserId(r, userId) && r.sbn.getId() == id &&
3791 TextUtils.equals(r.sbn.getTag(), tag) && r.sbn.getPackageName().equals(pkg)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003792 return i;
3793 }
3794 }
3795 return -1;
3796 }
3797
Christoph Studer71f18fd2014-05-20 17:02:04 +02003798 // lock on mNotificationList
3799 int indexOfNotificationLocked(String key) {
Christoph Studerc5115552014-06-12 20:22:31 +02003800 final int N = mNotificationList.size();
3801 for (int i = 0; i < N; i++) {
3802 if (key.equals(mNotificationList.get(i).getKey())) {
3803 return i;
3804 }
Christoph Studer71f18fd2014-05-20 17:02:04 +02003805 }
Christoph Studerc5115552014-06-12 20:22:31 +02003806 return -1;
Christoph Studer71f18fd2014-05-20 17:02:04 +02003807 }
3808
Mike Lockwoodc22404a2009-12-02 11:15:02 -05003809 private void updateNotificationPulse() {
3810 synchronized (mNotificationList) {
3811 updateLightsLocked();
3812 }
3813 }
John Spurlocke677d712014-02-13 12:52:19 -05003814
John Spurlock7340fc82014-04-24 18:50:12 -04003815 private static boolean isUidSystem(int uid) {
3816 final int appid = UserHandle.getAppId(uid);
3817 return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0);
3818 }
John Spurlockb408e8e2014-04-23 21:12:45 -04003819
John Spurlock7340fc82014-04-24 18:50:12 -04003820 private static boolean isCallerSystem() {
3821 return isUidSystem(Binder.getCallingUid());
3822 }
3823
3824 private static void checkCallerIsSystem() {
3825 if (isCallerSystem()) {
3826 return;
3827 }
3828 throw new SecurityException("Disallowed call for uid " + Binder.getCallingUid());
3829 }
3830
3831 private static void checkCallerIsSystemOrSameApp(String pkg) {
3832 if (isCallerSystem()) {
3833 return;
3834 }
Julia Reynolds0cd1b782016-06-29 08:43:00 -04003835 checkCallerIsSameApp(pkg);
3836 }
3837
3838 private static void checkCallerIsSameApp(String pkg) {
John Spurlock7340fc82014-04-24 18:50:12 -04003839 final int uid = Binder.getCallingUid();
3840 try {
3841 ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo(
3842 pkg, 0, UserHandle.getCallingUserId());
Dan Sandler09afc2e2014-07-18 14:29:20 -04003843 if (ai == null) {
3844 throw new SecurityException("Unknown package " + pkg);
3845 }
John Spurlock7340fc82014-04-24 18:50:12 -04003846 if (!UserHandle.isSameApp(ai.uid, uid)) {
3847 throw new SecurityException("Calling uid " + uid + " gave package"
3848 + pkg + " which is owned by uid " + ai.uid);
3849 }
3850 } catch (RemoteException re) {
3851 throw new SecurityException("Unknown package " + pkg + "\n" + re);
3852 }
3853 }
3854
John Spurlock32fe4c62014-10-02 12:16:02 -04003855 private static String callStateToString(int state) {
3856 switch (state) {
3857 case TelephonyManager.CALL_STATE_IDLE: return "CALL_STATE_IDLE";
3858 case TelephonyManager.CALL_STATE_RINGING: return "CALL_STATE_RINGING";
3859 case TelephonyManager.CALL_STATE_OFFHOOK: return "CALL_STATE_OFFHOOK";
3860 default: return "CALL_STATE_UNKNOWN_" + state;
3861 }
3862 }
3863
3864 private void listenForCallState() {
3865 TelephonyManager.from(getContext()).listen(new PhoneStateListener() {
3866 @Override
3867 public void onCallStateChanged(int state, String incomingNumber) {
3868 if (mCallState == state) return;
3869 if (DBG) Slog.d(TAG, "Call state changed: " + callStateToString(state));
3870 mCallState = state;
3871 }
3872 }, PhoneStateListener.LISTEN_CALL_STATE);
3873 }
3874
Christoph Studer05ad4822014-05-16 14:16:03 +02003875 /**
3876 * Generates a NotificationRankingUpdate from 'sbns', considering only
3877 * notifications visible to the given listener.
Chris Wren333a61c2014-05-28 16:40:57 -04003878 *
3879 * <p>Caller must hold a lock on mNotificationList.</p>
Christoph Studer05ad4822014-05-16 14:16:03 +02003880 */
Chris Wren333a61c2014-05-28 16:40:57 -04003881 private NotificationRankingUpdate makeRankingUpdateLocked(ManagedServiceInfo info) {
Chris Wren333a61c2014-05-28 16:40:57 -04003882 final int N = mNotificationList.size();
3883 ArrayList<String> keys = new ArrayList<String>(N);
Christoph Studer1d599da2014-06-12 15:25:59 +02003884 ArrayList<String> interceptedKeys = new ArrayList<String>(N);
Chris Wrenbdf33762015-12-04 15:50:51 -05003885 ArrayList<Integer> importance = new ArrayList<>(N);
Julia Reynoldse46bb372016-03-17 11:05:58 -04003886 Bundle overrideGroupKeys = new Bundle();
Chris Wren3ad4e3a2014-09-02 17:23:51 -04003887 Bundle visibilityOverrides = new Bundle();
Julia Reynoldsf612869ae2015-11-05 16:48:55 -05003888 Bundle suppressedVisualEffects = new Bundle();
Chris Wrenbdf33762015-12-04 15:50:51 -05003889 Bundle explanation = new Bundle();
Chris Wren333a61c2014-05-28 16:40:57 -04003890 for (int i = 0; i < N; i++) {
3891 NotificationRecord record = mNotificationList.get(i);
Christoph Studercef37cf2014-07-25 14:18:17 +02003892 if (!isVisibleToListener(record.sbn, info)) {
Christoph Studer05ad4822014-05-16 14:16:03 +02003893 continue;
3894 }
Chris Wrenbdf33762015-12-04 15:50:51 -05003895 final String key = record.sbn.getKey();
3896 keys.add(key);
3897 importance.add(record.getImportance());
3898 if (record.getImportanceExplanation() != null) {
3899 explanation.putCharSequence(key, record.getImportanceExplanation());
3900 }
Chris Wren333a61c2014-05-28 16:40:57 -04003901 if (record.isIntercepted()) {
Chris Wrenbdf33762015-12-04 15:50:51 -05003902 interceptedKeys.add(key);
Julia Reynoldsf612869ae2015-11-05 16:48:55 -05003903
Christoph Studer05ad4822014-05-16 14:16:03 +02003904 }
Chris Wrenbdf33762015-12-04 15:50:51 -05003905 suppressedVisualEffects.putInt(key, record.getSuppressedVisualEffects());
Chris Wren3ad4e3a2014-09-02 17:23:51 -04003906 if (record.getPackageVisibilityOverride()
3907 != NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE) {
Chris Wrenbdf33762015-12-04 15:50:51 -05003908 visibilityOverrides.putInt(key, record.getPackageVisibilityOverride());
Chris Wren3ad4e3a2014-09-02 17:23:51 -04003909 }
Julia Reynoldse46bb372016-03-17 11:05:58 -04003910 overrideGroupKeys.putString(key, record.sbn.getOverrideGroupKey());
Christoph Studer05ad4822014-05-16 14:16:03 +02003911 }
Chris Wrenbdf33762015-12-04 15:50:51 -05003912 final int M = keys.size();
3913 String[] keysAr = keys.toArray(new String[M]);
Christoph Studer1d599da2014-06-12 15:25:59 +02003914 String[] interceptedKeysAr = interceptedKeys.toArray(new String[interceptedKeys.size()]);
Chris Wrenbdf33762015-12-04 15:50:51 -05003915 int[] importanceAr = new int[M];
3916 for (int i = 0; i < M; i++) {
3917 importanceAr[i] = importance.get(i);
3918 }
Chris Wren3ad4e3a2014-09-02 17:23:51 -04003919 return new NotificationRankingUpdate(keysAr, interceptedKeysAr, visibilityOverrides,
Julia Reynoldse46bb372016-03-17 11:05:58 -04003920 suppressedVisualEffects, importanceAr, explanation, overrideGroupKeys);
Christoph Studer05ad4822014-05-16 14:16:03 +02003921 }
3922
Christoph Studercef37cf2014-07-25 14:18:17 +02003923 private boolean isVisibleToListener(StatusBarNotification sbn, ManagedServiceInfo listener) {
3924 if (!listener.enabledAndUserMatches(sbn.getUserId())) {
3925 return false;
3926 }
Justin Koh8d11a5a2014-08-04 18:29:49 -07003927 // TODO: remove this for older listeners.
Christoph Studercef37cf2014-07-25 14:18:17 +02003928 return true;
3929 }
3930
Andrei Stingaceanu355b2322016-02-12 16:43:51 +00003931 private boolean isPackageSuspendedForUser(String pkg, int uid) {
Andrei Stingaceanu0122f6512016-01-22 15:33:03 +00003932 int userId = UserHandle.getUserId(uid);
Andrei Stingaceanu0122f6512016-01-22 15:33:03 +00003933 try {
Andrei Stingaceanu355b2322016-02-12 16:43:51 +00003934 return AppGlobals.getPackageManager().isPackageSuspendedForUser(pkg, userId);
Andrei Stingaceanu0122f6512016-01-22 15:33:03 +00003935 } catch (RemoteException re) {
3936 throw new SecurityException("Could not talk to package manager service");
Andrei Stingaceanuefc4a342016-03-22 14:43:01 +00003937 } catch (IllegalArgumentException ex) {
3938 // Package not found.
3939 return false;
Andrei Stingaceanu0122f6512016-01-22 15:33:03 +00003940 }
Andrei Stingaceanu0122f6512016-01-22 15:33:03 +00003941 }
3942
Chris Wren47633422016-01-22 09:56:59 -05003943 private class TrimCache {
3944 StatusBarNotification heavy;
3945 StatusBarNotification sbnClone;
3946 StatusBarNotification sbnCloneLight;
3947
3948 TrimCache(StatusBarNotification sbn) {
3949 heavy = sbn;
3950 }
3951
3952 StatusBarNotification ForListener(ManagedServiceInfo info) {
3953 if (mListeners.getOnNotificationPostedTrim(info) == TRIM_LIGHT) {
3954 if (sbnCloneLight == null) {
3955 sbnCloneLight = heavy.cloneLight();
3956 }
3957 return sbnCloneLight;
3958 } else {
3959 if (sbnClone == null) {
3960 sbnClone = heavy.clone();
3961 }
3962 return sbnClone;
3963 }
3964 }
3965 }
3966
Chris Wren0efdb882016-03-01 17:17:47 -05003967 public class NotificationRankers extends ManagedServices {
Chris Wren51017d02015-12-15 15:34:46 -05003968
Chris Wren0efdb882016-03-01 17:17:47 -05003969 public NotificationRankers() {
Chris Wren51017d02015-12-15 15:34:46 -05003970 super(getContext(), mHandler, mNotificationList, mUserProfiles);
3971 }
3972
3973 @Override
3974 protected Config getConfig() {
3975 Config c = new Config();
Chris Wrene0ba7eb2016-03-04 17:30:43 -05003976 c.caption = "notification ranker service";
3977 c.serviceInterface = NotificationRankerService.SERVICE_INTERFACE;
3978 c.secureSettingName = null;
3979 c.bindPermission = Manifest.permission.BIND_NOTIFICATION_RANKER_SERVICE;
Chris Wren51017d02015-12-15 15:34:46 -05003980 c.settingsAction = Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS;
Chris Wrene0ba7eb2016-03-04 17:30:43 -05003981 c.clientLabel = R.string.notification_ranker_binding_label;
Chris Wren51017d02015-12-15 15:34:46 -05003982 return c;
3983 }
3984
3985 @Override
3986 protected IInterface asInterface(IBinder binder) {
3987 return INotificationListener.Stub.asInterface(binder);
3988 }
3989
3990 @Override
3991 protected boolean checkType(IInterface service) {
3992 return service instanceof INotificationListener;
3993 }
3994
3995 @Override
3996 protected void onServiceAdded(ManagedServiceInfo info) {
3997 mListeners.registerGuestService(info);
3998 }
3999
4000 @Override
4001 protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
4002 mListeners.unregisterService(removed.service, removed.userid);
4003 }
Chris Wren47633422016-01-22 09:56:59 -05004004
4005 public void onNotificationEnqueued(final NotificationRecord r) {
4006 final StatusBarNotification sbn = r.sbn;
4007 TrimCache trimCache = new TrimCache(sbn);
4008
Chris Wrene0ba7eb2016-03-04 17:30:43 -05004009 // mServices is the list inside ManagedServices of all the rankers,
Chris Wren47633422016-01-22 09:56:59 -05004010 // There should be only one, but it's a list, so while we enforce
4011 // singularity elsewhere, we keep it general here, to avoid surprises.
Chris Wren0efdb882016-03-01 17:17:47 -05004012 for (final ManagedServiceInfo info : NotificationRankers.this.mServices) {
Chris Wren47633422016-01-22 09:56:59 -05004013 boolean sbnVisible = isVisibleToListener(sbn, info);
4014 if (!sbnVisible) {
4015 continue;
4016 }
4017
4018 final int importance = r.getImportance();
4019 final boolean fromUser = r.isImportanceFromUser();
4020 final StatusBarNotification sbnToPost = trimCache.ForListener(info);
Chris Wrene0ba7eb2016-03-04 17:30:43 -05004021 mHandler.post(new Runnable() {
Chris Wren47633422016-01-22 09:56:59 -05004022 @Override
4023 public void run() {
4024 notifyEnqueued(info, sbnToPost, importance, fromUser);
4025 }
4026 });
4027 }
4028 }
4029
4030 private void notifyEnqueued(final ManagedServiceInfo info,
4031 final StatusBarNotification sbn, int importance, boolean fromUser) {
Chris Wrene0ba7eb2016-03-04 17:30:43 -05004032 final INotificationListener ranker = (INotificationListener) info.service;
Chris Wren47633422016-01-22 09:56:59 -05004033 StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
4034 try {
Chris Wrene0ba7eb2016-03-04 17:30:43 -05004035 ranker.onNotificationEnqueued(sbnHolder, importance, fromUser);
Chris Wren47633422016-01-22 09:56:59 -05004036 } catch (RemoteException ex) {
Chris Wrene0ba7eb2016-03-04 17:30:43 -05004037 Log.e(TAG, "unable to notify ranker (enqueued): " + ranker, ex);
Chris Wren47633422016-01-22 09:56:59 -05004038 }
4039 }
4040
4041 public boolean isEnabled() {
4042 return !mServices.isEmpty();
4043 }
Julia Reynolds1c9bd422016-03-15 09:25:56 -04004044
4045 @Override
4046 public void onUserSwitched(int user) {
Julia Reynoldsad929572016-05-26 13:49:17 -04004047 synchronized (mNotificationList) {
Julia Reynolds00d9d9f2016-06-21 07:47:22 -04004048 int i = mServices.size()-1;
4049 while (i --> 0) {
4050 final ManagedServiceInfo info = mServices.get(i);
Julia Reynoldsad929572016-05-26 13:49:17 -04004051 unregisterService(info.service, info.userid);
4052 }
Julia Reynolds1c9bd422016-03-15 09:25:56 -04004053 }
4054 registerRanker();
4055 }
4056
4057 @Override
Julia Reynolds6434eb22016-08-08 17:19:26 -04004058 public void onPackagesChanged(boolean removingPackage, String[] pkgList) {
4059 if (DEBUG) Slog.d(TAG, "onPackagesChanged removingPackage=" + removingPackage
Julia Reynolds1c9bd422016-03-15 09:25:56 -04004060 + " pkgList=" + (pkgList == null ? null : Arrays.asList(pkgList)));
Chris Wren88d2f6d2016-03-17 15:47:09 -04004061 if (mRankerServicePackageName == null) {
4062 return;
4063 }
Julia Reynolds1c9bd422016-03-15 09:25:56 -04004064
Julia Reynolds6434eb22016-08-08 17:19:26 -04004065 if (pkgList != null && (pkgList.length > 0) && !removingPackage) {
Julia Reynolds1c9bd422016-03-15 09:25:56 -04004066 for (String pkgName : pkgList) {
4067 if (mRankerServicePackageName.equals(pkgName)) {
4068 registerRanker();
4069 }
4070 }
4071 }
4072 }
4073
4074 protected void registerRanker() {
4075 // Find the updatable ranker and register it.
Chris Wren88d2f6d2016-03-17 15:47:09 -04004076 if (mRankerServicePackageName == null) {
4077 Slog.w(TAG, "could not start ranker service: no package specified!");
4078 return;
4079 }
Julia Reynolds1c9bd422016-03-15 09:25:56 -04004080 Set<ComponentName> rankerComponents = queryPackageForServices(
Julia Reynoldsa3dc1fb2016-04-12 10:25:12 -04004081 mRankerServicePackageName, UserHandle.USER_SYSTEM);
Julia Reynolds1c9bd422016-03-15 09:25:56 -04004082 Iterator<ComponentName> iterator = rankerComponents.iterator();
4083 if (iterator.hasNext()) {
4084 ComponentName rankerComponent = iterator.next();
4085 if (iterator.hasNext()) {
4086 Slog.e(TAG, "found multiple ranker services:" + rankerComponents);
4087 } else {
4088 registerSystemService(rankerComponent, UserHandle.USER_SYSTEM);
4089 }
4090 } else {
4091 Slog.w(TAG, "could not start ranker service: none found");
4092 }
4093 }
Chris Wren51017d02015-12-15 15:34:46 -05004094 }
4095
John Spurlock7340fc82014-04-24 18:50:12 -04004096 public class NotificationListeners extends ManagedServices {
4097
Christoph Studerb82bc782014-08-20 14:29:43 +02004098 private final ArraySet<ManagedServiceInfo> mLightTrimListeners = new ArraySet<>();
4099
John Spurlock7340fc82014-04-24 18:50:12 -04004100 public NotificationListeners() {
4101 super(getContext(), mHandler, mNotificationList, mUserProfiles);
4102 }
4103
4104 @Override
4105 protected Config getConfig() {
4106 Config c = new Config();
4107 c.caption = "notification listener";
4108 c.serviceInterface = NotificationListenerService.SERVICE_INTERFACE;
4109 c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_LISTENERS;
4110 c.bindPermission = android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE;
4111 c.settingsAction = Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS;
4112 c.clientLabel = R.string.notification_listener_binding_label;
4113 return c;
4114 }
4115
4116 @Override
4117 protected IInterface asInterface(IBinder binder) {
4118 return INotificationListener.Stub.asInterface(binder);
4119 }
4120
4121 @Override
Chris Wren51017d02015-12-15 15:34:46 -05004122 protected boolean checkType(IInterface service) {
4123 return service instanceof INotificationListener;
4124 }
4125
4126 @Override
John Spurlock3b98b3f2014-05-01 09:08:48 -04004127 public void onServiceAdded(ManagedServiceInfo info) {
4128 final INotificationListener listener = (INotificationListener) info.service;
Chris Wren333a61c2014-05-28 16:40:57 -04004129 final NotificationRankingUpdate update;
Christoph Studer05ad4822014-05-16 14:16:03 +02004130 synchronized (mNotificationList) {
Chris Wren333a61c2014-05-28 16:40:57 -04004131 update = makeRankingUpdateLocked(info);
Christoph Studer05ad4822014-05-16 14:16:03 +02004132 }
John Spurlock7340fc82014-04-24 18:50:12 -04004133 try {
Chris Wren333a61c2014-05-28 16:40:57 -04004134 listener.onListenerConnected(update);
John Spurlock7340fc82014-04-24 18:50:12 -04004135 } catch (RemoteException e) {
4136 // we tried
4137 }
4138 }
4139
John Spurlock1fa865f2014-07-21 14:56:39 -04004140 @Override
4141 protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
Bryce Lee7219ada2016-04-08 10:54:23 -07004142 if (removeDisabledHints(removed)) {
John Spurlockd8afe3c2014-08-01 14:04:07 -04004143 updateListenerHintsLocked();
Christoph Studer0d6ef4b2014-12-02 15:00:48 +01004144 updateEffectsSuppressorLocked();
John Spurlock1fa865f2014-07-21 14:56:39 -04004145 }
Christoph Studerb82bc782014-08-20 14:29:43 +02004146 mLightTrimListeners.remove(removed);
4147 }
4148
4149 public void setOnNotificationPostedTrimLocked(ManagedServiceInfo info, int trim) {
4150 if (trim == TRIM_LIGHT) {
4151 mLightTrimListeners.add(info);
4152 } else {
4153 mLightTrimListeners.remove(info);
4154 }
4155 }
4156
4157 public int getOnNotificationPostedTrim(ManagedServiceInfo info) {
4158 return mLightTrimListeners.contains(info) ? TRIM_LIGHT : TRIM_FULL;
John Spurlock1fa865f2014-07-21 14:56:39 -04004159 }
4160
John Spurlock7340fc82014-04-24 18:50:12 -04004161 /**
4162 * asynchronously notify all listeners about a new notification
Christoph Studercef37cf2014-07-25 14:18:17 +02004163 *
4164 * <p>
4165 * Also takes care of removing a notification that has been visible to a listener before,
4166 * but isn't anymore.
John Spurlock7340fc82014-04-24 18:50:12 -04004167 */
Christoph Studercef37cf2014-07-25 14:18:17 +02004168 public void notifyPostedLocked(StatusBarNotification sbn, StatusBarNotification oldSbn) {
Christoph Studerb82bc782014-08-20 14:29:43 +02004169 // Lazily initialized snapshots of the notification.
Chris Wren47633422016-01-22 09:56:59 -05004170 TrimCache trimCache = new TrimCache(sbn);
Christoph Studerb82bc782014-08-20 14:29:43 +02004171
John Spurlock7340fc82014-04-24 18:50:12 -04004172 for (final ManagedServiceInfo info : mServices) {
Christoph Studercef37cf2014-07-25 14:18:17 +02004173 boolean sbnVisible = isVisibleToListener(sbn, info);
4174 boolean oldSbnVisible = oldSbn != null ? isVisibleToListener(oldSbn, info) : false;
4175 // This notification hasn't been and still isn't visible -> ignore.
4176 if (!oldSbnVisible && !sbnVisible) {
Christoph Studer05ad4822014-05-16 14:16:03 +02004177 continue;
Chris Wrenf9536642014-04-17 10:01:54 -04004178 }
Chris Wren333a61c2014-05-28 16:40:57 -04004179 final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
Christoph Studercef37cf2014-07-25 14:18:17 +02004180
4181 // This notification became invisible -> remove the old one.
4182 if (oldSbnVisible && !sbnVisible) {
4183 final StatusBarNotification oldSbnLightClone = oldSbn.cloneLight();
4184 mHandler.post(new Runnable() {
4185 @Override
4186 public void run() {
4187 notifyRemoved(info, oldSbnLightClone, update);
4188 }
4189 });
Christoph Studer05ad4822014-05-16 14:16:03 +02004190 continue;
4191 }
Christoph Studercef37cf2014-07-25 14:18:17 +02004192
Chris Wren47633422016-01-22 09:56:59 -05004193 final StatusBarNotification sbnToPost = trimCache.ForListener(info);
Christoph Studer05ad4822014-05-16 14:16:03 +02004194 mHandler.post(new Runnable() {
4195 @Override
4196 public void run() {
Christoph Studerb82bc782014-08-20 14:29:43 +02004197 notifyPosted(info, sbnToPost, update);
Christoph Studer05ad4822014-05-16 14:16:03 +02004198 }
4199 });
Kenny Guy3a7c4a52014-03-03 18:24:03 +00004200 }
4201 }
Kenny Guy3a7c4a52014-03-03 18:24:03 +00004202
John Spurlock7340fc82014-04-24 18:50:12 -04004203 /**
4204 * asynchronously notify all listeners about a removed notification
4205 */
Chris Wren333a61c2014-05-28 16:40:57 -04004206 public void notifyRemovedLocked(StatusBarNotification sbn) {
John Spurlock7340fc82014-04-24 18:50:12 -04004207 // make a copy in case changes are made to the underlying Notification object
4208 // NOTE: this copy is lightweight: it doesn't include heavyweight parts of the
4209 // notification
4210 final StatusBarNotification sbnLight = sbn.cloneLight();
Chris Wrenf9536642014-04-17 10:01:54 -04004211 for (final ManagedServiceInfo info : mServices) {
Christoph Studercef37cf2014-07-25 14:18:17 +02004212 if (!isVisibleToListener(sbn, info)) {
Christoph Studer05ad4822014-05-16 14:16:03 +02004213 continue;
Chris Wrenf9536642014-04-17 10:01:54 -04004214 }
Chris Wren333a61c2014-05-28 16:40:57 -04004215 final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
Christoph Studer05ad4822014-05-16 14:16:03 +02004216 mHandler.post(new Runnable() {
4217 @Override
4218 public void run() {
Christoph Studercef37cf2014-07-25 14:18:17 +02004219 notifyRemoved(info, sbnLight, update);
Christoph Studer05ad4822014-05-16 14:16:03 +02004220 }
4221 });
Chris Wrenf9536642014-04-17 10:01:54 -04004222 }
4223 }
4224
4225 /**
4226 * asynchronously notify all listeners about a reordering of notifications
Chris Wrenf9536642014-04-17 10:01:54 -04004227 */
Chris Wren333a61c2014-05-28 16:40:57 -04004228 public void notifyRankingUpdateLocked() {
Chris Wrenf9536642014-04-17 10:01:54 -04004229 for (final ManagedServiceInfo serviceInfo : mServices) {
Christoph Studer05ad4822014-05-16 14:16:03 +02004230 if (!serviceInfo.isEnabledForCurrentProfiles()) {
4231 continue;
4232 }
Christoph Studercef37cf2014-07-25 14:18:17 +02004233 final NotificationRankingUpdate update = makeRankingUpdateLocked(serviceInfo);
John Spurlock7340fc82014-04-24 18:50:12 -04004234 mHandler.post(new Runnable() {
4235 @Override
4236 public void run() {
Chris Wren333a61c2014-05-28 16:40:57 -04004237 notifyRankingUpdate(serviceInfo, update);
John Spurlock7340fc82014-04-24 18:50:12 -04004238 }
4239 });
Kenny Guya263e4e2014-03-03 18:24:03 +00004240 }
Kenny Guya263e4e2014-03-03 18:24:03 +00004241 }
Kenny Guya263e4e2014-03-03 18:24:03 +00004242
John Spurlockd8afe3c2014-08-01 14:04:07 -04004243 public void notifyListenerHintsChangedLocked(final int hints) {
John Spurlock1fa865f2014-07-21 14:56:39 -04004244 for (final ManagedServiceInfo serviceInfo : mServices) {
4245 if (!serviceInfo.isEnabledForCurrentProfiles()) {
4246 continue;
4247 }
4248 mHandler.post(new Runnable() {
4249 @Override
4250 public void run() {
John Spurlockd8afe3c2014-08-01 14:04:07 -04004251 notifyListenerHintsChanged(serviceInfo, hints);
John Spurlock1fa865f2014-07-21 14:56:39 -04004252 }
4253 });
4254 }
4255 }
4256
Christoph Studer85a384b2014-08-27 20:16:15 +02004257 public void notifyInterruptionFilterChanged(final int interruptionFilter) {
4258 for (final ManagedServiceInfo serviceInfo : mServices) {
4259 if (!serviceInfo.isEnabledForCurrentProfiles()) {
4260 continue;
4261 }
4262 mHandler.post(new Runnable() {
4263 @Override
4264 public void run() {
4265 notifyInterruptionFilterChanged(serviceInfo, interruptionFilter);
4266 }
4267 });
4268 }
4269 }
4270
Christoph Studercef37cf2014-07-25 14:18:17 +02004271 private void notifyPosted(final ManagedServiceInfo info,
Christoph Studer05ad4822014-05-16 14:16:03 +02004272 final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) {
John Spurlock7340fc82014-04-24 18:50:12 -04004273 final INotificationListener listener = (INotificationListener)info.service;
Griff Hazen84a00ea2014-09-02 17:10:47 -07004274 StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
John Spurlock7340fc82014-04-24 18:50:12 -04004275 try {
Griff Hazen84a00ea2014-09-02 17:10:47 -07004276 listener.onNotificationPosted(sbnHolder, rankingUpdate);
John Spurlock7340fc82014-04-24 18:50:12 -04004277 } catch (RemoteException ex) {
4278 Log.e(TAG, "unable to notify listener (posted): " + listener, ex);
4279 }
4280 }
4281
Christoph Studercef37cf2014-07-25 14:18:17 +02004282 private void notifyRemoved(ManagedServiceInfo info, StatusBarNotification sbn,
Christoph Studer05ad4822014-05-16 14:16:03 +02004283 NotificationRankingUpdate rankingUpdate) {
John Spurlock7340fc82014-04-24 18:50:12 -04004284 if (!info.enabledAndUserMatches(sbn.getUserId())) {
4285 return;
4286 }
Christoph Studer05ad4822014-05-16 14:16:03 +02004287 final INotificationListener listener = (INotificationListener) info.service;
Griff Hazen84a00ea2014-09-02 17:10:47 -07004288 StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
John Spurlock7340fc82014-04-24 18:50:12 -04004289 try {
Griff Hazen84a00ea2014-09-02 17:10:47 -07004290 listener.onNotificationRemoved(sbnHolder, rankingUpdate);
John Spurlock7340fc82014-04-24 18:50:12 -04004291 } catch (RemoteException ex) {
4292 Log.e(TAG, "unable to notify listener (removed): " + listener, ex);
John Spurlockb408e8e2014-04-23 21:12:45 -04004293 }
Kenny Guya263e4e2014-03-03 18:24:03 +00004294 }
Chris Wrenf9536642014-04-17 10:01:54 -04004295
Christoph Studer05ad4822014-05-16 14:16:03 +02004296 private void notifyRankingUpdate(ManagedServiceInfo info,
4297 NotificationRankingUpdate rankingUpdate) {
4298 final INotificationListener listener = (INotificationListener) info.service;
Chris Wrenf9536642014-04-17 10:01:54 -04004299 try {
Christoph Studer05ad4822014-05-16 14:16:03 +02004300 listener.onNotificationRankingUpdate(rankingUpdate);
Chris Wrenf9536642014-04-17 10:01:54 -04004301 } catch (RemoteException ex) {
4302 Log.e(TAG, "unable to notify listener (ranking update): " + listener, ex);
4303 }
4304 }
John Spurlock1fa865f2014-07-21 14:56:39 -04004305
John Spurlockd8afe3c2014-08-01 14:04:07 -04004306 private void notifyListenerHintsChanged(ManagedServiceInfo info, int hints) {
John Spurlock1fa865f2014-07-21 14:56:39 -04004307 final INotificationListener listener = (INotificationListener) info.service;
4308 try {
John Spurlockd8afe3c2014-08-01 14:04:07 -04004309 listener.onListenerHintsChanged(hints);
John Spurlock1fa865f2014-07-21 14:56:39 -04004310 } catch (RemoteException ex) {
John Spurlockd8afe3c2014-08-01 14:04:07 -04004311 Log.e(TAG, "unable to notify listener (listener hints): " + listener, ex);
John Spurlock1fa865f2014-07-21 14:56:39 -04004312 }
4313 }
Justin Koh38156c52014-06-04 13:57:49 -07004314
Christoph Studer85a384b2014-08-27 20:16:15 +02004315 private void notifyInterruptionFilterChanged(ManagedServiceInfo info,
4316 int interruptionFilter) {
4317 final INotificationListener listener = (INotificationListener) info.service;
4318 try {
4319 listener.onInterruptionFilterChanged(interruptionFilter);
4320 } catch (RemoteException ex) {
4321 Log.e(TAG, "unable to notify listener (interruption filter): " + listener, ex);
4322 }
4323 }
4324
Justin Koh38156c52014-06-04 13:57:49 -07004325 private boolean isListenerPackage(String packageName) {
4326 if (packageName == null) {
4327 return false;
4328 }
4329 // TODO: clean up locking object later
4330 synchronized (mNotificationList) {
4331 for (final ManagedServiceInfo serviceInfo : mServices) {
4332 if (packageName.equals(serviceInfo.component.getPackageName())) {
4333 return true;
4334 }
4335 }
4336 }
4337 return false;
4338 }
Kenny Guya263e4e2014-03-03 18:24:03 +00004339 }
John Spurlock25e2d242014-06-27 13:58:23 -04004340
4341 public static final class DumpFilter {
Dan Sandlera1770312015-07-10 13:59:29 -04004342 public boolean filtered = false;
John Spurlock25e2d242014-06-27 13:58:23 -04004343 public String pkgFilter;
John Spurlock50806fc2014-07-15 10:22:02 -04004344 public boolean zen;
Chris Wrene4b38802015-07-07 15:54:19 -04004345 public long since;
4346 public boolean stats;
Dan Sandlera1770312015-07-10 13:59:29 -04004347 public boolean redact = true;
John Spurlock25e2d242014-06-27 13:58:23 -04004348
4349 public static DumpFilter parseFromArguments(String[] args) {
Dan Sandlera1770312015-07-10 13:59:29 -04004350 final DumpFilter filter = new DumpFilter();
4351 for (int ai = 0; ai < args.length; ai++) {
4352 final String a = args[ai];
4353 if ("--noredact".equals(a) || "--reveal".equals(a)) {
4354 filter.redact = false;
4355 } else if ("p".equals(a) || "pkg".equals(a) || "--package".equals(a)) {
4356 if (ai < args.length-1) {
4357 ai++;
4358 filter.pkgFilter = args[ai].trim().toLowerCase();
4359 if (filter.pkgFilter.isEmpty()) {
4360 filter.pkgFilter = null;
4361 } else {
4362 filter.filtered = true;
4363 }
4364 }
4365 } else if ("--zen".equals(a) || "zen".equals(a)) {
4366 filter.filtered = true;
4367 filter.zen = true;
4368 } else if ("--stats".equals(a)) {
4369 filter.stats = true;
4370 if (ai < args.length-1) {
4371 ai++;
Tobias Thierer28532d02016-04-21 14:52:10 +01004372 filter.since = Long.parseLong(args[ai]);
Dan Sandlera1770312015-07-10 13:59:29 -04004373 } else {
4374 filter.since = 0;
4375 }
4376 }
John Spurlock25e2d242014-06-27 13:58:23 -04004377 }
Dan Sandlera1770312015-07-10 13:59:29 -04004378 return filter;
John Spurlock25e2d242014-06-27 13:58:23 -04004379 }
4380
4381 public boolean matches(StatusBarNotification sbn) {
Dan Sandlera1770312015-07-10 13:59:29 -04004382 if (!filtered) return true;
4383 return zen ? true : sbn != null
John Spurlock50806fc2014-07-15 10:22:02 -04004384 && (matches(sbn.getPackageName()) || matches(sbn.getOpPkg()));
John Spurlock25e2d242014-06-27 13:58:23 -04004385 }
4386
4387 public boolean matches(ComponentName component) {
Dan Sandlera1770312015-07-10 13:59:29 -04004388 if (!filtered) return true;
4389 return zen ? true : component != null && matches(component.getPackageName());
John Spurlock25e2d242014-06-27 13:58:23 -04004390 }
4391
4392 public boolean matches(String pkg) {
Dan Sandlera1770312015-07-10 13:59:29 -04004393 if (!filtered) return true;
4394 return zen ? true : pkg != null && pkg.toLowerCase().contains(pkgFilter);
John Spurlock50806fc2014-07-15 10:22:02 -04004395 }
4396
4397 @Override
4398 public String toString() {
Chris Wrene4b38802015-07-07 15:54:19 -04004399 return stats ? "stats" : zen ? "zen" : ('\'' + pkgFilter + '\'');
John Spurlock25e2d242014-06-27 13:58:23 -04004400 }
4401 }
Griff Hazen84a00ea2014-09-02 17:10:47 -07004402
4403 /**
4404 * Wrapper for a StatusBarNotification object that allows transfer across a oneway
4405 * binder without sending large amounts of data over a oneway transaction.
4406 */
4407 private static final class StatusBarNotificationHolder
4408 extends IStatusBarNotificationHolder.Stub {
Griff Hazene9aac5f2014-09-05 20:04:09 -07004409 private StatusBarNotification mValue;
Griff Hazen84a00ea2014-09-02 17:10:47 -07004410
4411 public StatusBarNotificationHolder(StatusBarNotification value) {
4412 mValue = value;
4413 }
4414
Griff Hazene9aac5f2014-09-05 20:04:09 -07004415 /** Get the held value and clear it. This function should only be called once per holder */
Griff Hazen84a00ea2014-09-02 17:10:47 -07004416 @Override
4417 public StatusBarNotification get() {
Griff Hazene9aac5f2014-09-05 20:04:09 -07004418 StatusBarNotification value = mValue;
4419 mValue = null;
4420 return value;
Griff Hazen84a00ea2014-09-02 17:10:47 -07004421 }
4422 }
John Spurlock7c74f782015-06-04 13:01:42 -04004423
4424 private final class PolicyAccess {
4425 private static final String SEPARATOR = ":";
4426 private final String[] PERM = {
4427 android.Manifest.permission.ACCESS_NOTIFICATION_POLICY
4428 };
4429
4430 public boolean isPackageGranted(String pkg) {
4431 return pkg != null && getGrantedPackages().contains(pkg);
4432 }
4433
4434 public void put(String pkg, boolean granted) {
4435 if (pkg == null) return;
4436 final ArraySet<String> pkgs = getGrantedPackages();
4437 boolean changed;
4438 if (granted) {
4439 changed = pkgs.add(pkg);
4440 } else {
4441 changed = pkgs.remove(pkg);
4442 }
4443 if (!changed) return;
4444 final String setting = TextUtils.join(SEPARATOR, pkgs);
4445 final int currentUser = ActivityManager.getCurrentUser();
4446 Settings.Secure.putStringForUser(getContext().getContentResolver(),
4447 Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES,
4448 setting,
4449 currentUser);
4450 getContext().sendBroadcastAsUser(new Intent(NotificationManager
4451 .ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED)
4452 .setPackage(pkg)
4453 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), new UserHandle(currentUser), null);
4454 }
4455
4456 public ArraySet<String> getGrantedPackages() {
4457 final ArraySet<String> pkgs = new ArraySet<>();
Julia Reynoldsea6c4482015-08-13 09:01:33 -04004458
4459 long identity = Binder.clearCallingIdentity();
4460 try {
4461 final String setting = Settings.Secure.getStringForUser(
4462 getContext().getContentResolver(),
4463 Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES,
4464 ActivityManager.getCurrentUser());
4465 if (setting != null) {
4466 final String[] tokens = setting.split(SEPARATOR);
4467 for (int i = 0; i < tokens.length; i++) {
4468 String token = tokens[i];
4469 if (token != null) {
Andreas Gampe1ed71f32015-12-11 15:49:07 -08004470 token = token.trim();
Julia Reynoldsea6c4482015-08-13 09:01:33 -04004471 }
4472 if (TextUtils.isEmpty(token)) {
4473 continue;
4474 }
4475 pkgs.add(token);
John Spurlock7c74f782015-06-04 13:01:42 -04004476 }
John Spurlock7c74f782015-06-04 13:01:42 -04004477 }
Julia Reynoldsea6c4482015-08-13 09:01:33 -04004478 } finally {
4479 Binder.restoreCallingIdentity(identity);
John Spurlock7c74f782015-06-04 13:01:42 -04004480 }
4481 return pkgs;
4482 }
4483
4484 public String[] getRequestingPackages() throws RemoteException {
4485 final ParceledListSlice list = AppGlobals.getPackageManager()
4486 .getPackagesHoldingPermissions(PERM, 0 /*flags*/,
4487 ActivityManager.getCurrentUser());
4488 final List<PackageInfo> pkgs = list.getList();
4489 if (pkgs == null || pkgs.isEmpty()) return new String[0];
4490 final int N = pkgs.size();
4491 final String[] rt = new String[N];
4492 for (int i = 0; i < N; i++) {
4493 rt[i] = pkgs.get(i).packageName;
4494 }
4495 return rt;
4496 }
4497 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004498}