blob: 124d7f1b9c97b79fa552fdb2b86da1cd9760a458 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Adam Lesinski182f73f2013-12-05 16:48:06 -080017package com.android.server.notification;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080018
Chris Wrene0ba7eb2016-03-04 17:30:43 -050019import static android.service.notification.NotificationRankerService.REASON_APP_CANCEL;
20import static android.service.notification.NotificationRankerService.REASON_APP_CANCEL_ALL;
21import static android.service.notification.NotificationRankerService.REASON_DELEGATE_CANCEL;
22import static android.service.notification.NotificationRankerService.REASON_DELEGATE_CANCEL_ALL;
23import static android.service.notification.NotificationRankerService.REASON_DELEGATE_CLICK;
24import static android.service.notification.NotificationRankerService.REASON_DELEGATE_ERROR;
25import static android.service.notification.NotificationRankerService.REASON_GROUP_OPTIMIZATION;
26import static android.service.notification.NotificationRankerService.REASON_GROUP_SUMMARY_CANCELED;
27import static android.service.notification.NotificationRankerService.REASON_LISTENER_CANCEL;
28import static android.service.notification.NotificationRankerService.REASON_LISTENER_CANCEL_ALL;
29import static android.service.notification.NotificationRankerService.REASON_PACKAGE_BANNED;
30import static android.service.notification.NotificationRankerService.REASON_PACKAGE_CHANGED;
31import static android.service.notification.NotificationRankerService.REASON_PACKAGE_SUSPENDED;
32import static android.service.notification.NotificationRankerService.REASON_PROFILE_TURNED_OFF;
33import static android.service.notification.NotificationRankerService.REASON_USER_STOPPED;
Jason Monk63506742015-12-16 12:06:51 -050034import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_EFFECTS;
Julia Reynoldsd5607292016-02-05 15:25:58 -050035import static android.service.notification.NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_OFF;
Julia Reynolds61721582016-01-05 08:35:25 -050036import static android.service.notification.NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_ON;
Christoph Studerb82bc782014-08-20 14:29:43 +020037import static android.service.notification.NotificationListenerService.TRIM_FULL;
38import static android.service.notification.NotificationListenerService.TRIM_LIGHT;
Julia Reynoldsf0f629f2016-02-25 09:34:04 -050039import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_DEFAULT;
Chris Wren4a4b49d2016-02-09 11:25:08 -050040import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_NONE;
Jeff Sharkey098d5802012-04-26 17:30:34 -070041import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
42import static org.xmlpull.v1.XmlPullParser.END_TAG;
43import static org.xmlpull.v1.XmlPullParser.START_TAG;
svetoslavganov75986cf2009-05-14 22:28:01 -070044
Chris Wren51017d02015-12-15 15:34:46 -050045import android.Manifest;
Wei Liu97e56662016-03-04 10:52:33 -080046import android.annotation.Nullable;
Dianne Hackborn41203752012-08-31 14:05:51 -070047import android.app.ActivityManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080048import android.app.ActivityManagerNative;
John Spurlock7340fc82014-04-24 18:50:12 -040049import android.app.AppGlobals;
Daniel Sandler4a900ac2013-01-30 14:04:10 -050050import android.app.AppOpsManager;
Julia Reynoldsa47a27f2015-08-24 08:31:47 -040051import android.app.AutomaticZenRule;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080052import android.app.IActivityManager;
53import android.app.INotificationManager;
54import android.app.ITransientNotification;
55import android.app.Notification;
John Spurlockb4782522014-08-22 14:54:46 -040056import android.app.NotificationManager;
John Spurlock1fc476d2015-04-14 16:05:20 -040057import android.app.NotificationManager.Policy;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080058import android.app.PendingIntent;
59import android.app.StatusBarManager;
John Spurlock35ef0a62015-05-28 11:24:10 -040060import android.app.backup.BackupManager;
Amith Yamasanif47e51e2015-04-17 10:02:15 -070061import android.app.usage.UsageEvents;
Amith Yamasanif47e51e2015-04-17 10:02:15 -070062import android.app.usage.UsageStatsManagerInternal;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080063import android.content.BroadcastReceiver;
Daniel Sandler5feceeb2013-03-22 18:29:23 -070064import android.content.ComponentName;
Dianne Hackborn1dac2772009-06-26 18:16:48 -070065import android.content.ContentResolver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080066import android.content.Context;
67import android.content.Intent;
68import android.content.IntentFilter;
John Spurlock7340fc82014-04-24 18:50:12 -040069import android.content.pm.ApplicationInfo;
Kenny Guy70058402014-10-28 20:45:06 +000070import android.content.pm.IPackageManager;
Daniel Sandler4a900ac2013-01-30 14:04:10 -050071import android.content.pm.PackageInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080072import android.content.pm.PackageManager;
73import android.content.pm.PackageManager.NameNotFoundException;
Christoph Studercee44ba2014-05-20 18:36:43 +020074import android.content.pm.ParceledListSlice;
Chris Wren66189fc2015-06-25 14:04:33 -040075import android.content.pm.UserInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080076import android.content.res.Resources;
Dianne Hackborn1dac2772009-06-26 18:16:48 -070077import android.database.ContentObserver;
John Spurlock7b414672014-07-18 13:02:39 -040078import android.media.AudioAttributes;
svetoslavganov75986cf2009-05-14 22:28:01 -070079import android.media.AudioManager;
John Spurlockcdb57ae2015-02-11 19:04:11 -050080import android.media.AudioManagerInternal;
Jean-Michel Triviceb79bc2014-09-05 11:09:14 -070081import android.media.AudioSystem;
Jeff Sharkey098d5802012-04-26 17:30:34 -070082import android.media.IRingtonePlayer;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080083import android.net.Uri;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080084import android.os.Binder;
John Spurlock2b122f42014-08-27 16:29:47 -040085import android.os.Bundle;
John Spurlock056c5192014-04-20 21:52:01 -040086import android.os.Environment;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080087import android.os.Handler;
Chris Wrenf9536642014-04-17 10:01:54 -040088import android.os.HandlerThread;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080089import android.os.IBinder;
John Spurlock7340fc82014-04-24 18:50:12 -040090import android.os.IInterface;
Chris Wrenf9536642014-04-17 10:01:54 -040091import android.os.Looper;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080092import android.os.Message;
Dianne Hackbornd8a43f62009-08-17 23:33:56 -070093import android.os.Process;
svetoslavganov75986cf2009-05-14 22:28:01 -070094import android.os.RemoteException;
Selim Cinekb5605e52015-02-20 18:21:41 +010095import android.os.SystemProperties;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -070096import android.os.UserHandle;
Chris Wren66189fc2015-06-25 14:04:33 -040097import android.os.UserManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080098import android.os.Vibrator;
99import android.provider.Settings;
Chris Wren333a61c2014-05-28 16:40:57 -0400100import android.service.notification.Condition;
John Spurlock7340fc82014-04-24 18:50:12 -0400101import android.service.notification.IConditionProvider;
Chris Wren333a61c2014-05-28 16:40:57 -0400102import android.service.notification.INotificationListener;
Griff Hazen84a00ea2014-09-02 17:10:47 -0700103import android.service.notification.IStatusBarNotificationHolder;
Chris Wrene0ba7eb2016-03-04 17:30:43 -0500104import android.service.notification.NotificationRankerService;
John Spurlock7340fc82014-04-24 18:50:12 -0400105import android.service.notification.NotificationListenerService;
Christoph Studer05ad4822014-05-16 14:16:03 +0200106import android.service.notification.NotificationRankingUpdate;
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700107import android.service.notification.StatusBarNotification;
John Spurlock056c5192014-04-20 21:52:01 -0400108import android.service.notification.ZenModeConfig;
John Spurlock32fe4c62014-10-02 12:16:02 -0400109import android.telephony.PhoneStateListener;
Daniel Sandlere96ffb12010-03-11 13:38:06 -0500110import android.telephony.TelephonyManager;
svetoslavganov75986cf2009-05-14 22:28:01 -0700111import android.text.TextUtils;
John Spurlocka4294292014-03-24 18:02:32 -0400112import android.util.ArrayMap;
John Spurlock1fa865f2014-07-21 14:56:39 -0400113import android.util.ArraySet;
Dianne Hackborn39606a02012-07-31 17:54:35 -0700114import android.util.AtomicFile;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800115import android.util.Log;
Andy Stadler110988c2010-12-03 14:29:16 -0800116import android.util.Slog;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400117import android.util.Xml;
svetoslavganov75986cf2009-05-14 22:28:01 -0700118import android.view.accessibility.AccessibilityEvent;
119import android.view.accessibility.AccessibilityManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800120import android.widget.Toast;
Rubin Xu7eadc1b2016-02-01 16:13:45 +0000121
Scott Greenwald9a05b312013-06-28 00:37:54 -0400122import com.android.internal.R;
Chris Wren93bb8b82016-03-29 14:35:05 -0400123import com.android.internal.annotations.VisibleForTesting;
Chris Wrend1dbc922015-06-19 17:51:16 -0400124import com.android.internal.statusbar.NotificationVisibility;
John Spurlock056c5192014-04-20 21:52:01 -0400125import com.android.internal.util.FastXmlSerializer;
Julia Reynoldsa47a27f2015-08-24 08:31:47 -0400126import com.android.internal.util.Preconditions;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800127import com.android.server.EventLogTags;
Amith Yamasanif47e51e2015-04-17 10:02:15 -0700128import com.android.server.LocalServices;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800129import com.android.server.SystemService;
130import com.android.server.lights.Light;
131import com.android.server.lights.LightsManager;
John Spurlock7340fc82014-04-24 18:50:12 -0400132import com.android.server.notification.ManagedServices.ManagedServiceInfo;
John Spurlockb408e8e2014-04-23 21:12:45 -0400133import com.android.server.statusbar.StatusBarManagerInternal;
Ruben Brunkdd18a0b2015-12-04 16:16:31 -0800134import com.android.server.vr.VrManagerInternal;
Ruben Brunke24b9a62016-02-16 21:38:24 -0800135import com.android.server.notification.ManagedServices.UserProfiles;
Ruben Brunkdd18a0b2015-12-04 16:16:31 -0800136
John Spurlockb408e8e2014-04-23 21:12:45 -0400137import libcore.io.IoUtils;
Rubin Xu7eadc1b2016-02-01 16:13:45 +0000138
Chris Wrene4b38802015-07-07 15:54:19 -0400139import org.json.JSONArray;
140import org.json.JSONException;
141import org.json.JSONObject;
Jeff Sharkey098d5802012-04-26 17:30:34 -0700142import org.xmlpull.v1.XmlPullParser;
143import org.xmlpull.v1.XmlPullParserException;
John Spurlock056c5192014-04-20 21:52:01 -0400144import org.xmlpull.v1.XmlSerializer;
Jeff Sharkey098d5802012-04-26 17:30:34 -0700145
John Spurlock35ef0a62015-05-28 11:24:10 -0400146import java.io.ByteArrayInputStream;
147import java.io.ByteArrayOutputStream;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400148import java.io.File;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800149import java.io.FileDescriptor;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400150import java.io.FileInputStream;
151import java.io.FileNotFoundException;
John Spurlock056c5192014-04-20 21:52:01 -0400152import java.io.FileOutputStream;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400153import java.io.IOException;
John Spurlock35ef0a62015-05-28 11:24:10 -0400154import java.io.InputStream;
155import java.io.OutputStream;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800156import java.io.PrintWriter;
Wojciech Staszkiewicz9e9e2e72015-05-08 14:58:46 +0100157import java.nio.charset.StandardCharsets;
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500158import java.util.ArrayDeque;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800159import java.util.ArrayList;
Julia Reynolds1c9bd422016-03-15 09:25:56 -0400160import java.util.Arrays;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400161import java.util.HashSet;
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500162import java.util.Iterator;
John Spurlock7c74f782015-06-04 13:01:42 -0400163import java.util.List;
Chris Wrenacf424a2016-03-15 12:48:55 -0400164import java.util.Map;
Christoph Studer265c1052014-07-23 17:14:33 +0200165import java.util.Map.Entry;
John Spurlockb4782522014-08-22 14:54:46 -0400166import java.util.Objects;
Chris Wren0efdb882016-03-01 17:17:47 -0500167import java.util.Set;
Chris Wren51017d02015-12-15 15:34:46 -0500168import java.util.concurrent.TimeUnit;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400169
Daniel Sandlerd0a2f862010-08-03 15:29:31 -0400170/** {@hide} */
Adam Lesinski182f73f2013-12-05 16:48:06 -0800171public class NotificationManagerService extends SystemService {
172 static final String TAG = "NotificationService";
Christoph Studer1f32c652014-11-26 15:32:20 +0100173 static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
Selim Cinek40412492015-12-08 18:03:22 -0800174 public static final boolean ENABLE_CHILD_NOTIFICATIONS
175 = SystemProperties.getBoolean("debug.child_notifs", true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800176
Adam Lesinski182f73f2013-12-05 16:48:06 -0800177 static final int MAX_PACKAGE_NOTIFICATIONS = 50;
Joe Onoratobd73d012010-06-04 11:44:54 -0700178
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800179 // message codes
Adam Lesinski182f73f2013-12-05 16:48:06 -0800180 static final int MESSAGE_TIMEOUT = 2;
John Spurlock056c5192014-04-20 21:52:01 -0400181 static final int MESSAGE_SAVE_POLICY_FILE = 3;
Chris Wren51017d02015-12-15 15:34:46 -0500182 static final int MESSAGE_SEND_RANKING_UPDATE = 4;
183 static final int MESSAGE_LISTENER_HINTS_CHANGED = 5;
184 static final int MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED = 6;
185
186 // ranking thread messages
187 private static final int MESSAGE_RECONSIDER_RANKING = 1000;
188 private static final int MESSAGE_RANKING_SORT = 1001;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800189
Adam Lesinski182f73f2013-12-05 16:48:06 -0800190 static final int LONG_DELAY = 3500; // 3.5 seconds
191 static final int SHORT_DELAY = 2000; // 2 seconds
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800192
Adam Lesinski182f73f2013-12-05 16:48:06 -0800193 static final long[] DEFAULT_VIBRATE_PATTERN = {0, 250, 250, 250};
Christoph Studer265c1052014-07-23 17:14:33 +0200194
Adam Lesinski182f73f2013-12-05 16:48:06 -0800195 static final int VIBRATE_PATTERN_MAXLEN = 8 * 2 + 1; // up to eight bumps
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800196
Adam Lesinski182f73f2013-12-05 16:48:06 -0800197 static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_NOTIFICATION;
Daniel Sandler526fa0e2012-12-04 14:51:50 -0500198
Adam Lesinski182f73f2013-12-05 16:48:06 -0800199 static final boolean ENABLE_BLOCKED_NOTIFICATIONS = true;
200 static final boolean ENABLE_BLOCKED_TOASTS = true;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400201
Christoph Studer12aeda82014-09-23 19:08:56 +0200202 // When #matchesCallFilter is called from the ringer, wait at most
203 // 3s to resolve the contacts. This timeout is required since
204 // ContactsProvider might take a long time to start up.
205 //
206 // Return STARRED_CONTACT when the timeout is hit in order to avoid
207 // missed calls in ZEN mode "Important".
208 static final int MATCHES_CALL_FILTER_CONTACTS_TIMEOUT_MS = 3000;
209 static final float MATCHES_CALL_FILTER_TIMEOUT_AFFINITY =
210 ValidateNotificationPeople.STARRED_CONTACT;
211
Christoph Studer265c1052014-07-23 17:14:33 +0200212 /** notification_enqueue status value for a newly enqueued notification. */
213 private static final int EVENTLOG_ENQUEUE_STATUS_NEW = 0;
214
215 /** notification_enqueue status value for an existing notification. */
216 private static final int EVENTLOG_ENQUEUE_STATUS_UPDATE = 1;
217
218 /** notification_enqueue status value for an ignored notification. */
219 private static final int EVENTLOG_ENQUEUE_STATUS_IGNORED = 2;
Chris Wren0efdb882016-03-01 17:17:47 -0500220 private String mRankerServicePackageName;
Christoph Studer265c1052014-07-23 17:14:33 +0200221
Adam Lesinski182f73f2013-12-05 16:48:06 -0800222 private IActivityManager mAm;
223 AudioManager mAudioManager;
John Spurlockcdb57ae2015-02-11 19:04:11 -0500224 AudioManagerInternal mAudioManagerInternal;
Wei Liu97e56662016-03-04 10:52:33 -0800225 @Nullable StatusBarManagerInternal mStatusBar;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800226 Vibrator mVibrator;
Ruben Brunkdd18a0b2015-12-04 16:16:31 -0800227 private VrManagerInternal mVrManagerInternal;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800228
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800229 final IBinder mForegroundToken = new Binder();
Chris Wren93bb8b82016-03-29 14:35:05 -0400230 private Handler mHandler;
Chris Wrenf9536642014-04-17 10:01:54 -0400231 private final HandlerThread mRankingThread = new HandlerThread("ranker",
232 Process.THREAD_PRIORITY_BACKGROUND);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800233
Adam Lesinski182f73f2013-12-05 16:48:06 -0800234 private Light mNotificationLight;
235 Light mAttentionLight;
Mike Lockwood670f9322010-01-20 12:13:36 -0500236 private int mDefaultNotificationColor;
237 private int mDefaultNotificationLedOn;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800238
Mike Lockwood670f9322010-01-20 12:13:36 -0500239 private int mDefaultNotificationLedOff;
Daniel Sandleredbb3802012-11-13 20:49:47 -0800240 private long[] mDefaultVibrationPattern;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800241
Daniel Sandleredbb3802012-11-13 20:49:47 -0800242 private long[] mFallbackVibrationPattern;
Chris Wren5116a822014-06-04 15:59:50 -0400243 private boolean mUseAttentionLight;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800244 boolean mSystemReady;
Daniel Sandleredbb3802012-11-13 20:49:47 -0800245
John Spurlockd8afe3c2014-08-01 14:04:07 -0400246 private boolean mDisableNotificationEffects;
John Spurlock32fe4c62014-10-02 12:16:02 -0400247 private int mCallState;
Chris Wren6054e612014-11-25 17:16:46 -0500248 private String mSoundNotificationKey;
249 private String mVibrateNotificationKey;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800250
John Spurlockd8afe3c2014-08-01 14:04:07 -0400251 private final ArraySet<ManagedServiceInfo> mListenersDisablingEffects = new ArraySet<>();
John Spurlockb4782522014-08-22 14:54:46 -0400252 private ComponentName mEffectsSuppressor;
John Spurlockd8afe3c2014-08-01 14:04:07 -0400253 private int mListenerHints; // right now, all hints are global
John Spurlock83104102015-02-12 23:25:12 -0500254 private int mInterruptionFilter = NotificationListenerService.INTERRUPTION_FILTER_UNKNOWN;
John Spurlock1fa865f2014-07-21 14:56:39 -0400255
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500256 // for enabling and disabling notification pulse behavior
Mike Lockwood63b5ad92011-08-30 09:55:30 -0400257 private boolean mScreenOn = true;
Daniel Sandlere96ffb12010-03-11 13:38:06 -0500258 private boolean mInCall = false;
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500259 private boolean mNotificationPulseEnabled;
260
Daniel Sandler09a247e2013-02-14 10:24:17 -0500261 // used as a mutex for access to all active notifications & listeners
Adam Lesinski182f73f2013-12-05 16:48:06 -0800262 final ArrayList<NotificationRecord> mNotificationList =
Fred Quintana6ecaff12009-09-25 14:23:13 -0700263 new ArrayList<NotificationRecord>();
John Spurlocka4294292014-03-24 18:02:32 -0400264 final ArrayMap<String, NotificationRecord> mNotificationsByKey =
265 new ArrayMap<String, NotificationRecord>();
Adam Lesinski182f73f2013-12-05 16:48:06 -0800266 final ArrayList<ToastRecord> mToastQueue = new ArrayList<ToastRecord>();
Christoph Studer265c1052014-07-23 17:14:33 +0200267 final ArrayMap<String, NotificationRecord> mSummaryByGroupKey = new ArrayMap<>();
John Spurlock7c74f782015-06-04 13:01:42 -0400268 final PolicyAccess mPolicyAccess = new PolicyAccess();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800269
Chris Wren6054e612014-11-25 17:16:46 -0500270 // The last key in this list owns the hardware.
271 ArrayList<String> mLights = new ArrayList<>();
svetoslavganov75986cf2009-05-14 22:28:01 -0700272
Adam Lesinski182f73f2013-12-05 16:48:06 -0800273 private AppOpsManager mAppOps;
Amith Yamasanif47e51e2015-04-17 10:02:15 -0700274 private UsageStatsManagerInternal mAppUsageStats;
Daniel Sandler4a900ac2013-01-30 14:04:10 -0500275
Griff Hazen9f637d12014-06-10 11:13:51 -0700276 private Archive mArchive;
277
John Spurlock21258a32015-05-27 18:22:55 -0400278 // Persistent storage for notification policy
Daniel Sandler0da673f2012-04-11 12:33:16 -0400279 private AtomicFile mPolicyFile;
John Spurlock21258a32015-05-27 18:22:55 -0400280
Daniel Sandler0da673f2012-04-11 12:33:16 -0400281 private static final int DB_VERSION = 1;
282
John Spurlock21258a32015-05-27 18:22:55 -0400283 private static final String TAG_NOTIFICATION_POLICY = "notification-policy";
Daniel Sandler0da673f2012-04-11 12:33:16 -0400284 private static final String ATTR_VERSION = "version";
285
John Spurlock21258a32015-05-27 18:22:55 -0400286 // Obsolete: converted if present, but not resaved to disk.
Daniel Sandler0da673f2012-04-11 12:33:16 -0400287 private static final String TAG_BLOCKED_PKGS = "blocked-packages";
288 private static final String TAG_PACKAGE = "package";
289 private static final String ATTR_NAME = "name";
290
Chris Wren54bbef42014-07-09 18:37:56 -0400291 private RankingHelper mRankingHelper;
Scott Greenwald9a05b312013-06-28 00:37:54 -0400292
John Spurlockb408e8e2014-04-23 21:12:45 -0400293 private final UserProfiles mUserProfiles = new UserProfiles();
John Spurlock7340fc82014-04-24 18:50:12 -0400294 private NotificationListeners mListeners;
Chris Wren0efdb882016-03-01 17:17:47 -0500295 private NotificationRankers mRankerServices;
John Spurlock7340fc82014-04-24 18:50:12 -0400296 private ConditionProviders mConditionProviders;
Christoph Studer1c3f81f2014-04-16 15:05:56 +0200297 private NotificationUsageStats mUsageStats;
Christoph Studer546bec82014-03-14 12:17:12 +0100298
John Spurlocke6a7d932014-03-13 12:29:00 -0400299 private static final int MY_UID = Process.myUid();
300 private static final int MY_PID = Process.myPid();
Chris Wren51017d02015-12-15 15:34:46 -0500301 private RankingHandler mRankingHandler;
John Spurlocke6a7d932014-03-13 12:29:00 -0400302
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500303 private static class Archive {
Griff Hazen9f637d12014-06-10 11:13:51 -0700304 final int mBufferSize;
305 final ArrayDeque<StatusBarNotification> mBuffer;
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500306
Griff Hazen9f637d12014-06-10 11:13:51 -0700307 public Archive(int size) {
308 mBufferSize = size;
309 mBuffer = new ArrayDeque<StatusBarNotification>(mBufferSize);
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500310 }
Jeff Sharkey0c1baf92013-04-03 13:08:52 -0700311
Daniel Sandler5e62e3a2013-04-15 20:57:02 -0400312 public String toString() {
313 final StringBuilder sb = new StringBuilder();
314 final int N = mBuffer.size();
315 sb.append("Archive (");
316 sb.append(N);
317 sb.append(" notification");
318 sb.append((N==1)?")":"s)");
319 return sb.toString();
320 }
321
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500322 public void record(StatusBarNotification nr) {
Griff Hazen9f637d12014-06-10 11:13:51 -0700323 if (mBuffer.size() == mBufferSize) {
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500324 mBuffer.removeFirst();
325 }
Daniel Sandler26b81d52013-05-20 20:56:43 -0400326
327 // We don't want to store the heavy bits of the notification in the archive,
328 // but other clients in the system process might be using the object, so we
329 // store a (lightened) copy.
330 mBuffer.addLast(nr.cloneLight());
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500331 }
332
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500333 public Iterator<StatusBarNotification> descendingIterator() {
334 return mBuffer.descendingIterator();
335 }
Daniel Sandler78d0d252013-02-12 08:14:52 -0500336
337 public StatusBarNotification[] getArray(int count) {
Griff Hazen9f637d12014-06-10 11:13:51 -0700338 if (count == 0) count = mBufferSize;
Daniel Sandler78d0d252013-02-12 08:14:52 -0500339 final StatusBarNotification[] a
340 = new StatusBarNotification[Math.min(count, mBuffer.size())];
341 Iterator<StatusBarNotification> iter = descendingIterator();
342 int i=0;
343 while (iter.hasNext() && i < count) {
344 a[i++] = iter.next();
345 }
346 return a;
347 }
348
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500349 }
350
John Spurlock35ef0a62015-05-28 11:24:10 -0400351 private void readPolicyXml(InputStream stream, boolean forRestore)
352 throws XmlPullParserException, NumberFormatException, IOException {
353 final XmlPullParser parser = Xml.newPullParser();
354 parser.setInput(stream, StandardCharsets.UTF_8.name());
355
Chris Wrenacf424a2016-03-15 12:48:55 -0400356 while (parser.next() != END_DOCUMENT) {
John Spurlock35ef0a62015-05-28 11:24:10 -0400357 mZenModeHelper.readXml(parser, forRestore);
358 mRankingHelper.readXml(parser, forRestore);
359 }
360 }
361
John Spurlock056c5192014-04-20 21:52:01 -0400362 private void loadPolicyFile() {
John Spurlock21258a32015-05-27 18:22:55 -0400363 if (DBG) Slog.d(TAG, "loadPolicyFile");
John Spurlock056c5192014-04-20 21:52:01 -0400364 synchronized(mPolicyFile) {
Daniel Sandler0da673f2012-04-11 12:33:16 -0400365
John Spurlock056c5192014-04-20 21:52:01 -0400366 FileInputStream infile = null;
367 try {
368 infile = mPolicyFile.openRead();
John Spurlock35ef0a62015-05-28 11:24:10 -0400369 readPolicyXml(infile, false /*forRestore*/);
John Spurlock056c5192014-04-20 21:52:01 -0400370 } catch (FileNotFoundException e) {
371 // No data yet
372 } catch (IOException e) {
373 Log.wtf(TAG, "Unable to read notification policy", e);
374 } catch (NumberFormatException e) {
375 Log.wtf(TAG, "Unable to parse notification policy", e);
376 } catch (XmlPullParserException e) {
377 Log.wtf(TAG, "Unable to parse notification policy", e);
378 } finally {
379 IoUtils.closeQuietly(infile);
380 }
381 }
382 }
383
384 public void savePolicyFile() {
385 mHandler.removeMessages(MESSAGE_SAVE_POLICY_FILE);
386 mHandler.sendEmptyMessage(MESSAGE_SAVE_POLICY_FILE);
387 }
388
389 private void handleSavePolicyFile() {
John Spurlock21258a32015-05-27 18:22:55 -0400390 if (DBG) Slog.d(TAG, "handleSavePolicyFile");
John Spurlock056c5192014-04-20 21:52:01 -0400391 synchronized (mPolicyFile) {
392 final FileOutputStream stream;
393 try {
394 stream = mPolicyFile.startWrite();
395 } catch (IOException e) {
396 Slog.w(TAG, "Failed to save policy file", e);
397 return;
398 }
399
400 try {
John Spurlock35ef0a62015-05-28 11:24:10 -0400401 writePolicyXml(stream, false /*forBackup*/);
John Spurlock056c5192014-04-20 21:52:01 -0400402 mPolicyFile.finishWrite(stream);
403 } catch (IOException e) {
404 Slog.w(TAG, "Failed to save policy file, restoring backup", e);
405 mPolicyFile.failWrite(stream);
Daniel Sandler0da673f2012-04-11 12:33:16 -0400406 }
407 }
John Spurlock35ef0a62015-05-28 11:24:10 -0400408 BackupManager.dataChanged(getContext().getPackageName());
409 }
410
411 private void writePolicyXml(OutputStream stream, boolean forBackup) throws IOException {
412 final XmlSerializer out = new FastXmlSerializer();
413 out.setOutput(stream, StandardCharsets.UTF_8.name());
414 out.startDocument(null, true);
415 out.startTag(null, TAG_NOTIFICATION_POLICY);
416 out.attribute(null, ATTR_VERSION, Integer.toString(DB_VERSION));
417 mZenModeHelper.writeXml(out, forBackup);
418 mRankingHelper.writeXml(out, forBackup);
419 out.endTag(null, TAG_NOTIFICATION_POLICY);
420 out.endDocument();
Daniel Sandler0da673f2012-04-11 12:33:16 -0400421 }
422
Daniel Sandler4a900ac2013-01-30 14:04:10 -0500423 /** Use this when you actually want to post a notification or toast.
424 *
425 * Unchecked. Not exposed via Binder, but can be called in the course of enqueue*().
426 */
427 private boolean noteNotificationOp(String pkg, int uid) {
428 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
429 != AppOpsManager.MODE_ALLOWED) {
430 Slog.v(TAG, "notifications are disabled by AppOps for " + pkg);
431 return false;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400432 }
Daniel Sandler4a900ac2013-01-30 14:04:10 -0500433 return true;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400434 }
435
Chris Wren66189fc2015-06-25 14:04:33 -0400436 /** Use this to check if a package can post a notification or toast. */
437 private boolean checkNotificationOp(String pkg, int uid) {
438 return mAppOps.checkOp(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
Andrei Stingaceanu355b2322016-02-12 16:43:51 +0000439 == AppOpsManager.MODE_ALLOWED && !isPackageSuspendedForUser(pkg, uid);
Chris Wren66189fc2015-06-25 14:04:33 -0400440 }
441
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800442 private static final class ToastRecord
443 {
444 final int pid;
445 final String pkg;
446 final ITransientNotification callback;
447 int duration;
448
449 ToastRecord(int pid, String pkg, ITransientNotification callback, int duration)
450 {
451 this.pid = pid;
452 this.pkg = pkg;
453 this.callback = callback;
454 this.duration = duration;
455 }
456
457 void update(int duration) {
458 this.duration = duration;
459 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800460
John Spurlock25e2d242014-06-27 13:58:23 -0400461 void dump(PrintWriter pw, String prefix, DumpFilter filter) {
462 if (filter != null && !filter.matches(pkg)) return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800463 pw.println(prefix + this);
464 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800465
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800466 @Override
467 public final String toString()
468 {
469 return "ToastRecord{"
470 + Integer.toHexString(System.identityHashCode(this))
471 + " pkg=" + pkg
472 + " callback=" + callback
473 + " duration=" + duration;
474 }
475 }
476
Adam Lesinski182f73f2013-12-05 16:48:06 -0800477 private final NotificationDelegate mNotificationDelegate = new NotificationDelegate() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800478
Adam Lesinski182f73f2013-12-05 16:48:06 -0800479 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800480 public void onSetDisabled(int status) {
481 synchronized (mNotificationList) {
John Spurlockd8afe3c2014-08-01 14:04:07 -0400482 mDisableNotificationEffects =
483 (status & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0;
John Spurlock32fe4c62014-10-02 12:16:02 -0400484 if (disableNotificationEffects(null) != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800485 // cancel whatever's going on
486 long identity = Binder.clearCallingIdentity();
487 try {
Adam Lesinski182f73f2013-12-05 16:48:06 -0800488 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
Jeff Sharkey098d5802012-04-26 17:30:34 -0700489 if (player != null) {
490 player.stopAsync();
491 }
492 } catch (RemoteException e) {
493 } finally {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800494 Binder.restoreCallingIdentity(identity);
495 }
496
497 identity = Binder.clearCallingIdentity();
498 try {
499 mVibrator.cancel();
Jeff Sharkey098d5802012-04-26 17:30:34 -0700500 } finally {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800501 Binder.restoreCallingIdentity(identity);
502 }
503 }
504 }
505 }
506
Adam Lesinski182f73f2013-12-05 16:48:06 -0800507 @Override
John Spurlocke6a7d932014-03-13 12:29:00 -0400508 public void onClearAll(int callingUid, int callingPid, int userId) {
Adam Lesinskie8240262014-03-26 16:01:00 -0700509 synchronized (mNotificationList) {
Kenny Guya263e4e2014-03-03 18:24:03 +0000510 cancelAllLocked(callingUid, callingPid, userId, REASON_DELEGATE_CANCEL_ALL, null,
511 /*includeCurrentProfiles*/ true);
Adam Lesinskie8240262014-03-26 16:01:00 -0700512 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800513 }
514
Adam Lesinski182f73f2013-12-05 16:48:06 -0800515 @Override
Christoph Studer03b87a22014-04-30 17:33:27 +0200516 public void onNotificationClick(int callingUid, int callingPid, String key) {
517 synchronized (mNotificationList) {
Christoph Studer03b87a22014-04-30 17:33:27 +0200518 NotificationRecord r = mNotificationsByKey.get(key);
519 if (r == null) {
520 Log.w(TAG, "No notification with key: " + key);
521 return;
522 }
Chris Wrene6ddb8a2015-05-27 15:21:00 -0400523 final long now = System.currentTimeMillis();
524 EventLogTags.writeNotificationClicked(key,
525 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
526
Christoph Studer03b87a22014-04-30 17:33:27 +0200527 StatusBarNotification sbn = r.sbn;
528 cancelNotification(callingUid, callingPid, sbn.getPackageName(), sbn.getTag(),
529 sbn.getId(), Notification.FLAG_AUTO_CANCEL,
530 Notification.FLAG_FOREGROUND_SERVICE, false, r.getUserId(),
531 REASON_DELEGATE_CLICK, null);
532 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800533 }
534
Adam Lesinski182f73f2013-12-05 16:48:06 -0800535 @Override
Christoph Studer4da84cd2014-10-21 17:24:20 +0200536 public void onNotificationActionClick(int callingUid, int callingPid, String key,
537 int actionIndex) {
538 synchronized (mNotificationList) {
Christoph Studer4da84cd2014-10-21 17:24:20 +0200539 NotificationRecord r = mNotificationsByKey.get(key);
540 if (r == null) {
541 Log.w(TAG, "No notification with key: " + key);
542 return;
543 }
Chris Wrene6ddb8a2015-05-27 15:21:00 -0400544 final long now = System.currentTimeMillis();
545 EventLogTags.writeNotificationActionClicked(key, actionIndex,
546 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
Christoph Studer4da84cd2014-10-21 17:24:20 +0200547 // TODO: Log action click via UsageStats.
548 }
549 }
550
551 @Override
John Spurlocke6a7d932014-03-13 12:29:00 -0400552 public void onNotificationClear(int callingUid, int callingPid,
553 String pkg, String tag, int id, int userId) {
554 cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
Kenny Guy3a7c4a52014-03-03 18:24:03 +0000555 Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
John Spurlocke6a7d932014-03-13 12:29:00 -0400556 true, userId, REASON_DELEGATE_CANCEL, null);
Daniel Sandler0f0b11c2010-08-04 15:54:58 -0400557 }
558
Adam Lesinski182f73f2013-12-05 16:48:06 -0800559 @Override
Chris Wrenb659c4f2015-06-25 17:12:27 -0400560 public void onPanelRevealed(boolean clearEffects, int items) {
561 EventLogTags.writeNotificationPanelRevealed(items);
Christoph Studer1f32c652014-11-26 15:32:20 +0100562 if (clearEffects) {
563 clearEffects();
564 }
565 }
566
567 @Override
568 public void onPanelHidden() {
569 EventLogTags.writeNotificationPanelHidden();
570 }
571
572 @Override
573 public void clearEffects() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800574 synchronized (mNotificationList) {
Christoph Studer1f32c652014-11-26 15:32:20 +0100575 if (DBG) Slog.d(TAG, "clearEffects");
Chris Wren93bb8b82016-03-29 14:35:05 -0400576 clearSoundLocked();
577 clearVibrateLocked();
578 clearLightsLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800579 }
580 }
Joe Onorato005847b2010-06-04 16:08:02 -0400581
Adam Lesinski182f73f2013-12-05 16:48:06 -0800582 @Override
John Spurlocke6a7d932014-03-13 12:29:00 -0400583 public void onNotificationError(int callingUid, int callingPid, String pkg, String tag, int id,
Kenny Guy3a7c4a52014-03-03 18:24:03 +0000584 int uid, int initialPid, String message, int userId) {
Daniel Sandlerd0a2f862010-08-03 15:29:31 -0400585 Slog.d(TAG, "onNotification error pkg=" + pkg + " tag=" + tag + " id=" + id
586 + "; will crashApplication(uid=" + uid + ", pid=" + initialPid + ")");
John Spurlocke6a7d932014-03-13 12:29:00 -0400587 cancelNotification(callingUid, callingPid, pkg, tag, id, 0, 0, false, userId,
588 REASON_DELEGATE_ERROR, null);
Dianne Hackborn9d39d0c2010-06-24 15:57:42 -0700589 long ident = Binder.clearCallingIdentity();
590 try {
591 ActivityManagerNative.getDefault().crashApplication(uid, initialPid, pkg,
592 "Bad notification posted from package " + pkg
593 + ": " + message);
594 } catch (RemoteException e) {
595 }
596 Binder.restoreCallingIdentity(ident);
Joe Onorato005847b2010-06-04 16:08:02 -0400597 }
John Spurlocke677d712014-02-13 12:52:19 -0500598
599 @Override
Chris Wrend1dbc922015-06-19 17:51:16 -0400600 public void onNotificationVisibilityChanged(NotificationVisibility[] newlyVisibleKeys,
601 NotificationVisibility[] noLongerVisibleKeys) {
Christoph Studerffeb0c32014-05-07 22:23:56 +0200602 synchronized (mNotificationList) {
Chris Wrend1dbc922015-06-19 17:51:16 -0400603 for (NotificationVisibility nv : newlyVisibleKeys) {
604 NotificationRecord r = mNotificationsByKey.get(nv.key);
Christoph Studerffeb0c32014-05-07 22:23:56 +0200605 if (r == null) continue;
Chris Wrend1dbc922015-06-19 17:51:16 -0400606 r.setVisibility(true, nv.rank);
607 nv.recycle();
Christoph Studerffeb0c32014-05-07 22:23:56 +0200608 }
609 // Note that we might receive this event after notifications
610 // have already left the system, e.g. after dismissing from the
611 // shade. Hence not finding notifications in
612 // mNotificationsByKey is not an exceptional condition.
Chris Wrend1dbc922015-06-19 17:51:16 -0400613 for (NotificationVisibility nv : noLongerVisibleKeys) {
614 NotificationRecord r = mNotificationsByKey.get(nv.key);
Christoph Studerffeb0c32014-05-07 22:23:56 +0200615 if (r == null) continue;
Chris Wrend1dbc922015-06-19 17:51:16 -0400616 r.setVisibility(false, nv.rank);
617 nv.recycle();
Christoph Studerffeb0c32014-05-07 22:23:56 +0200618 }
619 }
Christoph Studer92b389d2014-04-01 18:44:40 +0200620 }
Chris Wren78403d72014-07-28 10:23:24 +0100621
622 @Override
623 public void onNotificationExpansionChanged(String key,
624 boolean userAction, boolean expanded) {
Chris Wren78403d72014-07-28 10:23:24 +0100625 synchronized (mNotificationList) {
626 NotificationRecord r = mNotificationsByKey.get(key);
627 if (r != null) {
628 r.stats.onExpansionChanged(userAction, expanded);
Chris Wrene6ddb8a2015-05-27 15:21:00 -0400629 final long now = System.currentTimeMillis();
630 EventLogTags.writeNotificationExpansion(key,
631 userAction ? 1 : 0, expanded ? 1 : 0,
632 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
Chris Wren78403d72014-07-28 10:23:24 +0100633 }
634 }
635 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800636 };
637
Chris Wren93bb8b82016-03-29 14:35:05 -0400638 private void clearSoundLocked() {
639 mSoundNotificationKey = null;
640 long identity = Binder.clearCallingIdentity();
641 try {
642 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
643 if (player != null) {
644 player.stopAsync();
645 }
646 } catch (RemoteException e) {
647 } finally {
648 Binder.restoreCallingIdentity(identity);
649 }
650 }
651
652 private void clearVibrateLocked() {
653 mVibrateNotificationKey = null;
654 long identity = Binder.clearCallingIdentity();
655 try {
656 mVibrator.cancel();
657 } finally {
658 Binder.restoreCallingIdentity(identity);
659 }
660 }
661
662 private void clearLightsLocked() {
663 // light
664 mLights.clear();
665 updateLightsLocked();
666 }
667
Kenny Guy70058402014-10-28 20:45:06 +0000668 private final BroadcastReceiver mPackageIntentReceiver = new BroadcastReceiver() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800669 @Override
670 public void onReceive(Context context, Intent intent) {
671 String action = intent.getAction();
Dianne Hackborn29cd7f12015-01-08 10:37:05 -0800672 if (action == null) {
673 return;
674 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800675
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800676 boolean queryRestart = false;
Chris Wrenae9bb572013-05-15 14:50:28 -0400677 boolean queryRemove = false;
Daniel Sandler26ece572012-06-01 15:38:46 -0400678 boolean packageChanged = false;
John Spurlock79f78922013-05-16 09:10:05 -0400679 boolean cancelNotifications = true;
Andrei Stingaceanu0122f6512016-01-22 15:33:03 +0000680 int reason = REASON_PACKAGE_CHANGED;
Chris Wrenf9536642014-04-17 10:01:54 -0400681
Chris Wren3da73022013-05-10 14:41:21 -0400682 if (action.equals(Intent.ACTION_PACKAGE_ADDED)
Chris Wrenae9bb572013-05-15 14:50:28 -0400683 || (queryRemove=action.equals(Intent.ACTION_PACKAGE_REMOVED))
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800684 || action.equals(Intent.ACTION_PACKAGE_RESTARTED)
Daniel Sandler26ece572012-06-01 15:38:46 -0400685 || (packageChanged=action.equals(Intent.ACTION_PACKAGE_CHANGED))
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800686 || (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART))
Andrei Stingaceanu0122f6512016-01-22 15:33:03 +0000687 || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)
688 || action.equals(Intent.ACTION_PACKAGES_SUSPENDED)) {
Kenny Guy70058402014-10-28 20:45:06 +0000689 int changeUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
690 UserHandle.USER_ALL);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800691 String pkgList[] = null;
Chris Wrenae9bb572013-05-15 14:50:28 -0400692 boolean queryReplace = queryRemove &&
693 intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
John Spurlocke77bb362014-04-26 10:24:59 -0400694 if (DBG) Slog.i(TAG, "action=" + action + " queryReplace=" + queryReplace);
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800695 if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800696 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
Andrei Stingaceanu0122f6512016-01-22 15:33:03 +0000697 } else if (action.equals(Intent.ACTION_PACKAGES_SUSPENDED)) {
698 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
699 reason = REASON_PACKAGE_SUSPENDED;
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800700 } else if (queryRestart) {
701 pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800702 } else {
703 Uri uri = intent.getData();
704 if (uri == null) {
705 return;
706 }
707 String pkgName = uri.getSchemeSpecificPart();
708 if (pkgName == null) {
709 return;
710 }
Daniel Sandler26ece572012-06-01 15:38:46 -0400711 if (packageChanged) {
712 // We cancel notifications for packages which have just been disabled
Christopher Tate06e5fed2013-10-09 14:39:15 -0700713 try {
Kenny Guy70058402014-10-28 20:45:06 +0000714 final IPackageManager pm = AppGlobals.getPackageManager();
715 final int enabled = pm.getApplicationEnabledSetting(pkgName,
716 changeUserId != UserHandle.USER_ALL ? changeUserId :
Xiaohui Chenddbe4ca2015-08-13 16:20:56 -0700717 UserHandle.USER_SYSTEM);
Christopher Tate06e5fed2013-10-09 14:39:15 -0700718 if (enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED
719 || enabled == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
720 cancelNotifications = false;
721 }
722 } catch (IllegalArgumentException e) {
723 // Package doesn't exist; probably racing with uninstall.
724 // cancelNotifications is already true, so nothing to do here.
725 if (DBG) {
726 Slog.i(TAG, "Exception trying to look up app enabled setting", e);
727 }
Kenny Guy70058402014-10-28 20:45:06 +0000728 } catch (RemoteException e) {
729 // Failed to talk to PackageManagerService Should never happen!
Daniel Sandler26ece572012-06-01 15:38:46 -0400730 }
731 }
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800732 pkgList = new String[]{pkgName};
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800733 }
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700734
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800735 if (pkgList != null && (pkgList.length > 0)) {
736 for (String pkgName : pkgList) {
John Spurlock79f78922013-05-16 09:10:05 -0400737 if (cancelNotifications) {
John Spurlocke6a7d932014-03-13 12:29:00 -0400738 cancelAllNotificationsInt(MY_UID, MY_PID, pkgName, 0, 0, !queryRestart,
Julia Reynoldsef37f282016-02-12 09:11:27 -0500739 changeUserId, reason, null);
John Spurlock79f78922013-05-16 09:10:05 -0400740 }
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800741 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800742 }
John Spurlockb408e8e2014-04-23 21:12:45 -0400743 mListeners.onPackagesChanged(queryReplace, pkgList);
Chris Wrene0ba7eb2016-03-04 17:30:43 -0500744 mRankerServices.onPackagesChanged(queryReplace, pkgList);
John Spurlock7340fc82014-04-24 18:50:12 -0400745 mConditionProviders.onPackagesChanged(queryReplace, pkgList);
John Spurlock35ef0a62015-05-28 11:24:10 -0400746 mRankingHelper.onPackagesChanged(queryReplace, pkgList);
Kenny Guy70058402014-10-28 20:45:06 +0000747 }
748 }
749 };
750
751 private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
752 @Override
753 public void onReceive(Context context, Intent intent) {
754 String action = intent.getAction();
755
756 if (action.equals(Intent.ACTION_SCREEN_ON)) {
Mike Lockwood63b5ad92011-08-30 09:55:30 -0400757 // Keep track of screen on/off state, but do not turn off the notification light
758 // until user passes through the lock screen or views the notification.
759 mScreenOn = true;
Christoph Studer1f32c652014-11-26 15:32:20 +0100760 updateNotificationPulse();
Mike Lockwood63b5ad92011-08-30 09:55:30 -0400761 } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
762 mScreenOn = false;
Christoph Studer1f32c652014-11-26 15:32:20 +0100763 updateNotificationPulse();
Daniel Sandlere96ffb12010-03-11 13:38:06 -0500764 } else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) {
John Spurlock5d2eeb12014-01-16 10:46:36 -0500765 mInCall = TelephonyManager.EXTRA_STATE_OFFHOOK
766 .equals(intent.getStringExtra(TelephonyManager.EXTRA_STATE));
Daniel Sandlere96ffb12010-03-11 13:38:06 -0500767 updateNotificationPulse();
Dianne Hackborn80a4af22012-08-27 19:18:31 -0700768 } else if (action.equals(Intent.ACTION_USER_STOPPED)) {
769 int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
770 if (userHandle >= 0) {
John Spurlocke6a7d932014-03-13 12:29:00 -0400771 cancelAllNotificationsInt(MY_UID, MY_PID, null, 0, 0, true, userHandle,
Julia Reynoldsef37f282016-02-12 09:11:27 -0500772 REASON_USER_STOPPED, null);
Dianne Hackborn80a4af22012-08-27 19:18:31 -0700773 }
Rubin Xu7eadc1b2016-02-01 16:13:45 +0000774 } else if (action.equals(Intent.ACTION_MANAGED_PROFILE_AVAILABILITY_CHANGED)) {
775 boolean inQuietMode = intent.getBooleanExtra(Intent.EXTRA_QUIET_MODE, false);
776 int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
777 if (inQuietMode && userHandle >= 0) {
778 cancelAllNotificationsInt(MY_UID, MY_PID, null, 0, 0, true, userHandle,
Julia Reynoldsef37f282016-02-12 09:11:27 -0500779 REASON_PROFILE_TURNED_OFF, null);
Rubin Xu7eadc1b2016-02-01 16:13:45 +0000780 }
Mike Lockwood63b5ad92011-08-30 09:55:30 -0400781 } else if (action.equals(Intent.ACTION_USER_PRESENT)) {
782 // turn off LED when user passes through lock screen
783 mNotificationLight.turnOff();
Wei Liu97e56662016-03-04 10:52:33 -0800784 if (mStatusBar != null) {
785 mStatusBar.notificationLightOff();
786 }
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400787 } else if (action.equals(Intent.ACTION_USER_SWITCHED)) {
John Spurlock1b8b22b2015-05-20 09:47:13 -0400788 final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400789 // reload per-user settings
790 mSettingsObserver.update(null);
John Spurlockb408e8e2014-04-23 21:12:45 -0400791 mUserProfiles.updateCache(context);
Christoph Studerb53dfd42014-09-12 14:45:59 +0200792 // Refresh managed services
John Spurlock1b8b22b2015-05-20 09:47:13 -0400793 mConditionProviders.onUserSwitched(user);
794 mListeners.onUserSwitched(user);
Chris Wrene0ba7eb2016-03-04 17:30:43 -0500795 mRankerServices.onUserSwitched(user);
John Spurlock21258a32015-05-27 18:22:55 -0400796 mZenModeHelper.onUserSwitched(user);
Kenny Guy3a7c4a52014-03-03 18:24:03 +0000797 } else if (action.equals(Intent.ACTION_USER_ADDED)) {
John Spurlockb408e8e2014-04-23 21:12:45 -0400798 mUserProfiles.updateCache(context);
John Spurlock21258a32015-05-27 18:22:55 -0400799 } else if (action.equals(Intent.ACTION_USER_REMOVED)) {
800 final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
801 mZenModeHelper.onUserRemoved(user);
Julia Reynoldsa3dcaff2016-02-03 15:04:05 -0500802 } else if (action.equals(Intent.ACTION_USER_UNLOCKED)) {
803 final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
804 mConditionProviders.onUserUnlocked(user);
805 mListeners.onUserUnlocked(user);
Chris Wrene0ba7eb2016-03-04 17:30:43 -0500806 mRankerServices.onUserUnlocked(user);
Julia Reynoldsa3dcaff2016-02-03 15:04:05 -0500807 mZenModeHelper.onUserUnlocked(user);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800808 }
809 }
810 };
811
John Spurlock7c74f782015-06-04 13:01:42 -0400812 private final class SettingsObserver extends ContentObserver {
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400813 private final Uri NOTIFICATION_LIGHT_PULSE_URI
814 = Settings.System.getUriFor(Settings.System.NOTIFICATION_LIGHT_PULSE);
815
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700816 SettingsObserver(Handler handler) {
817 super(handler);
818 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800819
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700820 void observe() {
Adam Lesinski182f73f2013-12-05 16:48:06 -0800821 ContentResolver resolver = getContext().getContentResolver();
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400822 resolver.registerContentObserver(NOTIFICATION_LIGHT_PULSE_URI,
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700823 false, this, UserHandle.USER_ALL);
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400824 update(null);
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700825 }
826
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400827 @Override public void onChange(boolean selfChange, Uri uri) {
828 update(uri);
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700829 }
830
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400831 public void update(Uri uri) {
Adam Lesinski182f73f2013-12-05 16:48:06 -0800832 ContentResolver resolver = getContext().getContentResolver();
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400833 if (uri == null || NOTIFICATION_LIGHT_PULSE_URI.equals(uri)) {
834 boolean pulseEnabled = Settings.System.getInt(resolver,
835 Settings.System.NOTIFICATION_LIGHT_PULSE, 0) != 0;
836 if (mNotificationPulseEnabled != pulseEnabled) {
837 mNotificationPulseEnabled = pulseEnabled;
838 updateNotificationPulse();
839 }
840 }
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700841 }
842 }
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500843
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400844 private SettingsObserver mSettingsObserver;
John Spurlock056c5192014-04-20 21:52:01 -0400845 private ZenModeHelper mZenModeHelper;
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400846
John Spurlockcad57682014-07-26 17:09:56 -0400847 private final Runnable mBuzzBeepBlinked = new Runnable() {
848 @Override
849 public void run() {
Wei Liu97e56662016-03-04 10:52:33 -0800850 if (mStatusBar != null) {
851 mStatusBar.buzzBeepBlinked();
852 }
John Spurlockcad57682014-07-26 17:09:56 -0400853 }
854 };
855
Daniel Sandleredbb3802012-11-13 20:49:47 -0800856 static long[] getLongArray(Resources r, int resid, int maxlen, long[] def) {
857 int[] ar = r.getIntArray(resid);
858 if (ar == null) {
859 return def;
860 }
861 final int len = ar.length > maxlen ? maxlen : ar.length;
862 long[] out = new long[len];
863 for (int i=0; i<len; i++) {
864 out[i] = ar[i];
865 }
866 return out;
867 }
868
Jeff Brownb880d882014-02-10 19:47:07 -0800869 public NotificationManagerService(Context context) {
870 super(context);
871 }
872
Chris Wren93bb8b82016-03-29 14:35:05 -0400873 @VisibleForTesting
874 void setAudioManager(AudioManager audioMananger) {
875 mAudioManager = audioMananger;
876 }
877
878 @VisibleForTesting
879 void setVibrator(Vibrator vibrator) {
880 mVibrator = vibrator;
881 }
882
883 @VisibleForTesting
884 void setSystemReady(boolean systemReady) {
885 mSystemReady = systemReady;
886 }
887
888 @VisibleForTesting
889 void setHandler(Handler handler) {
890 mHandler = handler;
891 }
892
Adam Lesinski182f73f2013-12-05 16:48:06 -0800893 @Override
894 public void onStart() {
Chris Wren54bbef42014-07-09 18:37:56 -0400895 Resources resources = getContext().getResources();
896
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800897 mAm = ActivityManagerNative.getDefault();
Adam Lesinski182f73f2013-12-05 16:48:06 -0800898 mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
899 mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
Amith Yamasanif47e51e2015-04-17 10:02:15 -0700900 mAppUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
San Mehat3ee13172010-02-04 20:54:43 -0800901
Chris Wren0efdb882016-03-01 17:17:47 -0500902 // This is the package that contains the AOSP framework update.
903 mRankerServicePackageName = getContext().getPackageManager()
904 .getServicesSystemSharedLibraryPackageName();
905
Adam Lesinski182f73f2013-12-05 16:48:06 -0800906 mHandler = new WorkerHandler();
Chris Wrenf9536642014-04-17 10:01:54 -0400907 mRankingThread.start();
Chris Wren54bbef42014-07-09 18:37:56 -0400908 String[] extractorNames;
909 try {
910 extractorNames = resources.getStringArray(R.array.config_notificationSignalExtractors);
911 } catch (Resources.NotFoundException e) {
912 extractorNames = new String[0];
913 }
Chris Wren5eab2b72015-06-16 13:56:22 -0400914 mUsageStats = new NotificationUsageStats(getContext());
Chris Wren51017d02015-12-15 15:34:46 -0500915 mRankingHandler = new RankingHandlerWorker(mRankingThread.getLooper());
Chris Wren54bbef42014-07-09 18:37:56 -0400916 mRankingHelper = new RankingHelper(getContext(),
Chris Wren51017d02015-12-15 15:34:46 -0500917 mRankingHandler,
Chris Wren5eab2b72015-06-16 13:56:22 -0400918 mUsageStats,
Chris Wren54bbef42014-07-09 18:37:56 -0400919 extractorNames);
John Spurlockb2278d62015-04-07 12:47:12 -0400920 mConditionProviders = new ConditionProviders(getContext(), mHandler, mUserProfiles);
921 mZenModeHelper = new ZenModeHelper(getContext(), mHandler.getLooper(), mConditionProviders);
John Spurlock1c923a32014-04-27 16:42:29 -0400922 mZenModeHelper.addCallback(new ZenModeHelper.Callback() {
John Spurlock056c5192014-04-20 21:52:01 -0400923 @Override
924 public void onConfigChanged() {
925 savePolicyFile();
926 }
John Spurlockd8afe3c2014-08-01 14:04:07 -0400927
928 @Override
929 void onZenModeChanged() {
John Spurlock80774932015-05-07 17:38:50 -0400930 sendRegisteredOnlyBroadcast(NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED);
Jason Monka9927322015-12-13 16:22:37 -0500931 getContext().sendBroadcastAsUser(
Jason Monk63506742015-12-16 12:06:51 -0500932 new Intent(NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED_INTERNAL)
933 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT),
Jason Monka9927322015-12-13 16:22:37 -0500934 UserHandle.ALL, android.Manifest.permission.MANAGE_NOTIFICATIONS);
John Spurlockd8afe3c2014-08-01 14:04:07 -0400935 synchronized(mNotificationList) {
Christoph Studer85a384b2014-08-27 20:16:15 +0200936 updateInterruptionFilterLocked();
John Spurlockd8afe3c2014-08-01 14:04:07 -0400937 }
938 }
John Spurlock1fc476d2015-04-14 16:05:20 -0400939
940 @Override
941 void onPolicyChanged() {
John Spurlock80774932015-05-07 17:38:50 -0400942 sendRegisteredOnlyBroadcast(NotificationManager.ACTION_NOTIFICATION_POLICY_CHANGED);
943 }
John Spurlock056c5192014-04-20 21:52:01 -0400944 });
945 final File systemDir = new File(Environment.getDataDirectory(), "system");
946 mPolicyFile = new AtomicFile(new File(systemDir, "notification_policy.xml"));
Daniel Sandler4a900ac2013-01-30 14:04:10 -0500947
Chris Wrenacf424a2016-03-15 12:48:55 -0400948 syncBlockDb();
Daniel Sandler0da673f2012-04-11 12:33:16 -0400949
Chris Wren0efdb882016-03-01 17:17:47 -0500950 // This is a MangedServices object that keeps track of the listeners.
John Spurlock7340fc82014-04-24 18:50:12 -0400951 mListeners = new NotificationListeners();
Chris Wren0efdb882016-03-01 17:17:47 -0500952
953 // This is a MangedServices object that keeps track of the ranker.
954 mRankerServices = new NotificationRankers();
955 // Find the updatable ranker and register it.
Julia Reynolds1c9bd422016-03-15 09:25:56 -0400956 mRankerServices.registerRanker();
Chris Wren0efdb882016-03-01 17:17:47 -0500957
Adam Lesinski182f73f2013-12-05 16:48:06 -0800958 mStatusBar = getLocalService(StatusBarManagerInternal.class);
Wei Liu97e56662016-03-04 10:52:33 -0800959 if (mStatusBar != null) {
960 mStatusBar.setNotificationDelegate(mNotificationDelegate);
961 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800962
Adam Lesinski182f73f2013-12-05 16:48:06 -0800963 final LightsManager lights = getLocalService(LightsManager.class);
964 mNotificationLight = lights.getLight(LightsManager.LIGHT_ID_NOTIFICATIONS);
965 mAttentionLight = lights.getLight(LightsManager.LIGHT_ID_ATTENTION);
Mike Lockwood3cb67a32009-11-27 14:25:58 -0500966
Mike Lockwood670f9322010-01-20 12:13:36 -0500967 mDefaultNotificationColor = resources.getColor(
Scott Greenwald9a05b312013-06-28 00:37:54 -0400968 R.color.config_defaultNotificationColor);
Mike Lockwood670f9322010-01-20 12:13:36 -0500969 mDefaultNotificationLedOn = resources.getInteger(
Scott Greenwald9a05b312013-06-28 00:37:54 -0400970 R.integer.config_defaultNotificationLedOn);
Mike Lockwood670f9322010-01-20 12:13:36 -0500971 mDefaultNotificationLedOff = resources.getInteger(
Scott Greenwald9a05b312013-06-28 00:37:54 -0400972 R.integer.config_defaultNotificationLedOff);
Mike Lockwood670f9322010-01-20 12:13:36 -0500973
Daniel Sandleredbb3802012-11-13 20:49:47 -0800974 mDefaultVibrationPattern = getLongArray(resources,
Scott Greenwald9a05b312013-06-28 00:37:54 -0400975 R.array.config_defaultNotificationVibePattern,
Daniel Sandleredbb3802012-11-13 20:49:47 -0800976 VIBRATE_PATTERN_MAXLEN,
977 DEFAULT_VIBRATE_PATTERN);
978
979 mFallbackVibrationPattern = getLongArray(resources,
Scott Greenwald9a05b312013-06-28 00:37:54 -0400980 R.array.config_notificationFallbackVibePattern,
Daniel Sandleredbb3802012-11-13 20:49:47 -0800981 VIBRATE_PATTERN_MAXLEN,
982 DEFAULT_VIBRATE_PATTERN);
983
Chris Wren5116a822014-06-04 15:59:50 -0400984 mUseAttentionLight = resources.getBoolean(R.bool.config_useAttentionLight);
985
Joe Onorato39f5b6a2009-07-23 12:29:19 -0400986 // Don't start allowing notifications until the setup wizard has run once.
987 // After that, including subsequent boots, init with notifications turned on.
988 // This works on the first boot because the setup wizard will toggle this
989 // flag at least once and we'll go back to 0 after that.
Adam Lesinski182f73f2013-12-05 16:48:06 -0800990 if (0 == Settings.Global.getInt(getContext().getContentResolver(),
Jeff Brownbf6f6f92012-09-25 15:03:20 -0700991 Settings.Global.DEVICE_PROVISIONED, 0)) {
John Spurlockd8afe3c2014-08-01 14:04:07 -0400992 mDisableNotificationEffects = true;
Joe Onorato39f5b6a2009-07-23 12:29:19 -0400993 }
John Spurlockb2278d62015-04-07 12:47:12 -0400994 mZenModeHelper.initZenMode();
John Spurlockf3701772015-02-12 13:29:37 -0500995 mInterruptionFilter = mZenModeHelper.getZenModeListenerInterruptionFilter();
Joe Onorato39f5b6a2009-07-23 12:29:19 -0400996
John Spurlockb408e8e2014-04-23 21:12:45 -0400997 mUserProfiles.updateCache(getContext());
John Spurlock32fe4c62014-10-02 12:16:02 -0400998 listenForCallState();
Kenny Guya263e4e2014-03-03 18:24:03 +0000999
Mike Lockwood35e16bf2010-11-30 19:53:36 -05001000 // register for various Intents
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001001 IntentFilter filter = new IntentFilter();
Mike Lockwoodc22404a2009-12-02 11:15:02 -05001002 filter.addAction(Intent.ACTION_SCREEN_ON);
1003 filter.addAction(Intent.ACTION_SCREEN_OFF);
Daniel Sandlere96ffb12010-03-11 13:38:06 -05001004 filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
Mike Lockwood63b5ad92011-08-30 09:55:30 -04001005 filter.addAction(Intent.ACTION_USER_PRESENT);
Dianne Hackborn80a4af22012-08-27 19:18:31 -07001006 filter.addAction(Intent.ACTION_USER_STOPPED);
Daniel Sandler4b749ef2013-03-18 21:53:04 -04001007 filter.addAction(Intent.ACTION_USER_SWITCHED);
Kenny Guy3a7c4a52014-03-03 18:24:03 +00001008 filter.addAction(Intent.ACTION_USER_ADDED);
John Spurlock21258a32015-05-27 18:22:55 -04001009 filter.addAction(Intent.ACTION_USER_REMOVED);
Julia Reynoldsa3dcaff2016-02-03 15:04:05 -05001010 filter.addAction(Intent.ACTION_USER_UNLOCKED);
Rubin Xu7eadc1b2016-02-01 16:13:45 +00001011 filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABILITY_CHANGED);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001012 getContext().registerReceiver(mIntentReceiver, filter);
Kenny Guy70058402014-10-28 20:45:06 +00001013
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08001014 IntentFilter pkgFilter = new IntentFilter();
Chris Wren3da73022013-05-10 14:41:21 -04001015 pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08001016 pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
Daniel Sandleraac0eb02011-08-06 22:51:56 -04001017 pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08001018 pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
1019 pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
1020 pkgFilter.addDataScheme("package");
Kenny Guy70058402014-10-28 20:45:06 +00001021 getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, pkgFilter, null,
1022 null);
1023
Andrei Stingaceanu0122f6512016-01-22 15:33:03 +00001024 IntentFilter suspendedPkgFilter = new IntentFilter();
1025 suspendedPkgFilter.addAction(Intent.ACTION_PACKAGES_SUSPENDED);
1026 getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL,
1027 suspendedPkgFilter, null, null);
1028
Suchi Amalapurapub56ae202010-02-04 22:51:07 -08001029 IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
Kenny Guy70058402014-10-28 20:45:06 +00001030 getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, sdFilter, null,
1031 null);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001032
Daniel Sandler4b749ef2013-03-18 21:53:04 -04001033 mSettingsObserver = new SettingsObserver(mHandler);
Scott Greenwald9a05b312013-06-28 00:37:54 -04001034
Griff Hazen9f637d12014-06-10 11:13:51 -07001035 mArchive = new Archive(resources.getInteger(
1036 R.integer.config_notificationServiceArchiveSize));
1037
Adam Lesinski182f73f2013-12-05 16:48:06 -08001038 publishBinderService(Context.NOTIFICATION_SERVICE, mService);
1039 publishLocalService(NotificationManagerInternal.class, mInternalService);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001040 }
1041
John Spurlocke7a835b2015-05-13 10:47:05 -04001042 private void sendRegisteredOnlyBroadcast(String action) {
1043 getContext().sendBroadcastAsUser(new Intent(action)
1044 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), UserHandle.ALL, null);
1045 }
1046
Daniel Sandler4a900ac2013-01-30 14:04:10 -05001047 /**
Chris Wrenacf424a2016-03-15 12:48:55 -04001048 * Make sure the XML config and the the AppOps system agree about blocks.
Daniel Sandler4a900ac2013-01-30 14:04:10 -05001049 */
Chris Wrenacf424a2016-03-15 12:48:55 -04001050 private void syncBlockDb() {
John Spurlock056c5192014-04-20 21:52:01 -04001051 loadPolicyFile();
Daniel Sandler4a900ac2013-01-30 14:04:10 -05001052
Chris Wrenacf424a2016-03-15 12:48:55 -04001053 // sync bans from ranker into app opps
1054 Map<Integer, String> packageBans = mRankingHelper.getPackageBans();
1055 for(Entry<Integer, String> ban : packageBans.entrySet()) {
1056 final int uid = ban.getKey();
1057 final String packageName = ban.getValue();
1058 setNotificationsEnabledForPackageImpl(packageName, uid, false);
1059 }
1060
1061 // sync bans from app opps into ranker
1062 packageBans.clear();
1063 for (UserInfo user : UserManager.get(getContext()).getUsers()) {
1064 final int userId = user.getUserHandle().getIdentifier();
1065 final PackageManager packageManager = getContext().getPackageManager();
1066 List<PackageInfo> packages = packageManager.getInstalledPackagesAsUser(0, userId);
1067 final int packageCount = packages.size();
1068 for (int p = 0; p < packageCount; p++) {
1069 final String packageName = packages.get(p).packageName;
1070 try {
1071 final int uid = packageManager.getPackageUidAsUser(packageName, userId);
1072 if (!checkNotificationOp(packageName, uid)) {
1073 packageBans.put(uid, packageName);
1074 }
1075 } catch (NameNotFoundException e) {
1076 // forget you
1077 }
Daniel Sandler4a900ac2013-01-30 14:04:10 -05001078 }
1079 }
Chris Wrenacf424a2016-03-15 12:48:55 -04001080 for (Entry<Integer, String> ban : packageBans.entrySet()) {
1081 mRankingHelper.setImportance(ban.getValue(), ban.getKey(), IMPORTANCE_NONE);
1082 }
1083
1084 savePolicyFile();
Daniel Sandler4a900ac2013-01-30 14:04:10 -05001085 }
1086
Adam Lesinski182f73f2013-12-05 16:48:06 -08001087 @Override
1088 public void onBootPhase(int phase) {
1089 if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
1090 // no beeping until we're basically done booting
1091 mSystemReady = true;
Jeff Sharkey098d5802012-04-26 17:30:34 -07001092
Adam Lesinski182f73f2013-12-05 16:48:06 -08001093 // Grab our optional AudioService
1094 mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
John Spurlockcdb57ae2015-02-11 19:04:11 -05001095 mAudioManagerInternal = getLocalService(AudioManagerInternal.class);
Ruben Brunkdd18a0b2015-12-04 16:16:31 -08001096 mVrManagerInternal = getLocalService(VrManagerInternal.class);
John Spurlock661f2cf2014-11-17 10:29:10 -05001097 mZenModeHelper.onSystemReady();
Adam Lesinskia6db4ab2014-03-24 12:31:45 -07001098 } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
1099 // This observer will force an update when observe is called, causing us to
1100 // bind to listener services.
1101 mSettingsObserver.observe();
John Spurlockb408e8e2014-04-23 21:12:45 -04001102 mListeners.onBootPhaseAppsCanStart();
Chris Wrene0ba7eb2016-03-04 17:30:43 -05001103 mRankerServices.onBootPhaseAppsCanStart();
John Spurlock7340fc82014-04-24 18:50:12 -04001104 mConditionProviders.onBootPhaseAppsCanStart();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001105 }
1106 }
1107
Adam Lesinski182f73f2013-12-05 16:48:06 -08001108 void setNotificationsEnabledForPackageImpl(String pkg, int uid, boolean enabled) {
1109 Slog.v(TAG, (enabled?"en":"dis") + "abling notifications for " + pkg);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001110
Adam Lesinski182f73f2013-12-05 16:48:06 -08001111 mAppOps.setMode(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg,
1112 enabled ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001113
Adam Lesinski182f73f2013-12-05 16:48:06 -08001114 // Now, cancel any outstanding notifications that are part of a just-disabled app
1115 if (ENABLE_BLOCKED_NOTIFICATIONS && !enabled) {
John Spurlocke6a7d932014-03-13 12:29:00 -04001116 cancelAllNotificationsInt(MY_UID, MY_PID, pkg, 0, 0, true, UserHandle.getUserId(uid),
Julia Reynoldsef37f282016-02-12 09:11:27 -05001117 REASON_PACKAGE_BANNED, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001118 }
1119 }
1120
John Spurlockd8afe3c2014-08-01 14:04:07 -04001121 private void updateListenerHintsLocked() {
Christoph Studer85a384b2014-08-27 20:16:15 +02001122 final int hints = mListenersDisablingEffects.isEmpty() ? 0 : HINT_HOST_DISABLE_EFFECTS;
John Spurlockd8afe3c2014-08-01 14:04:07 -04001123 if (hints == mListenerHints) return;
John Spurlocka7082992015-03-09 12:19:23 -04001124 ZenLog.traceListenerHintsChanged(mListenerHints, hints, mListenersDisablingEffects.size());
John Spurlockd8afe3c2014-08-01 14:04:07 -04001125 mListenerHints = hints;
1126 scheduleListenerHintsChanged(hints);
John Spurlock1fa865f2014-07-21 14:56:39 -04001127 }
1128
John Spurlockb4782522014-08-22 14:54:46 -04001129 private void updateEffectsSuppressorLocked() {
1130 final ComponentName suppressor = !mListenersDisablingEffects.isEmpty()
1131 ? mListenersDisablingEffects.valueAt(0).component : null;
1132 if (Objects.equals(suppressor, mEffectsSuppressor)) return;
John Spurlocka7082992015-03-09 12:19:23 -04001133 ZenLog.traceEffectsSuppressorChanged(mEffectsSuppressor, suppressor);
John Spurlockb4782522014-08-22 14:54:46 -04001134 mEffectsSuppressor = suppressor;
John Spurlock8403b752014-12-10 12:47:01 -05001135 mZenModeHelper.setEffectsSuppressed(suppressor != null);
John Spurlocke7a835b2015-05-13 10:47:05 -04001136 sendRegisteredOnlyBroadcast(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED);
John Spurlockb4782522014-08-22 14:54:46 -04001137 }
1138
Christoph Studer85a384b2014-08-27 20:16:15 +02001139 private void updateInterruptionFilterLocked() {
1140 int interruptionFilter = mZenModeHelper.getZenModeListenerInterruptionFilter();
1141 if (interruptionFilter == mInterruptionFilter) return;
1142 mInterruptionFilter = interruptionFilter;
1143 scheduleInterruptionFilterChanged(interruptionFilter);
1144 }
1145
Adam Lesinski182f73f2013-12-05 16:48:06 -08001146 private final IBinder mService = new INotificationManager.Stub() {
1147 // Toasts
1148 // ============================================================================
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001149
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001150 @Override
Adam Lesinski182f73f2013-12-05 16:48:06 -08001151 public void enqueueToast(String pkg, ITransientNotification callback, int duration)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001152 {
Adam Lesinski182f73f2013-12-05 16:48:06 -08001153 if (DBG) {
1154 Slog.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback
1155 + " duration=" + duration);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001156 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001157
1158 if (pkg == null || callback == null) {
1159 Slog.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback);
1160 return ;
1161 }
1162
John Spurlock7340fc82014-04-24 18:50:12 -04001163 final boolean isSystemToast = isCallerSystem() || ("android".equals(pkg));
Andrei Stingaceanu355b2322016-02-12 16:43:51 +00001164 final boolean isPackageSuspended =
1165 isPackageSuspendedForUser(pkg, Binder.getCallingUid());
Adam Lesinski182f73f2013-12-05 16:48:06 -08001166
Andrei Stingaceanu0122f6512016-01-22 15:33:03 +00001167 if (ENABLE_BLOCKED_TOASTS && (!noteNotificationOp(pkg, Binder.getCallingUid())
Andrei Stingaceanu355b2322016-02-12 16:43:51 +00001168 || isPackageSuspended)) {
Adam Lesinski182f73f2013-12-05 16:48:06 -08001169 if (!isSystemToast) {
Andrei Stingaceanu0122f6512016-01-22 15:33:03 +00001170 Slog.e(TAG, "Suppressing toast from package " + pkg
Andrei Stingaceanu355b2322016-02-12 16:43:51 +00001171 + (isPackageSuspended
Andrei Stingaceanu0122f6512016-01-22 15:33:03 +00001172 ? " due to package suspended by administrator."
1173 : " by user request."));
Adam Lesinski182f73f2013-12-05 16:48:06 -08001174 return;
1175 }
1176 }
1177
1178 synchronized (mToastQueue) {
1179 int callingPid = Binder.getCallingPid();
1180 long callingId = Binder.clearCallingIdentity();
1181 try {
1182 ToastRecord record;
1183 int index = indexOfToastLocked(pkg, callback);
1184 // If it's already in the queue, we update it in place, we don't
1185 // move it to the end of the queue.
1186 if (index >= 0) {
1187 record = mToastQueue.get(index);
1188 record.update(duration);
1189 } else {
1190 // Limit the number of toasts that any given package except the android
1191 // package can enqueue. Prevents DOS attacks and deals with leaks.
1192 if (!isSystemToast) {
1193 int count = 0;
1194 final int N = mToastQueue.size();
1195 for (int i=0; i<N; i++) {
1196 final ToastRecord r = mToastQueue.get(i);
1197 if (r.pkg.equals(pkg)) {
1198 count++;
1199 if (count >= MAX_PACKAGE_NOTIFICATIONS) {
1200 Slog.e(TAG, "Package has already posted " + count
1201 + " toasts. Not showing more. Package=" + pkg);
1202 return;
1203 }
1204 }
1205 }
1206 }
1207
1208 record = new ToastRecord(callingPid, pkg, callback, duration);
1209 mToastQueue.add(record);
1210 index = mToastQueue.size() - 1;
1211 keepProcessAliveLocked(callingPid);
1212 }
1213 // If it's at index 0, it's the current toast. It doesn't matter if it's
1214 // new or just been updated. Call back and tell it to show itself.
1215 // If the callback fails, this will remove it from the list, so don't
1216 // assume that it's valid after this.
1217 if (index == 0) {
1218 showNextToastLocked();
1219 }
1220 } finally {
1221 Binder.restoreCallingIdentity(callingId);
1222 }
1223 }
1224 }
1225
1226 @Override
1227 public void cancelToast(String pkg, ITransientNotification callback) {
1228 Slog.i(TAG, "cancelToast pkg=" + pkg + " callback=" + callback);
1229
1230 if (pkg == null || callback == null) {
1231 Slog.e(TAG, "Not cancelling notification. pkg=" + pkg + " callback=" + callback);
1232 return ;
1233 }
1234
1235 synchronized (mToastQueue) {
1236 long callingId = Binder.clearCallingIdentity();
1237 try {
1238 int index = indexOfToastLocked(pkg, callback);
1239 if (index >= 0) {
1240 cancelToastLocked(index);
1241 } else {
1242 Slog.w(TAG, "Toast already cancelled. pkg=" + pkg
1243 + " callback=" + callback);
1244 }
1245 } finally {
1246 Binder.restoreCallingIdentity(callingId);
1247 }
1248 }
1249 }
1250
1251 @Override
Christoph Studer8fd7f1e2014-04-11 17:35:05 -04001252 public void enqueueNotificationWithTag(String pkg, String opPkg, String tag, int id,
Adam Lesinski182f73f2013-12-05 16:48:06 -08001253 Notification notification, int[] idOut, int userId) throws RemoteException {
Christoph Studer8fd7f1e2014-04-11 17:35:05 -04001254 enqueueNotificationInternal(pkg, opPkg, Binder.getCallingUid(),
Adam Lesinski182f73f2013-12-05 16:48:06 -08001255 Binder.getCallingPid(), tag, id, notification, idOut, userId);
1256 }
1257
1258 @Override
1259 public void cancelNotificationWithTag(String pkg, String tag, int id, int userId) {
John Spurlock7340fc82014-04-24 18:50:12 -04001260 checkCallerIsSystemOrSameApp(pkg);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001261 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1262 Binder.getCallingUid(), userId, true, false, "cancelNotificationWithTag", pkg);
1263 // Don't allow client applications to cancel foreground service notis.
John Spurlocke6a7d932014-03-13 12:29:00 -04001264 cancelNotification(Binder.getCallingUid(), Binder.getCallingPid(), pkg, tag, id, 0,
Adam Lesinski182f73f2013-12-05 16:48:06 -08001265 Binder.getCallingUid() == Process.SYSTEM_UID
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04001266 ? 0 : Notification.FLAG_FOREGROUND_SERVICE, false, userId,
Chris Wren9fa689f2015-11-20 16:44:53 -05001267 REASON_APP_CANCEL, null);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001268 }
1269
1270 @Override
1271 public void cancelAllNotifications(String pkg, int userId) {
John Spurlock7340fc82014-04-24 18:50:12 -04001272 checkCallerIsSystemOrSameApp(pkg);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001273
1274 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1275 Binder.getCallingUid(), userId, true, false, "cancelAllNotifications", pkg);
1276
1277 // Calling from user space, don't allow the canceling of actively
1278 // running foreground services.
John Spurlocke6a7d932014-03-13 12:29:00 -04001279 cancelAllNotificationsInt(Binder.getCallingUid(), Binder.getCallingPid(),
1280 pkg, 0, Notification.FLAG_FOREGROUND_SERVICE, true, userId,
Julia Reynoldsef37f282016-02-12 09:11:27 -05001281 REASON_APP_CANCEL_ALL, null);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001282 }
1283
1284 @Override
1285 public void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) {
John Spurlock7340fc82014-04-24 18:50:12 -04001286 checkCallerIsSystem();
Adam Lesinski182f73f2013-12-05 16:48:06 -08001287
1288 setNotificationsEnabledForPackageImpl(pkg, uid, enabled);
Chris Wrenacf424a2016-03-15 12:48:55 -04001289 mRankingHelper.setEnabled(pkg, uid, enabled);
1290 savePolicyFile();
Adam Lesinski182f73f2013-12-05 16:48:06 -08001291 }
1292
1293 /**
1294 * Use this when you just want to know if notifications are OK for this package.
1295 */
1296 @Override
Julia Reynolds81afbcd2016-02-09 14:54:08 -05001297 public boolean areNotificationsEnabled(String pkg) {
1298 return areNotificationsEnabledForPackage(pkg, Binder.getCallingUid());
1299 }
1300
1301 /**
1302 * Use this when you just want to know if notifications are OK for this package.
1303 */
1304 @Override
Adam Lesinski182f73f2013-12-05 16:48:06 -08001305 public boolean areNotificationsEnabledForPackage(String pkg, int uid) {
Julia Reynolds81afbcd2016-02-09 14:54:08 -05001306 checkCallerIsSystemOrSameApp(pkg);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001307 return (mAppOps.checkOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
Andrei Stingaceanu355b2322016-02-12 16:43:51 +00001308 == AppOpsManager.MODE_ALLOWED) && !isPackageSuspendedForUser(pkg, uid);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001309 }
1310
Chris Wren54bbef42014-07-09 18:37:56 -04001311 @Override
Julia Reynoldsef37f282016-02-12 09:11:27 -05001312 public void setPriority(String pkg, int uid, int priority) {
Julia Reynoldsdd3e86b2016-02-02 10:24:30 -05001313 checkCallerIsSystem();
Julia Reynoldsef37f282016-02-12 09:11:27 -05001314 mRankingHelper.setPriority(pkg, uid, priority);
Chris Wren54bbef42014-07-09 18:37:56 -04001315 savePolicyFile();
1316 }
1317
1318 @Override
Julia Reynoldsef37f282016-02-12 09:11:27 -05001319 public int getPriority(String pkg, int uid) {
Chris Wren54bbef42014-07-09 18:37:56 -04001320 checkCallerIsSystem();
Julia Reynoldsef37f282016-02-12 09:11:27 -05001321 return mRankingHelper.getPriority(pkg, uid);
Chris Wren54bbef42014-07-09 18:37:56 -04001322 }
1323
Chris Wren3ad4e3a2014-09-02 17:23:51 -04001324 @Override
Julia Reynoldsef37f282016-02-12 09:11:27 -05001325 public void setVisibilityOverride(String pkg, int uid, int visibility) {
Chris Wren3ad4e3a2014-09-02 17:23:51 -04001326 checkCallerIsSystem();
Julia Reynoldsef37f282016-02-12 09:11:27 -05001327 mRankingHelper.setVisibilityOverride(pkg, uid, visibility);
Chris Wren3ad4e3a2014-09-02 17:23:51 -04001328 savePolicyFile();
1329 }
1330
1331 @Override
Julia Reynoldsef37f282016-02-12 09:11:27 -05001332 public int getVisibilityOverride(String pkg, int uid) {
Chris Wren3ad4e3a2014-09-02 17:23:51 -04001333 checkCallerIsSystem();
Julia Reynoldsef37f282016-02-12 09:11:27 -05001334 return mRankingHelper.getVisibilityOverride(pkg, uid);
Chris Wren3ad4e3a2014-09-02 17:23:51 -04001335 }
1336
Julia Reynolds5d25ee72015-11-20 15:38:20 -05001337 @Override
Julia Reynolds0edb50c2016-02-26 14:08:25 -05001338 public void setImportance(String pkg, int uid, int importance) {
Julia Reynoldsead00aa2015-12-07 08:23:48 -05001339 enforceSystemOrSystemUI("Caller not system or systemui");
Julia Reynoldsef37f282016-02-12 09:11:27 -05001340 setNotificationsEnabledForPackageImpl(pkg, uid,
1341 importance != NotificationListenerService.Ranking.IMPORTANCE_NONE);
1342 mRankingHelper.setImportance(pkg, uid, importance);
Julia Reynolds5d25ee72015-11-20 15:38:20 -05001343 savePolicyFile();
1344 }
1345
1346 @Override
Julia Reynoldsef37f282016-02-12 09:11:27 -05001347 public int getPackageImportance(String pkg) {
Julia Reynolds81afbcd2016-02-09 14:54:08 -05001348 checkCallerIsSystemOrSameApp(pkg);
Julia Reynoldsef37f282016-02-12 09:11:27 -05001349 return mRankingHelper.getImportance(pkg, Binder.getCallingUid());
Julia Reynolds81afbcd2016-02-09 14:54:08 -05001350 }
1351
1352 @Override
Julia Reynoldsef37f282016-02-12 09:11:27 -05001353 public int getImportance(String pkg, int uid) {
Julia Reynoldscac88622016-03-03 09:28:19 -05001354 enforceSystemOrSystemUI("Caller not system or systemui");
Julia Reynoldsef37f282016-02-12 09:11:27 -05001355 return mRankingHelper.getImportance(pkg, uid);
Julia Reynoldsbe8fdee2015-12-18 09:04:34 -05001356 }
1357
Adam Lesinski182f73f2013-12-05 16:48:06 -08001358 /**
1359 * System-only API for getting a list of current (i.e. not cleared) notifications.
1360 *
1361 * Requires ACCESS_NOTIFICATIONS which is signature|system.
Chris Wrenf9536642014-04-17 10:01:54 -04001362 * @returns A list of all the notifications, in natural order.
Adam Lesinski182f73f2013-12-05 16:48:06 -08001363 */
1364 @Override
1365 public StatusBarNotification[] getActiveNotifications(String callingPkg) {
1366 // enforce() will ensure the calling uid has the correct permission
1367 getContext().enforceCallingOrSelfPermission(
1368 android.Manifest.permission.ACCESS_NOTIFICATIONS,
1369 "NotificationManagerService.getActiveNotifications");
1370
1371 StatusBarNotification[] tmp = null;
1372 int uid = Binder.getCallingUid();
1373
1374 // noteOp will check to make sure the callingPkg matches the uid
1375 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
1376 == AppOpsManager.MODE_ALLOWED) {
1377 synchronized (mNotificationList) {
1378 tmp = new StatusBarNotification[mNotificationList.size()];
1379 final int N = mNotificationList.size();
1380 for (int i=0; i<N; i++) {
1381 tmp[i] = mNotificationList.get(i).sbn;
1382 }
1383 }
1384 }
1385 return tmp;
1386 }
1387
1388 /**
Dan Sandler994349c2015-04-15 11:02:54 -04001389 * Public API for getting a list of current notifications for the calling package/uid.
1390 *
1391 * @returns A list of all the package's notifications, in natural order.
1392 */
1393 @Override
1394 public ParceledListSlice<StatusBarNotification> getAppActiveNotifications(String pkg,
1395 int incomingUserId) {
1396 checkCallerIsSystemOrSameApp(pkg);
1397 int userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1398 Binder.getCallingUid(), incomingUserId, true, false,
1399 "getAppActiveNotifications", pkg);
1400
Erik Wolsheimer2242b4d2015-11-24 13:22:04 -08001401 final ArrayList<StatusBarNotification> list
1402 = new ArrayList<StatusBarNotification>(mNotificationList.size());
Dan Sandler994349c2015-04-15 11:02:54 -04001403
1404 synchronized (mNotificationList) {
Erik Wolsheimer2242b4d2015-11-24 13:22:04 -08001405 final int N = mNotificationList.size();
Dan Sandler994349c2015-04-15 11:02:54 -04001406 for (int i = 0; i < N; i++) {
1407 final StatusBarNotification sbn = mNotificationList.get(i).sbn;
1408 if (sbn.getPackageName().equals(pkg) && sbn.getUserId() == userId) {
1409 // We could pass back a cloneLight() but clients might get confused and
1410 // try to send this thing back to notify() again, which would not work
1411 // very well.
1412 final StatusBarNotification sbnOut = new StatusBarNotification(
1413 sbn.getPackageName(),
1414 sbn.getOpPkg(),
1415 sbn.getId(), sbn.getTag(), sbn.getUid(), sbn.getInitialPid(),
1416 0, // hide score from apps
1417 sbn.getNotification().clone(),
1418 sbn.getUser(), sbn.getPostTime());
1419 list.add(sbnOut);
1420 }
1421 }
1422 }
1423
1424 return new ParceledListSlice<StatusBarNotification>(list);
1425 }
1426
1427 /**
Adam Lesinski182f73f2013-12-05 16:48:06 -08001428 * System-only API for getting a list of recent (cleared, no longer shown) notifications.
1429 *
1430 * Requires ACCESS_NOTIFICATIONS which is signature|system.
1431 */
1432 @Override
1433 public StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count) {
1434 // enforce() will ensure the calling uid has the correct permission
1435 getContext().enforceCallingOrSelfPermission(
1436 android.Manifest.permission.ACCESS_NOTIFICATIONS,
1437 "NotificationManagerService.getHistoricalNotifications");
1438
1439 StatusBarNotification[] tmp = null;
1440 int uid = Binder.getCallingUid();
1441
1442 // noteOp will check to make sure the callingPkg matches the uid
1443 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
1444 == AppOpsManager.MODE_ALLOWED) {
1445 synchronized (mArchive) {
1446 tmp = mArchive.getArray(count);
1447 }
1448 }
1449 return tmp;
1450 }
1451
1452 /**
1453 * Register a listener binder directly with the notification manager.
1454 *
1455 * Only works with system callers. Apps should extend
1456 * {@link android.service.notification.NotificationListenerService}.
1457 */
1458 @Override
1459 public void registerListener(final INotificationListener listener,
Chris Wren0efdb882016-03-01 17:17:47 -05001460 final ComponentName component, final int userid) {
Christoph Studer3e144d32014-05-22 16:48:40 +02001461 enforceSystemOrSystemUI("INotificationManager.registerListener");
Chris Wren0efdb882016-03-01 17:17:47 -05001462 mListeners.registerService(listener, component, userid);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001463 }
1464
1465 /**
1466 * Remove a listener binder directly
1467 */
1468 @Override
Chris Wrene0ba7eb2016-03-04 17:30:43 -05001469 public void unregisterListener(INotificationListener token, int userid) {
Chris Wrenb7c81092016-03-10 11:41:10 -05001470 mListeners.unregisterService(token, userid);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001471 }
1472
1473 /**
1474 * Allow an INotificationListener to simulate a "clear all" operation.
1475 *
1476 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onClearAllNotifications}
1477 *
1478 * @param token The binder for the listener, to check that the caller is allowed
1479 */
1480 @Override
John Spurlocka4294292014-03-24 18:02:32 -04001481 public void cancelNotificationsFromListener(INotificationListener token, String[] keys) {
John Spurlocke6a7d932014-03-13 12:29:00 -04001482 final int callingUid = Binder.getCallingUid();
1483 final int callingPid = Binder.getCallingPid();
Adam Lesinski182f73f2013-12-05 16:48:06 -08001484 long identity = Binder.clearCallingIdentity();
1485 try {
Adam Lesinskie8240262014-03-26 16:01:00 -07001486 synchronized (mNotificationList) {
John Spurlock7340fc82014-04-24 18:50:12 -04001487 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
John Spurlocka4294292014-03-24 18:02:32 -04001488 if (keys != null) {
1489 final int N = keys.length;
1490 for (int i = 0; i < N; i++) {
1491 NotificationRecord r = mNotificationsByKey.get(keys[i]);
Griff Hazen335e1f02014-09-11 14:49:31 -07001492 if (r == null) continue;
Kenny Guya263e4e2014-03-03 18:24:03 +00001493 final int userId = r.sbn.getUserId();
1494 if (userId != info.userid && userId != UserHandle.USER_ALL &&
John Spurlockb408e8e2014-04-23 21:12:45 -04001495 !mUserProfiles.isCurrentProfile(userId)) {
Kenny Guya263e4e2014-03-03 18:24:03 +00001496 throw new SecurityException("Disallowed call from listener: "
John Spurlock7340fc82014-04-24 18:50:12 -04001497 + info.service);
Kenny Guya263e4e2014-03-03 18:24:03 +00001498 }
Griff Hazen335e1f02014-09-11 14:49:31 -07001499 cancelNotificationFromListenerLocked(info, callingUid, callingPid,
1500 r.sbn.getPackageName(), r.sbn.getTag(), r.sbn.getId(),
1501 userId);
John Spurlocka4294292014-03-24 18:02:32 -04001502 }
1503 } else {
1504 cancelAllLocked(callingUid, callingPid, info.userid,
Kenny Guya263e4e2014-03-03 18:24:03 +00001505 REASON_LISTENER_CANCEL_ALL, info, info.supportsProfiles());
John Spurlocka4294292014-03-24 18:02:32 -04001506 }
Adam Lesinskie8240262014-03-26 16:01:00 -07001507 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001508 } finally {
1509 Binder.restoreCallingIdentity(identity);
1510 }
1511 }
1512
Chris Wrenab41eec2016-01-04 18:01:27 -05001513 /**
1514 * Handle request from an approved listener to re-enable itself.
1515 *
1516 * @param component The componenet to be re-enabled, caller must match package.
1517 */
1518 @Override
1519 public void requestBindListener(ComponentName component) {
1520 checkCallerIsSystemOrSameApp(component.getPackageName());
1521 long identity = Binder.clearCallingIdentity();
1522 try {
Chris Wrene0ba7eb2016-03-04 17:30:43 -05001523 ManagedServices manager = mRankerServices.isComponentEnabledForCurrentProfiles(component)
1524 ? mRankerServices
Chris Wrenab41eec2016-01-04 18:01:27 -05001525 : mListeners;
1526 manager.setComponentState(component, true);
1527 } finally {
1528 Binder.restoreCallingIdentity(identity);
1529 }
1530 }
1531
1532 @Override
1533 public void requestUnbindListener(INotificationListener token) {
1534 long identity = Binder.clearCallingIdentity();
1535 try {
1536 // allow bound services to disable themselves
1537 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1538 info.getOwner().setComponentState(info.component, false);
1539 } finally {
1540 Binder.restoreCallingIdentity(identity);
1541 }
1542 }
1543
Amith Yamasanif47e51e2015-04-17 10:02:15 -07001544 @Override
1545 public void setNotificationsShownFromListener(INotificationListener token, String[] keys) {
Amith Yamasanif47e51e2015-04-17 10:02:15 -07001546 long identity = Binder.clearCallingIdentity();
1547 try {
1548 synchronized (mNotificationList) {
1549 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1550 if (keys != null) {
1551 final int N = keys.length;
1552 for (int i = 0; i < N; i++) {
1553 NotificationRecord r = mNotificationsByKey.get(keys[i]);
1554 if (r == null) continue;
1555 final int userId = r.sbn.getUserId();
1556 if (userId != info.userid && userId != UserHandle.USER_ALL &&
1557 !mUserProfiles.isCurrentProfile(userId)) {
1558 throw new SecurityException("Disallowed call from listener: "
1559 + info.service);
1560 }
1561 if (!r.isSeen()) {
1562 if (DBG) Slog.d(TAG, "Marking notification as seen " + keys[i]);
1563 mAppUsageStats.reportEvent(r.sbn.getPackageName(),
Xiaohui Chenddbe4ca2015-08-13 16:20:56 -07001564 userId == UserHandle.USER_ALL ? UserHandle.USER_SYSTEM
Amith Yamasanif47e51e2015-04-17 10:02:15 -07001565 : userId,
Adam Lesinskic8e87292015-06-10 15:33:45 -07001566 UsageEvents.Event.USER_INTERACTION);
Amith Yamasanif47e51e2015-04-17 10:02:15 -07001567 r.setSeen();
1568 }
1569 }
1570 }
1571 }
1572 } finally {
1573 Binder.restoreCallingIdentity(identity);
1574 }
1575 }
1576
John Spurlock7340fc82014-04-24 18:50:12 -04001577 private void cancelNotificationFromListenerLocked(ManagedServiceInfo info,
Kenny Guya263e4e2014-03-03 18:24:03 +00001578 int callingUid, int callingPid, String pkg, String tag, int id, int userId) {
John Spurlocka4294292014-03-24 18:02:32 -04001579 cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
1580 Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
1581 true,
Kenny Guya263e4e2014-03-03 18:24:03 +00001582 userId, REASON_LISTENER_CANCEL, info);
John Spurlocka4294292014-03-24 18:02:32 -04001583 }
1584
Adam Lesinski182f73f2013-12-05 16:48:06 -08001585 /**
1586 * Allow an INotificationListener to simulate clearing (dismissing) a single notification.
1587 *
1588 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onNotificationClear}
1589 *
1590 * @param token The binder for the listener, to check that the caller is allowed
1591 */
1592 @Override
1593 public void cancelNotificationFromListener(INotificationListener token, String pkg,
1594 String tag, int id) {
John Spurlocke6a7d932014-03-13 12:29:00 -04001595 final int callingUid = Binder.getCallingUid();
1596 final int callingPid = Binder.getCallingPid();
Adam Lesinski182f73f2013-12-05 16:48:06 -08001597 long identity = Binder.clearCallingIdentity();
1598 try {
Adam Lesinskie8240262014-03-26 16:01:00 -07001599 synchronized (mNotificationList) {
John Spurlock7340fc82014-04-24 18:50:12 -04001600 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
Kenny Guya263e4e2014-03-03 18:24:03 +00001601 if (info.supportsProfiles()) {
1602 Log.e(TAG, "Ignoring deprecated cancelNotification(pkg, tag, id) "
1603 + "from " + info.component
1604 + " use cancelNotification(key) instead.");
1605 } else {
1606 cancelNotificationFromListenerLocked(info, callingUid, callingPid,
1607 pkg, tag, id, info.userid);
1608 }
Adam Lesinskie8240262014-03-26 16:01:00 -07001609 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001610 } finally {
1611 Binder.restoreCallingIdentity(identity);
1612 }
1613 }
1614
1615 /**
1616 * Allow an INotificationListener to request the list of outstanding notifications seen by
1617 * the current user. Useful when starting up, after which point the listener callbacks
1618 * should be used.
1619 *
1620 * @param token The binder for the listener, to check that the caller is allowed
Dan Sandlerea75fdd2014-08-12 12:29:19 -04001621 * @param keys An array of notification keys to fetch, or null to fetch everything
Chris Wrenf9536642014-04-17 10:01:54 -04001622 * @returns The return value will contain the notifications specified in keys, in that
1623 * order, or if keys is null, all the notifications, in natural order.
Adam Lesinski182f73f2013-12-05 16:48:06 -08001624 */
1625 @Override
Christoph Studercee44ba2014-05-20 18:36:43 +02001626 public ParceledListSlice<StatusBarNotification> getActiveNotificationsFromListener(
Christoph Studerb82bc782014-08-20 14:29:43 +02001627 INotificationListener token, String[] keys, int trim) {
Adam Lesinski182f73f2013-12-05 16:48:06 -08001628 synchronized (mNotificationList) {
John Spurlock7340fc82014-04-24 18:50:12 -04001629 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
Dan Sandlerea75fdd2014-08-12 12:29:19 -04001630 final boolean getKeys = keys != null;
1631 final int N = getKeys ? keys.length : mNotificationList.size();
Christoph Studerb82bc782014-08-20 14:29:43 +02001632 final ArrayList<StatusBarNotification> list
1633 = new ArrayList<StatusBarNotification>(N);
Christoph Studercee44ba2014-05-20 18:36:43 +02001634 for (int i=0; i<N; i++) {
Dan Sandlerea75fdd2014-08-12 12:29:19 -04001635 final NotificationRecord r = getKeys
1636 ? mNotificationsByKey.get(keys[i])
1637 : mNotificationList.get(i);
Christoph Studerb82bc782014-08-20 14:29:43 +02001638 if (r == null) continue;
1639 StatusBarNotification sbn = r.sbn;
1640 if (!isVisibleToListener(sbn, info)) continue;
1641 StatusBarNotification sbnToSend =
1642 (trim == TRIM_FULL) ? sbn : sbn.cloneLight();
1643 list.add(sbnToSend);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001644 }
Christoph Studercee44ba2014-05-20 18:36:43 +02001645 return new ParceledListSlice<StatusBarNotification>(list);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001646 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001647 }
1648
1649 @Override
John Spurlockd8afe3c2014-08-01 14:04:07 -04001650 public void requestHintsFromListener(INotificationListener token, int hints) {
1651 final long identity = Binder.clearCallingIdentity();
1652 try {
1653 synchronized (mNotificationList) {
1654 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1655 final boolean disableEffects = (hints & HINT_HOST_DISABLE_EFFECTS) != 0;
1656 if (disableEffects) {
1657 mListenersDisablingEffects.add(info);
1658 } else {
1659 mListenersDisablingEffects.remove(info);
1660 }
John Spurlockd8afe3c2014-08-01 14:04:07 -04001661 updateListenerHintsLocked();
John Spurlockb4782522014-08-22 14:54:46 -04001662 updateEffectsSuppressorLocked();
John Spurlock1fa865f2014-07-21 14:56:39 -04001663 }
John Spurlockd8afe3c2014-08-01 14:04:07 -04001664 } finally {
1665 Binder.restoreCallingIdentity(identity);
John Spurlock1fa865f2014-07-21 14:56:39 -04001666 }
1667 }
1668
1669 @Override
John Spurlockd8afe3c2014-08-01 14:04:07 -04001670 public int getHintsFromListener(INotificationListener token) {
John Spurlock1fa865f2014-07-21 14:56:39 -04001671 synchronized (mNotificationList) {
John Spurlockd8afe3c2014-08-01 14:04:07 -04001672 return mListenerHints;
John Spurlock1fa865f2014-07-21 14:56:39 -04001673 }
1674 }
1675
1676 @Override
Christoph Studer85a384b2014-08-27 20:16:15 +02001677 public void requestInterruptionFilterFromListener(INotificationListener token,
1678 int interruptionFilter) throws RemoteException {
1679 final long identity = Binder.clearCallingIdentity();
1680 try {
1681 synchronized (mNotificationList) {
John Spurlock661f2cf2014-11-17 10:29:10 -05001682 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1683 mZenModeHelper.requestFromListener(info.component, interruptionFilter);
Christoph Studer85a384b2014-08-27 20:16:15 +02001684 updateInterruptionFilterLocked();
1685 }
1686 } finally {
1687 Binder.restoreCallingIdentity(identity);
1688 }
1689 }
1690
1691 @Override
1692 public int getInterruptionFilterFromListener(INotificationListener token)
1693 throws RemoteException {
1694 synchronized (mNotificationLight) {
1695 return mInterruptionFilter;
1696 }
1697 }
1698
1699 @Override
Christoph Studerb82bc782014-08-20 14:29:43 +02001700 public void setOnNotificationPostedTrimFromListener(INotificationListener token, int trim)
1701 throws RemoteException {
1702 synchronized (mNotificationList) {
1703 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1704 if (info == null) return;
1705 mListeners.setOnNotificationPostedTrimLocked(info, trim);
1706 }
1707 }
1708
1709 @Override
John Spurlockb2278d62015-04-07 12:47:12 -04001710 public int getZenMode() {
1711 return mZenModeHelper.getZenMode();
1712 }
1713
1714 @Override
John Spurlock056c5192014-04-20 21:52:01 -04001715 public ZenModeConfig getZenModeConfig() {
John Spurlockcdb57ae2015-02-11 19:04:11 -05001716 enforceSystemOrSystemUIOrVolume("INotificationManager.getZenModeConfig");
John Spurlock056c5192014-04-20 21:52:01 -04001717 return mZenModeHelper.getConfig();
1718 }
1719
1720 @Override
John Spurlockb2278d62015-04-07 12:47:12 -04001721 public void setZenMode(int mode, Uri conditionId, String reason) throws RemoteException {
John Spurlockcdb57ae2015-02-11 19:04:11 -05001722 enforceSystemOrSystemUIOrVolume("INotificationManager.setZenMode");
1723 final long identity = Binder.clearCallingIdentity();
1724 try {
John Spurlockb2278d62015-04-07 12:47:12 -04001725 mZenModeHelper.setManualZenMode(mode, conditionId, reason);
John Spurlockcdb57ae2015-02-11 19:04:11 -05001726 } finally {
1727 Binder.restoreCallingIdentity(identity);
1728 }
1729 }
1730
1731 @Override
Julia Reynolds361e82d32016-02-26 18:19:49 -05001732 public List<ZenModeConfig.ZenRule> getZenRules() throws RemoteException {
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04001733 enforcePolicyAccess(Binder.getCallingUid(), "getAutomaticZenRules");
Julia Reynolds361e82d32016-02-26 18:19:49 -05001734 return mZenModeHelper.getZenRules();
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04001735 }
1736
1737 @Override
Julia Reynolds4fe98d62015-10-06 16:23:41 -04001738 public AutomaticZenRule getAutomaticZenRule(String id) throws RemoteException {
1739 Preconditions.checkNotNull(id, "Id is null");
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04001740 enforcePolicyAccess(Binder.getCallingUid(), "getAutomaticZenRule");
Julia Reynolds4fe98d62015-10-06 16:23:41 -04001741 return mZenModeHelper.getAutomaticZenRule(id);
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04001742 }
1743
1744 @Override
Julia Reynolds361e82d32016-02-26 18:19:49 -05001745 public String addAutomaticZenRule(AutomaticZenRule automaticZenRule)
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04001746 throws RemoteException {
1747 Preconditions.checkNotNull(automaticZenRule, "automaticZenRule is null");
1748 Preconditions.checkNotNull(automaticZenRule.getName(), "Name is null");
1749 Preconditions.checkNotNull(automaticZenRule.getOwner(), "Owner is null");
1750 Preconditions.checkNotNull(automaticZenRule.getConditionId(), "ConditionId is null");
Julia Reynolds4fe98d62015-10-06 16:23:41 -04001751 enforcePolicyAccess(Binder.getCallingUid(), "addAutomaticZenRule");
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04001752
Julia Reynolds4fe98d62015-10-06 16:23:41 -04001753 return mZenModeHelper.addAutomaticZenRule(automaticZenRule,
1754 "addAutomaticZenRule");
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04001755 }
1756
1757 @Override
Julia Reynolds361e82d32016-02-26 18:19:49 -05001758 public boolean updateAutomaticZenRule(String id, AutomaticZenRule automaticZenRule)
Julia Reynolds4fe98d62015-10-06 16:23:41 -04001759 throws RemoteException {
1760 Preconditions.checkNotNull(automaticZenRule, "automaticZenRule is null");
1761 Preconditions.checkNotNull(automaticZenRule.getName(), "Name is null");
1762 Preconditions.checkNotNull(automaticZenRule.getOwner(), "Owner is null");
1763 Preconditions.checkNotNull(automaticZenRule.getConditionId(), "ConditionId is null");
1764 enforcePolicyAccess(Binder.getCallingUid(), "updateAutomaticZenRule");
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04001765
Julia Reynolds361e82d32016-02-26 18:19:49 -05001766 return mZenModeHelper.updateAutomaticZenRule(id, automaticZenRule,
Julia Reynolds4fe98d62015-10-06 16:23:41 -04001767 "updateAutomaticZenRule");
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04001768 }
1769
1770 @Override
Julia Reynolds4fe98d62015-10-06 16:23:41 -04001771 public boolean removeAutomaticZenRule(String id) throws RemoteException {
1772 Preconditions.checkNotNull(id, "Id is null");
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04001773 // Verify that they can modify zen rules.
1774 enforcePolicyAccess(Binder.getCallingUid(), "removeAutomaticZenRule");
1775
Julia Reynolds4fe98d62015-10-06 16:23:41 -04001776 return mZenModeHelper.removeAutomaticZenRule(id, "removeAutomaticZenRule");
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04001777 }
1778
1779 @Override
Julia Reynoldsc8e54e82015-11-30 16:43:05 -05001780 public boolean removeAutomaticZenRules(String packageName) throws RemoteException {
1781 Preconditions.checkNotNull(packageName, "Package name is null");
1782 enforceSystemOrSystemUI("removeAutomaticZenRules");
1783
1784 return mZenModeHelper.removeAutomaticZenRules(packageName, "removeAutomaticZenRules");
1785 }
1786
1787 @Override
Julia Reynolds43b70cd2016-01-14 15:05:34 -05001788 public int getRuleInstanceCount(ComponentName owner) throws RemoteException {
1789 Preconditions.checkNotNull(owner, "Owner is null");
1790 enforceSystemOrSystemUI("getRuleInstanceCount");
1791
1792 return mZenModeHelper.getCurrentInstanceCount(owner);
1793 }
1794
1795 @Override
John Spurlock80774932015-05-07 17:38:50 -04001796 public void setInterruptionFilter(String pkg, int filter) throws RemoteException {
1797 enforcePolicyAccess(pkg, "setInterruptionFilter");
1798 final int zen = NotificationManager.zenModeFromInterruptionFilter(filter, -1);
1799 if (zen == -1) throw new IllegalArgumentException("Invalid filter: " + filter);
1800 final long identity = Binder.clearCallingIdentity();
1801 try {
1802 mZenModeHelper.setManualZenMode(zen, null, "setInterruptionFilter");
1803 } finally {
1804 Binder.restoreCallingIdentity(identity);
1805 }
1806 }
1807
1808 @Override
John Spurlocka7d92b12015-05-13 14:48:02 -04001809 public void notifyConditions(final String pkg, IConditionProvider provider,
1810 final Condition[] conditions) {
John Spurlocke77bb362014-04-26 10:24:59 -04001811 final ManagedServiceInfo info = mConditionProviders.checkServiceToken(provider);
1812 checkCallerIsSystemOrSameApp(pkg);
John Spurlocka7d92b12015-05-13 14:48:02 -04001813 mHandler.post(new Runnable() {
1814 @Override
1815 public void run() {
1816 mConditionProviders.notifyConditions(pkg, info, conditions);
1817 }
1818 });
John Spurlocke77bb362014-04-26 10:24:59 -04001819 }
1820
John Spurlockcdb57ae2015-02-11 19:04:11 -05001821 private void enforceSystemOrSystemUIOrVolume(String message) {
1822 if (mAudioManagerInternal != null) {
1823 final int vcuid = mAudioManagerInternal.getVolumeControllerUid();
1824 if (vcuid > 0 && Binder.getCallingUid() == vcuid) {
1825 return;
1826 }
1827 }
1828 enforceSystemOrSystemUI(message);
1829 }
1830
John Spurlocke77bb362014-04-26 10:24:59 -04001831 private void enforceSystemOrSystemUI(String message) {
1832 if (isCallerSystem()) return;
1833 getContext().enforceCallingPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
1834 message);
John Spurlock7340fc82014-04-24 18:50:12 -04001835 }
1836
Julia Reynolds48034f82016-03-09 10:15:16 -05001837 private void enforceSystemOrSystemUIOrSamePackage(String pkg, String message) {
1838 try {
1839 checkCallerIsSystemOrSameApp(pkg);
1840 } catch (SecurityException e) {
1841 getContext().enforceCallingPermission(
1842 android.Manifest.permission.STATUS_BAR_SERVICE,
1843 message);
1844 }
1845 }
1846
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04001847 private void enforcePolicyAccess(int uid, String method) {
1848 if (PackageManager.PERMISSION_GRANTED == getContext().checkCallingPermission(
1849 android.Manifest.permission.MANAGE_NOTIFICATIONS)) {
1850 return;
1851 }
1852 boolean accessAllowed = false;
1853 String[] packages = getContext().getPackageManager().getPackagesForUid(uid);
1854 final int packageCount = packages.length;
1855 for (int i = 0; i < packageCount; i++) {
1856 if (checkPolicyAccess(packages[i])) {
1857 accessAllowed = true;
1858 }
1859 }
1860 if (!accessAllowed) {
1861 Slog.w(TAG, "Notification policy access denied calling " + method);
1862 throw new SecurityException("Notification policy access denied");
1863 }
1864 }
1865
John Spurlock80774932015-05-07 17:38:50 -04001866 private void enforcePolicyAccess(String pkg, String method) {
Julia Reynolds6ee26172015-09-28 11:34:48 -04001867 if (PackageManager.PERMISSION_GRANTED == getContext().checkCallingPermission(
1868 android.Manifest.permission.MANAGE_NOTIFICATIONS)) {
1869 return;
1870 }
John Spurlock80774932015-05-07 17:38:50 -04001871 if (!checkPolicyAccess(pkg)) {
1872 Slog.w(TAG, "Notification policy access denied calling " + method);
1873 throw new SecurityException("Notification policy access denied");
John Spurlock1fc476d2015-04-14 16:05:20 -04001874 }
1875 }
1876
John Spurlock80774932015-05-07 17:38:50 -04001877 private boolean checkPackagePolicyAccess(String pkg) {
John Spurlock7c74f782015-06-04 13:01:42 -04001878 return mPolicyAccess.isPackageGranted(pkg);
John Spurlock80774932015-05-07 17:38:50 -04001879 }
1880
1881 private boolean checkPolicyAccess(String pkg) {
Julia Reynolds0867b3a2016-03-30 17:29:54 -04001882 try {
1883 int uid = getContext().getPackageManager().getPackageUidAsUser(
1884 pkg, UserHandle.getCallingUserId());
1885 if (PackageManager.PERMISSION_GRANTED == ActivityManager.checkComponentPermission(
1886 android.Manifest.permission.MANAGE_NOTIFICATIONS, uid,
1887 -1, true)) {
1888 return true;
1889 }
1890 } catch (NameNotFoundException e) {
1891 return false;
Julia Reynoldsa2d01022016-03-18 15:03:43 -04001892 }
John Spurlock80774932015-05-07 17:38:50 -04001893 return checkPackagePolicyAccess(pkg) || mListeners.isComponentEnabledForPackage(pkg);
John Spurlock1fc476d2015-04-14 16:05:20 -04001894 }
1895
John Spurlock7340fc82014-04-24 18:50:12 -04001896 @Override
Adam Lesinski182f73f2013-12-05 16:48:06 -08001897 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1898 if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
1899 != PackageManager.PERMISSION_GRANTED) {
John Spurlock2b122f42014-08-27 16:29:47 -04001900 pw.println("Permission Denial: can't dump NotificationManager from pid="
Adam Lesinski182f73f2013-12-05 16:48:06 -08001901 + Binder.getCallingPid()
1902 + ", uid=" + Binder.getCallingUid());
1903 return;
1904 }
1905
Chris Wrene4b38802015-07-07 15:54:19 -04001906 final DumpFilter filter = DumpFilter.parseFromArguments(args);
1907 if (filter != null && filter.stats) {
1908 dumpJson(pw, filter);
1909 } else {
1910 dumpImpl(pw, filter);
1911 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001912 }
John Spurlockb4782522014-08-22 14:54:46 -04001913
1914 @Override
1915 public ComponentName getEffectsSuppressor() {
John Spurlockcdb57ae2015-02-11 19:04:11 -05001916 enforceSystemOrSystemUIOrVolume("INotificationManager.getEffectsSuppressor");
John Spurlockb4782522014-08-22 14:54:46 -04001917 return mEffectsSuppressor;
1918 }
John Spurlock2b122f42014-08-27 16:29:47 -04001919
1920 @Override
1921 public boolean matchesCallFilter(Bundle extras) {
1922 enforceSystemOrSystemUI("INotificationManager.matchesCallFilter");
Christoph Studer12aeda82014-09-23 19:08:56 +02001923 return mZenModeHelper.matchesCallFilter(
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07001924 Binder.getCallingUserHandle(),
Christoph Studer12aeda82014-09-23 19:08:56 +02001925 extras,
1926 mRankingHelper.findExtractor(ValidateNotificationPeople.class),
1927 MATCHES_CALL_FILTER_CONTACTS_TIMEOUT_MS,
1928 MATCHES_CALL_FILTER_TIMEOUT_AFFINITY);
John Spurlock2b122f42014-08-27 16:29:47 -04001929 }
John Spurlock530052a2014-11-30 16:26:19 -05001930
1931 @Override
1932 public boolean isSystemConditionProviderEnabled(String path) {
John Spurlockcdb57ae2015-02-11 19:04:11 -05001933 enforceSystemOrSystemUIOrVolume("INotificationManager.isSystemConditionProviderEnabled");
John Spurlockb2278d62015-04-07 12:47:12 -04001934 return mConditionProviders.isSystemProviderEnabled(path);
John Spurlock530052a2014-11-30 16:26:19 -05001935 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001936
Christopher Tatef9767d62015-04-08 14:35:43 -07001937 // Backup/restore interface
1938 @Override
1939 public byte[] getBackupPayload(int user) {
John Spurlock35ef0a62015-05-28 11:24:10 -04001940 if (DBG) Slog.d(TAG, "getBackupPayload u=" + user);
Xiaohui Chenddbe4ca2015-08-13 16:20:56 -07001941 //TODO: http://b/22388012
1942 if (user != UserHandle.USER_SYSTEM) {
John Spurlock35ef0a62015-05-28 11:24:10 -04001943 Slog.w(TAG, "getBackupPayload: cannot backup policy for user " + user);
1944 return null;
1945 }
1946 final ByteArrayOutputStream baos = new ByteArrayOutputStream();
1947 try {
1948 writePolicyXml(baos, true /*forBackup*/);
1949 return baos.toByteArray();
1950 } catch (IOException e) {
1951 Slog.w(TAG, "getBackupPayload: error writing payload for user " + user, e);
1952 }
Christopher Tatef9767d62015-04-08 14:35:43 -07001953 return null;
1954 }
1955
1956 @Override
1957 public void applyRestore(byte[] payload, int user) {
John Spurlock35ef0a62015-05-28 11:24:10 -04001958 if (DBG) Slog.d(TAG, "applyRestore u=" + user + " payload="
1959 + (payload != null ? new String(payload, StandardCharsets.UTF_8) : null));
1960 if (payload == null) {
1961 Slog.w(TAG, "applyRestore: no payload to restore for user " + user);
1962 return;
1963 }
Xiaohui Chenddbe4ca2015-08-13 16:20:56 -07001964 //TODO: http://b/22388012
1965 if (user != UserHandle.USER_SYSTEM) {
John Spurlock35ef0a62015-05-28 11:24:10 -04001966 Slog.w(TAG, "applyRestore: cannot restore policy for user " + user);
1967 return;
1968 }
1969 final ByteArrayInputStream bais = new ByteArrayInputStream(payload);
1970 try {
1971 readPolicyXml(bais, true /*forRestore*/);
1972 savePolicyFile();
1973 } catch (NumberFormatException | XmlPullParserException | IOException e) {
1974 Slog.w(TAG, "applyRestore: error reading payload", e);
1975 }
Christopher Tatef9767d62015-04-08 14:35:43 -07001976 }
1977
John Spurlock1fc476d2015-04-14 16:05:20 -04001978 @Override
John Spurlock80774932015-05-07 17:38:50 -04001979 public boolean isNotificationPolicyAccessGranted(String pkg) {
1980 return checkPolicyAccess(pkg);
John Spurlock1fc476d2015-04-14 16:05:20 -04001981 }
1982
1983 @Override
Julia Reynolds48034f82016-03-09 10:15:16 -05001984 public boolean isNotificationPolicyAccessGrantedForPackage(String pkg) {;
1985 enforceSystemOrSystemUIOrSamePackage(pkg,
1986 "request policy access status for another package");
Julia Reynoldsa2d01022016-03-18 15:03:43 -04001987 return checkPolicyAccess(pkg);
John Spurlock80774932015-05-07 17:38:50 -04001988 }
1989
1990 @Override
1991 public String[] getPackagesRequestingNotificationPolicyAccess()
1992 throws RemoteException {
1993 enforceSystemOrSystemUI("request policy access packages");
1994 final long identity = Binder.clearCallingIdentity();
1995 try {
John Spurlock7c74f782015-06-04 13:01:42 -04001996 return mPolicyAccess.getRequestingPackages();
John Spurlock80774932015-05-07 17:38:50 -04001997 } finally {
1998 Binder.restoreCallingIdentity(identity);
1999 }
2000 }
2001
2002 @Override
2003 public void setNotificationPolicyAccessGranted(String pkg, boolean granted)
2004 throws RemoteException {
2005 enforceSystemOrSystemUI("grant notification policy access");
2006 final long identity = Binder.clearCallingIdentity();
2007 try {
2008 synchronized (mNotificationList) {
2009 mPolicyAccess.put(pkg, granted);
2010 }
2011 } finally {
2012 Binder.restoreCallingIdentity(identity);
2013 }
2014 }
2015
2016 @Override
2017 public Policy getNotificationPolicy(String pkg) {
2018 enforcePolicyAccess(pkg, "getNotificationPolicy");
John Spurlock1fc476d2015-04-14 16:05:20 -04002019 final long identity = Binder.clearCallingIdentity();
2020 try {
2021 return mZenModeHelper.getNotificationPolicy();
2022 } finally {
2023 Binder.restoreCallingIdentity(identity);
2024 }
2025 }
2026
2027 @Override
John Spurlock80774932015-05-07 17:38:50 -04002028 public void setNotificationPolicy(String pkg, Policy policy) {
2029 enforcePolicyAccess(pkg, "setNotificationPolicy");
John Spurlock1fc476d2015-04-14 16:05:20 -04002030 final long identity = Binder.clearCallingIdentity();
2031 try {
2032 mZenModeHelper.setNotificationPolicy(policy);
2033 } finally {
2034 Binder.restoreCallingIdentity(identity);
2035 }
2036 }
Chris Wren51017d02015-12-15 15:34:46 -05002037
2038 @Override
Chris Wrene0ba7eb2016-03-04 17:30:43 -05002039 public void setImportanceFromRankerService(INotificationListener token, String key,
Chris Wren47633422016-01-22 09:56:59 -05002040 int importance, CharSequence explanation) throws RemoteException {
Chris Wren4a4b49d2016-02-09 11:25:08 -05002041 if (importance == IMPORTANCE_NONE) {
2042 throw new IllegalArgumentException("blocking not allowed: key=" + key);
2043 }
Chris Wren51017d02015-12-15 15:34:46 -05002044 final long identity = Binder.clearCallingIdentity();
2045 try {
2046 synchronized (mNotificationList) {
Chris Wrene0ba7eb2016-03-04 17:30:43 -05002047 mRankerServices.checkServiceTokenLocked(token);
Chris Wren51017d02015-12-15 15:34:46 -05002048 NotificationRecord n = mNotificationsByKey.get(key);
2049 n.setImportance(importance, explanation);
2050 mRankingHandler.requestSort();
2051 }
2052 } finally {
2053 Binder.restoreCallingIdentity(identity);
2054 }
2055 }
John Spurlock1fc476d2015-04-14 16:05:20 -04002056 };
John Spurlocka4294292014-03-24 18:02:32 -04002057
John Spurlock32fe4c62014-10-02 12:16:02 -04002058 private String disableNotificationEffects(NotificationRecord record) {
2059 if (mDisableNotificationEffects) {
2060 return "booleanState";
2061 }
2062 if ((mListenerHints & HINT_HOST_DISABLE_EFFECTS) != 0) {
2063 return "listenerHints";
2064 }
2065 if (mCallState != TelephonyManager.CALL_STATE_IDLE && !mZenModeHelper.isCall(record)) {
2066 return "callState";
2067 }
2068 return null;
Chris Wrene4b38802015-07-07 15:54:19 -04002069 };
2070
2071 private void dumpJson(PrintWriter pw, DumpFilter filter) {
2072 JSONObject dump = new JSONObject();
2073 try {
2074 dump.put("service", "Notification Manager");
Chris Wrenacf424a2016-03-15 12:48:55 -04002075 dump.put("bans", mRankingHelper.dumpBansJson(filter));
2076 dump.put("ranking", mRankingHelper.dumpJson(filter));
Chris Wrene4b38802015-07-07 15:54:19 -04002077 dump.put("stats", mUsageStats.dumpJson(filter));
2078 } catch (JSONException e) {
2079 e.printStackTrace();
2080 }
2081 pw.println(dump);
John Spurlock1fa865f2014-07-21 14:56:39 -04002082 }
2083
John Spurlock25e2d242014-06-27 13:58:23 -04002084 void dumpImpl(PrintWriter pw, DumpFilter filter) {
2085 pw.print("Current Notification Manager state");
Dan Sandlera1770312015-07-10 13:59:29 -04002086 if (filter.filtered) {
John Spurlock50806fc2014-07-15 10:22:02 -04002087 pw.print(" (filtered to "); pw.print(filter); pw.print(")");
John Spurlock25e2d242014-06-27 13:58:23 -04002088 }
2089 pw.println(':');
Adam Lesinski182f73f2013-12-05 16:48:06 -08002090 int N;
Julia Reynoldse6b53e62015-07-31 09:25:10 -04002091 final boolean zenOnly = filter.filtered && filter.zen;
Adam Lesinski182f73f2013-12-05 16:48:06 -08002092
John Spurlock50806fc2014-07-15 10:22:02 -04002093 if (!zenOnly) {
2094 synchronized (mToastQueue) {
2095 N = mToastQueue.size();
2096 if (N > 0) {
2097 pw.println(" Toast Queue:");
2098 for (int i=0; i<N; i++) {
2099 mToastQueue.get(i).dump(pw, " ", filter);
2100 }
2101 pw.println(" ");
Adam Lesinski182f73f2013-12-05 16:48:06 -08002102 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08002103 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08002104 }
2105
2106 synchronized (mNotificationList) {
John Spurlock50806fc2014-07-15 10:22:02 -04002107 if (!zenOnly) {
2108 N = mNotificationList.size();
John Spurlock25e2d242014-06-27 13:58:23 -04002109 if (N > 0) {
John Spurlock50806fc2014-07-15 10:22:02 -04002110 pw.println(" Notification List:");
John Spurlock25e2d242014-06-27 13:58:23 -04002111 for (int i=0; i<N; i++) {
John Spurlock50806fc2014-07-15 10:22:02 -04002112 final NotificationRecord nr = mNotificationList.get(i);
Julia Reynoldse6b53e62015-07-31 09:25:10 -04002113 if (filter.filtered && !filter.matches(nr.sbn)) continue;
Dan Sandlera1770312015-07-10 13:59:29 -04002114 nr.dump(pw, " ", getContext(), filter.redact);
John Spurlock25e2d242014-06-27 13:58:23 -04002115 }
2116 pw.println(" ");
Adam Lesinski182f73f2013-12-05 16:48:06 -08002117 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08002118
Julia Reynoldse6b53e62015-07-31 09:25:10 -04002119 if (!filter.filtered) {
John Spurlock50806fc2014-07-15 10:22:02 -04002120 N = mLights.size();
2121 if (N > 0) {
2122 pw.println(" Lights List:");
2123 for (int i=0; i<N; i++) {
Chris Wren6054e612014-11-25 17:16:46 -05002124 if (i == N - 1) {
2125 pw.print(" > ");
2126 } else {
2127 pw.print(" ");
2128 }
2129 pw.println(mLights.get(i));
John Spurlock50806fc2014-07-15 10:22:02 -04002130 }
2131 pw.println(" ");
2132 }
John Spurlockcb566aa2014-08-03 22:58:28 -04002133 pw.println(" mUseAttentionLight=" + mUseAttentionLight);
2134 pw.println(" mNotificationPulseEnabled=" + mNotificationPulseEnabled);
Chris Wren6054e612014-11-25 17:16:46 -05002135 pw.println(" mSoundNotificationKey=" + mSoundNotificationKey);
2136 pw.println(" mVibrateNotificationKey=" + mVibrateNotificationKey);
John Spurlockd8afe3c2014-08-01 14:04:07 -04002137 pw.println(" mDisableNotificationEffects=" + mDisableNotificationEffects);
John Spurlock32fe4c62014-10-02 12:16:02 -04002138 pw.println(" mCallState=" + callStateToString(mCallState));
John Spurlock50806fc2014-07-15 10:22:02 -04002139 pw.println(" mSystemReady=" + mSystemReady);
2140 }
2141 pw.println(" mArchive=" + mArchive.toString());
2142 Iterator<StatusBarNotification> iter = mArchive.descendingIterator();
2143 int i=0;
2144 while (iter.hasNext()) {
2145 final StatusBarNotification sbn = iter.next();
2146 if (filter != null && !filter.matches(sbn)) continue;
2147 pw.println(" " + sbn);
2148 if (++i >= 5) {
2149 if (iter.hasNext()) pw.println(" ...");
2150 break;
2151 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08002152 }
2153 }
2154
John Spurlock50806fc2014-07-15 10:22:02 -04002155 if (!zenOnly) {
2156 pw.println("\n Usage Stats:");
2157 mUsageStats.dump(pw, " ", filter);
2158 }
Christoph Studer546bec82014-03-14 12:17:12 +01002159
Julia Reynoldse6b53e62015-07-31 09:25:10 -04002160 if (!filter.filtered || zenOnly) {
John Spurlock25e2d242014-06-27 13:58:23 -04002161 pw.println("\n Zen Mode:");
John Spurlockf3701772015-02-12 13:29:37 -05002162 pw.print(" mInterruptionFilter="); pw.println(mInterruptionFilter);
John Spurlock25e2d242014-06-27 13:58:23 -04002163 mZenModeHelper.dump(pw, " ");
John Spurlock6ae82a72014-07-16 16:23:01 -04002164
2165 pw.println("\n Zen Log:");
2166 ZenLog.dump(pw, " ");
John Spurlock25e2d242014-06-27 13:58:23 -04002167 }
John Spurlocke77bb362014-04-26 10:24:59 -04002168
John Spurlock50806fc2014-07-15 10:22:02 -04002169 if (!zenOnly) {
2170 pw.println("\n Ranking Config:");
2171 mRankingHelper.dump(pw, " ", filter);
Chris Wren54bbef42014-07-09 18:37:56 -04002172
John Spurlock50806fc2014-07-15 10:22:02 -04002173 pw.println("\n Notification listeners:");
2174 mListeners.dump(pw, filter);
John Spurlockd8afe3c2014-08-01 14:04:07 -04002175 pw.print(" mListenerHints: "); pw.println(mListenerHints);
2176 pw.print(" mListenersDisablingEffects: (");
2177 N = mListenersDisablingEffects.size();
John Spurlock1fa865f2014-07-21 14:56:39 -04002178 for (int i = 0; i < N; i++) {
John Spurlockd8afe3c2014-08-01 14:04:07 -04002179 final ManagedServiceInfo listener = mListenersDisablingEffects.valueAt(i);
John Spurlock1fa865f2014-07-21 14:56:39 -04002180 if (i > 0) pw.print(',');
2181 pw.print(listener.component);
2182 }
2183 pw.println(')');
Chris Wren0efdb882016-03-01 17:17:47 -05002184 pw.println("\n mRankerServicePackageName: " + mRankerServicePackageName);
Chris Wrene0ba7eb2016-03-04 17:30:43 -05002185 pw.println("\n Notification ranker services:");
2186 mRankerServices.dump(pw, filter);
John Spurlock50806fc2014-07-15 10:22:02 -04002187 }
John Spurlock80774932015-05-07 17:38:50 -04002188 pw.println("\n Policy access:");
2189 pw.print(" mPolicyAccess: "); pw.println(mPolicyAccess);
John Spurlocke77bb362014-04-26 10:24:59 -04002190
2191 pw.println("\n Condition providers:");
John Spurlock25e2d242014-06-27 13:58:23 -04002192 mConditionProviders.dump(pw, filter);
Christoph Studer265c1052014-07-23 17:14:33 +02002193
2194 pw.println("\n Group summaries:");
2195 for (Entry<String, NotificationRecord> entry : mSummaryByGroupKey.entrySet()) {
2196 NotificationRecord r = entry.getValue();
2197 pw.println(" " + entry.getKey() + " -> " + r.getKey());
2198 if (mNotificationsByKey.get(r.getKey()) != r) {
2199 pw.println("!!!!!!LEAK: Record not found in mNotificationsByKey.");
Dan Sandlera1770312015-07-10 13:59:29 -04002200 r.dump(pw, " ", getContext(), filter.redact);
Christoph Studer265c1052014-07-23 17:14:33 +02002201 }
2202 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002203 }
2204 }
2205
Adam Lesinski182f73f2013-12-05 16:48:06 -08002206 /**
2207 * The private API only accessible to the system process.
2208 */
2209 private final NotificationManagerInternal mInternalService = new NotificationManagerInternal() {
2210 @Override
Christoph Studer8fd7f1e2014-04-11 17:35:05 -04002211 public void enqueueNotification(String pkg, String opPkg, int callingUid, int callingPid,
Adam Lesinski182f73f2013-12-05 16:48:06 -08002212 String tag, int id, Notification notification, int[] idReceived, int userId) {
Christoph Studer8fd7f1e2014-04-11 17:35:05 -04002213 enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, notification,
Adam Lesinski182f73f2013-12-05 16:48:06 -08002214 idReceived, userId);
2215 }
Christoph Studer365e4c32014-09-18 20:35:36 +02002216
2217 @Override
2218 public void removeForegroundServiceFlagFromNotification(String pkg, int notificationId,
2219 int userId) {
2220 checkCallerIsSystem();
2221 synchronized (mNotificationList) {
2222 int i = indexOfNotificationLocked(pkg, null, notificationId, userId);
2223 if (i < 0) {
2224 Log.d(TAG, "stripForegroundServiceFlag: Could not find notification with "
2225 + "pkg=" + pkg + " / id=" + notificationId + " / userId=" + userId);
2226 return;
2227 }
2228 NotificationRecord r = mNotificationList.get(i);
2229 StatusBarNotification sbn = r.sbn;
2230 // NoMan adds flags FLAG_NO_CLEAR and FLAG_ONGOING_EVENT when it sees
2231 // FLAG_FOREGROUND_SERVICE. Hence it's not enough to remove FLAG_FOREGROUND_SERVICE,
2232 // we have to revert to the flags we received initially *and* force remove
2233 // FLAG_FOREGROUND_SERVICE.
2234 sbn.getNotification().flags =
2235 (r.mOriginalFlags & ~Notification.FLAG_FOREGROUND_SERVICE);
2236 mRankingHelper.sort(mNotificationList);
2237 mListeners.notifyPostedLocked(sbn, sbn /* oldSbn */);
2238 }
2239 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08002240 };
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002241
Christoph Studer8fd7f1e2014-04-11 17:35:05 -04002242 void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,
Scott Greenwald9b05c612013-06-25 23:44:05 -04002243 final int callingPid, final String tag, final int id, final Notification notification,
Adam Lesinski182f73f2013-12-05 16:48:06 -08002244 int[] idOut, int incomingUserId) {
Daniel Sandler0da673f2012-04-11 12:33:16 -04002245 if (DBG) {
Adam Lesinski182f73f2013-12-05 16:48:06 -08002246 Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id
2247 + " notification=" + notification);
Daniel Sandler0da673f2012-04-11 12:33:16 -04002248 }
John Spurlock7340fc82014-04-24 18:50:12 -04002249 checkCallerIsSystemOrSameApp(pkg);
2250 final boolean isSystemNotification = isUidSystem(callingUid) || ("android".equals(pkg));
Justin Koh38156c52014-06-04 13:57:49 -07002251 final boolean isNotificationFromListener = mListeners.isListenerPackage(pkg);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08002252
Scott Greenwald9b05c612013-06-25 23:44:05 -04002253 final int userId = ActivityManager.handleIncomingUser(callingPid,
2254 callingUid, incomingUserId, true, false, "enqueueNotification", pkg);
Jeff Sharkey65c4a2b2012-09-25 17:22:27 -07002255 final UserHandle user = new UserHandle(userId);
Dianne Hackborn41203752012-08-31 14:05:51 -07002256
Joe Onoratobd73d012010-06-04 11:44:54 -07002257 // Limit the number of notifications that any given package except the android
Justin Koh38156c52014-06-04 13:57:49 -07002258 // package or a registered listener can enqueue. Prevents DOS attacks and deals with leaks.
2259 if (!isSystemNotification && !isNotificationFromListener) {
Joe Onoratobd73d012010-06-04 11:44:54 -07002260 synchronized (mNotificationList) {
2261 int count = 0;
2262 final int N = mNotificationList.size();
2263 for (int i=0; i<N; i++) {
2264 final NotificationRecord r = mNotificationList.get(i);
Daniel Sandler4f91efd2013-04-25 16:38:41 -04002265 if (r.sbn.getPackageName().equals(pkg) && r.sbn.getUserId() == userId) {
Vladimir Marko2526f332013-09-11 11:13:55 +01002266 if (r.sbn.getId() == id && TextUtils.equals(r.sbn.getTag(), tag)) {
2267 break; // Allow updating existing notification
2268 }
Joe Onoratobd73d012010-06-04 11:44:54 -07002269 count++;
2270 if (count >= MAX_PACKAGE_NOTIFICATIONS) {
2271 Slog.e(TAG, "Package has already posted " + count
2272 + " notifications. Not showing more. package=" + pkg);
2273 return;
2274 }
2275 }
2276 }
2277 }
2278 }
2279
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002280 if (pkg == null || notification == null) {
2281 throw new IllegalArgumentException("null not allowed: pkg=" + pkg
2282 + " id=" + id + " notification=" + notification);
2283 }
Dan Sandlerd63f9322015-05-06 15:18:49 -04002284
Chris Wren47633422016-01-22 09:56:59 -05002285 // Sanitize inputs
2286 notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN,
2287 Notification.PRIORITY_MAX);
Daniel Sandler0da673f2012-04-11 12:33:16 -04002288
Chris Wren47633422016-01-22 09:56:59 -05002289 // setup local book-keeping
2290 final StatusBarNotification n = new StatusBarNotification(
2291 pkg, opPkg, id, tag, callingUid, callingPid, 0, notification,
2292 user);
2293 final NotificationRecord r = new NotificationRecord(getContext(), n);
2294 mHandler.post(new EnqueueNotificationRunnable(userId, r));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002295
2296 idOut[0] = id;
2297 }
2298
Chris Wren47633422016-01-22 09:56:59 -05002299 private class EnqueueNotificationRunnable implements Runnable {
2300 private final NotificationRecord r;
2301 private final int userId;
2302
2303 EnqueueNotificationRunnable(int userId, NotificationRecord r) {
2304 this.userId = userId;
2305 this.r = r;
2306 };
2307
2308 @Override
2309 public void run() {
2310
2311 synchronized (mNotificationList) {
2312 final StatusBarNotification n = r.sbn;
Chris Wren1ac52a92016-02-24 14:54:52 -05002313 if (DBG) Slog.d(TAG, "EnqueueNotificationRunnable.run for: " + n.getKey());
Chris Wren47633422016-01-22 09:56:59 -05002314 NotificationRecord old = mNotificationsByKey.get(n.getKey());
2315 if (old != null) {
2316 // Retain ranking information from previous record
2317 r.copyRankingInformation(old);
2318 }
2319
2320 final int callingUid = n.getUid();
2321 final int callingPid = n.getInitialPid();
2322 final Notification notification = n.getNotification();
2323 final String pkg = n.getPackageName();
2324 final int id = n.getId();
2325 final String tag = n.getTag();
2326 final boolean isSystemNotification = isUidSystem(callingUid) ||
2327 ("android".equals(pkg));
2328
2329 // Handle grouped notifications and bail out early if we
2330 // can to avoid extracting signals.
2331 handleGroupedNotificationLocked(r, old, callingUid, callingPid);
2332 boolean ignoreNotification =
2333 removeUnusedGroupedNotificationLocked(r, old, callingUid, callingPid);
Chris Wren1ac52a92016-02-24 14:54:52 -05002334 if (DBG) Slog.d(TAG, "ignoreNotification is " + ignoreNotification);
Chris Wren47633422016-01-22 09:56:59 -05002335
2336 // This conditional is a dirty hack to limit the logging done on
2337 // behalf of the download manager without affecting other apps.
2338 if (!pkg.equals("com.android.providers.downloads")
2339 || Log.isLoggable("DownloadManager", Log.VERBOSE)) {
2340 int enqueueStatus = EVENTLOG_ENQUEUE_STATUS_NEW;
2341 if (ignoreNotification) {
2342 enqueueStatus = EVENTLOG_ENQUEUE_STATUS_IGNORED;
2343 } else if (old != null) {
2344 enqueueStatus = EVENTLOG_ENQUEUE_STATUS_UPDATE;
2345 }
2346 EventLogTags.writeNotificationEnqueue(callingUid, callingPid,
2347 pkg, id, tag, userId, notification.toString(),
2348 enqueueStatus);
2349 }
2350
2351 if (ignoreNotification) {
2352 return;
2353 }
2354
2355 mRankingHelper.extractSignals(r);
2356
Andrei Stingaceanu355b2322016-02-12 16:43:51 +00002357 final boolean isPackageSuspended = isPackageSuspendedForUser(pkg, callingUid);
Chris Wren47633422016-01-22 09:56:59 -05002358
Julia Reynoldsef37f282016-02-12 09:11:27 -05002359 // blocked apps
Chris Wren47633422016-01-22 09:56:59 -05002360 if (r.getImportance() == NotificationListenerService.Ranking.IMPORTANCE_NONE
Andrei Stingaceanu355b2322016-02-12 16:43:51 +00002361 || !noteNotificationOp(pkg, callingUid) || isPackageSuspended) {
Chris Wren47633422016-01-22 09:56:59 -05002362 if (!isSystemNotification) {
Andrei Stingaceanu355b2322016-02-12 16:43:51 +00002363 if (isPackageSuspended) {
Andrei Stingaceanu0122f6512016-01-22 15:33:03 +00002364 Slog.e(TAG, "Suppressing notification from package due to package "
2365 + "suspended by administrator.");
2366 mUsageStats.registerSuspendedByAdmin(r);
2367 } else {
2368 Slog.e(TAG, "Suppressing notification from package by user request.");
2369 mUsageStats.registerBlocked(r);
2370 }
Chris Wren47633422016-01-22 09:56:59 -05002371 return;
2372 }
2373 }
2374
Chris Wrene0ba7eb2016-03-04 17:30:43 -05002375 // tell the ranker service about the notification
2376 if (mRankerServices.isEnabled()) {
2377 mRankerServices.onNotificationEnqueued(r);
Chris Wren47633422016-01-22 09:56:59 -05002378 // TODO delay the code below here for 100ms or until there is an answer
2379 }
2380
2381
2382 int index = indexOfNotificationLocked(n.getKey());
2383 if (index < 0) {
2384 mNotificationList.add(r);
2385 mUsageStats.registerPostedByApp(r);
2386 } else {
2387 old = mNotificationList.get(index);
2388 mNotificationList.set(index, r);
2389 mUsageStats.registerUpdatedByApp(r, old);
2390 // Make sure we don't lose the foreground service state.
2391 notification.flags |=
2392 old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;
2393 r.isUpdate = true;
2394 }
2395
2396 mNotificationsByKey.put(n.getKey(), r);
2397
2398 // Ensure if this is a foreground service that the proper additional
2399 // flags are set.
2400 if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
2401 notification.flags |= Notification.FLAG_ONGOING_EVENT
2402 | Notification.FLAG_NO_CLEAR;
2403 }
2404
2405 applyZenModeLocked(r);
2406 mRankingHelper.sort(mNotificationList);
2407
2408 if (notification.getSmallIcon() != null) {
2409 StatusBarNotification oldSbn = (old != null) ? old.sbn : null;
2410 mListeners.notifyPostedLocked(n, oldSbn);
2411 } else {
2412 Slog.e(TAG, "Not posting notification without small icon: " + notification);
2413 if (old != null && !old.isCanceled) {
2414 mListeners.notifyRemovedLocked(n);
2415 }
2416 // ATTENTION: in a future release we will bail out here
2417 // so that we do not play sounds, show lights, etc. for invalid
2418 // notifications
2419 Slog.e(TAG, "WARNING: In a future release this will crash the app: "
2420 + n.getPackageName());
2421 }
2422
2423 buzzBeepBlinkLocked(r);
2424 }
2425 }
2426 }
2427
Christoph Studer265c1052014-07-23 17:14:33 +02002428 /**
2429 * Ensures that grouped notification receive their special treatment.
2430 *
2431 * <p>Cancels group children if the new notification causes a group to lose
2432 * its summary.</p>
2433 *
2434 * <p>Updates mSummaryByGroupKey.</p>
2435 */
2436 private void handleGroupedNotificationLocked(NotificationRecord r, NotificationRecord old,
2437 int callingUid, int callingPid) {
2438 StatusBarNotification sbn = r.sbn;
2439 Notification n = sbn.getNotification();
2440 String group = sbn.getGroupKey();
2441 boolean isSummary = n.isGroupSummary();
2442
2443 Notification oldN = old != null ? old.sbn.getNotification() : null;
2444 String oldGroup = old != null ? old.sbn.getGroupKey() : null;
2445 boolean oldIsSummary = old != null && oldN.isGroupSummary();
2446
2447 if (oldIsSummary) {
2448 NotificationRecord removedSummary = mSummaryByGroupKey.remove(oldGroup);
2449 if (removedSummary != old) {
2450 String removedKey =
2451 removedSummary != null ? removedSummary.getKey() : "<null>";
2452 Slog.w(TAG, "Removed summary didn't match old notification: old=" + old.getKey() +
2453 ", removed=" + removedKey);
2454 }
2455 }
2456 if (isSummary) {
2457 mSummaryByGroupKey.put(group, r);
2458 }
2459
2460 // Clear out group children of the old notification if the update
2461 // causes the group summary to go away. This happens when the old
2462 // notification was a summary and the new one isn't, or when the old
2463 // notification was a summary and its group key changed.
2464 if (oldIsSummary && (!isSummary || !oldGroup.equals(group))) {
2465 cancelGroupChildrenLocked(old, callingUid, callingPid, null,
2466 REASON_GROUP_SUMMARY_CANCELED);
2467 }
2468 }
2469
2470 /**
2471 * Performs group notification optimizations if SysUI is the only active
2472 * notification listener and returns whether the given notification should
2473 * be ignored.
2474 *
2475 * <p>Returns true if the given notification is a child of a group with a
2476 * summary, which means that SysUI will never show it, and hence the new
Christoph Studer4a9849b2015-01-06 18:55:08 +01002477 * notification can be safely ignored. Also cancels any previous instance
2478 * of the ignored notification.</p>
Christoph Studer265c1052014-07-23 17:14:33 +02002479 *
2480 * <p>For summaries, cancels all children of that group, as SysUI will
2481 * never show them anymore.</p>
2482 *
2483 * @return true if the given notification can be ignored as an optimization
2484 */
2485 private boolean removeUnusedGroupedNotificationLocked(NotificationRecord r,
Christoph Studer4a9849b2015-01-06 18:55:08 +01002486 NotificationRecord old, int callingUid, int callingPid) {
Selim Cinekb5605e52015-02-20 18:21:41 +01002487 if (!ENABLE_CHILD_NOTIFICATIONS) {
2488 // No optimizations are possible if listeners want groups.
2489 if (mListeners.notificationGroupsDesired()) {
2490 return false;
Christoph Studer265c1052014-07-23 17:14:33 +02002491 }
Selim Cinekb5605e52015-02-20 18:21:41 +01002492
2493 StatusBarNotification sbn = r.sbn;
2494 String group = sbn.getGroupKey();
2495 boolean isSummary = sbn.getNotification().isGroupSummary();
2496 boolean isChild = sbn.getNotification().isGroupChild();
2497
2498 NotificationRecord summary = mSummaryByGroupKey.get(group);
2499 if (isChild && summary != null) {
2500 // Child with an active summary -> ignore
Christoph Studer4a9849b2015-01-06 18:55:08 +01002501 if (DBG) {
Selim Cinekb5605e52015-02-20 18:21:41 +01002502 Slog.d(TAG, "Ignoring group child " + sbn.getKey() + " due to existing summary "
2503 + summary.getKey());
Christoph Studer4a9849b2015-01-06 18:55:08 +01002504 }
Selim Cinekb5605e52015-02-20 18:21:41 +01002505 // Make sure we don't leave an old version of the notification around.
2506 if (old != null) {
2507 if (DBG) {
2508 Slog.d(TAG, "Canceling old version of ignored group child " + sbn.getKey());
2509 }
2510 cancelNotificationLocked(old, false, REASON_GROUP_OPTIMIZATION);
2511 }
2512 return true;
2513 } else if (isSummary) {
2514 // Summary -> cancel children
2515 cancelGroupChildrenLocked(r, callingUid, callingPid, null,
2516 REASON_GROUP_OPTIMIZATION);
Christoph Studer4a9849b2015-01-06 18:55:08 +01002517 }
Christoph Studer265c1052014-07-23 17:14:33 +02002518 }
2519 return false;
2520 }
2521
Chris Wren93bb8b82016-03-29 14:35:05 -04002522 @VisibleForTesting
2523 void buzzBeepBlinkLocked(NotificationRecord record) {
Chris Wren82ba59d2015-06-05 11:23:44 -04002524 boolean buzz = false;
2525 boolean beep = false;
2526 boolean blink = false;
2527
Chris Wrena3446562014-06-03 18:11:47 -04002528 final Notification notification = record.sbn.getNotification();
Chris Wren93bb8b82016-03-29 14:35:05 -04002529 final String key = record.getKey();
Chris Wrena3446562014-06-03 18:11:47 -04002530
2531 // Should this notification make noise, vibe, or use the LED?
Julia Reynoldsf0f629f2016-02-25 09:34:04 -05002532 final boolean aboveThreshold = record.getImportance() >= IMPORTANCE_DEFAULT;
Chris Wrence00a232014-11-21 16:25:19 -05002533 final boolean canInterrupt = aboveThreshold && !record.isIntercepted();
Chris Wrena3446562014-06-03 18:11:47 -04002534 if (DBG || record.isIntercepted())
2535 Slog.v(TAG,
2536 "pkg=" + record.sbn.getPackageName() + " canInterrupt=" + canInterrupt +
2537 " intercept=" + record.isIntercepted()
2538 );
2539
2540 final int currentUser;
2541 final long token = Binder.clearCallingIdentity();
2542 try {
2543 currentUser = ActivityManager.getCurrentUser();
2544 } finally {
2545 Binder.restoreCallingIdentity(token);
2546 }
2547
2548 // If we're not supposed to beep, vibrate, etc. then don't.
John Spurlock32fe4c62014-10-02 12:16:02 -04002549 final String disableEffects = disableNotificationEffects(record);
2550 if (disableEffects != null) {
2551 ZenLog.traceDisableEffects(record, disableEffects);
2552 }
Chris Wren93bb8b82016-03-29 14:35:05 -04002553
2554 // Remember if this notification already owns the notification channels.
2555 boolean wasBeep = key != null && key.equals(mSoundNotificationKey);
2556 boolean wasBuzz = key != null && key.equals(mVibrateNotificationKey);
2557
2558 // These are set inside the conditional if the notification is allowed to make noise.
2559 boolean hasValidVibrate = false;
2560 boolean hasValidSound = false;
John Spurlock32fe4c62014-10-02 12:16:02 -04002561 if (disableEffects == null
Chris Wrena3446562014-06-03 18:11:47 -04002562 && (record.getUserId() == UserHandle.USER_ALL ||
2563 record.getUserId() == currentUser ||
2564 mUserProfiles.isCurrentProfile(record.getUserId()))
2565 && canInterrupt
2566 && mSystemReady
2567 && mAudioManager != null) {
2568 if (DBG) Slog.v(TAG, "Interrupting!");
2569
Chris Wrena3446562014-06-03 18:11:47 -04002570 // should we use the default notification sound? (indicated either by
2571 // DEFAULT_SOUND or because notification.sound is pointing at
2572 // Settings.System.NOTIFICATION_SOUND)
2573 final boolean useDefaultSound =
2574 (notification.defaults & Notification.DEFAULT_SOUND) != 0 ||
2575 Settings.System.DEFAULT_NOTIFICATION_URI
2576 .equals(notification.sound);
2577
2578 Uri soundUri = null;
Chris Wrena3446562014-06-03 18:11:47 -04002579 if (useDefaultSound) {
2580 soundUri = Settings.System.DEFAULT_NOTIFICATION_URI;
2581
2582 // check to see if the default notification sound is silent
2583 ContentResolver resolver = getContext().getContentResolver();
2584 hasValidSound = Settings.System.getString(resolver,
2585 Settings.System.NOTIFICATION_SOUND) != null;
2586 } else if (notification.sound != null) {
2587 soundUri = notification.sound;
2588 hasValidSound = (soundUri != null);
2589 }
2590
Chris Wrena3446562014-06-03 18:11:47 -04002591 // Does the notification want to specify its own vibration?
2592 final boolean hasCustomVibrate = notification.vibrate != null;
2593
2594 // new in 4.2: if there was supposed to be a sound and we're in vibrate
2595 // mode, and no other vibration is specified, we fall back to vibration
2596 final boolean convertSoundToVibration =
Chris Wren93bb8b82016-03-29 14:35:05 -04002597 !hasCustomVibrate
2598 && hasValidSound
2599 && (mAudioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_VIBRATE);
Chris Wrena3446562014-06-03 18:11:47 -04002600
2601 // The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback.
2602 final boolean useDefaultVibrate =
2603 (notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
2604
Chris Wren93bb8b82016-03-29 14:35:05 -04002605 hasValidVibrate = useDefaultVibrate || convertSoundToVibration ||
2606 hasCustomVibrate;
Chris Wrena3446562014-06-03 18:11:47 -04002607
Chris Wren93bb8b82016-03-29 14:35:05 -04002608 // We can alert, and we're allowed to alert, but if the developer asked us to only do
2609 // it once, and we already have, then don't.
2610 if (!(record.isUpdate
2611 && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0)) {
2612
2613 sendAccessibilityEvent(notification, record.sbn.getPackageName());
2614
2615 if (hasValidSound) {
2616 boolean looping =
2617 (notification.flags & Notification.FLAG_INSISTENT) != 0;
2618 AudioAttributes audioAttributes = audioAttributesForNotification(notification);
2619 mSoundNotificationKey = key;
2620 // do not play notifications if stream volume is 0 (typically because
2621 // ringer mode is silent) or if there is a user of exclusive audio focus
2622 if ((mAudioManager.getStreamVolume(
2623 AudioAttributes.toLegacyStreamType(audioAttributes)) != 0)
2624 && !mAudioManager.isAudioFocusExclusive()) {
2625 final long identity = Binder.clearCallingIdentity();
2626 try {
2627 final IRingtonePlayer player =
2628 mAudioManager.getRingtonePlayer();
2629 if (player != null) {
2630 if (DBG) Slog.v(TAG, "Playing sound " + soundUri
2631 + " with attributes " + audioAttributes);
2632 player.playAsync(soundUri, record.sbn.getUser(), looping,
2633 audioAttributes);
2634 beep = true;
2635 }
2636 } catch (RemoteException e) {
2637 } finally {
2638 Binder.restoreCallingIdentity(identity);
2639 }
Chris Wrena3446562014-06-03 18:11:47 -04002640 }
Chris Wren93bb8b82016-03-29 14:35:05 -04002641 }
2642
2643 if (hasValidVibrate && !(mAudioManager.getRingerModeInternal()
2644 == AudioManager.RINGER_MODE_SILENT)) {
2645 mVibrateNotificationKey = key;
2646
2647 if (useDefaultVibrate || convertSoundToVibration) {
2648 // Escalate privileges so we can use the vibrator even if the
2649 // notifying app does not have the VIBRATE permission.
2650 long identity = Binder.clearCallingIdentity();
2651 try {
2652 mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
2653 useDefaultVibrate ? mDefaultVibrationPattern
2654 : mFallbackVibrationPattern,
2655 ((notification.flags & Notification.FLAG_INSISTENT) != 0)
2656 ? 0: -1, audioAttributesForNotification(notification));
2657 buzz = true;
2658 } finally {
2659 Binder.restoreCallingIdentity(identity);
2660 }
2661 } else if (notification.vibrate.length > 1) {
2662 // If you want your own vibration pattern, you need the VIBRATE
2663 // permission
2664 mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
2665 notification.vibrate,
2666 ((notification.flags & Notification.FLAG_INSISTENT) != 0)
2667 ? 0: -1, audioAttributesForNotification(notification));
2668 buzz = true;
2669 }
Chris Wrena3446562014-06-03 18:11:47 -04002670 }
2671 }
Chris Wren93bb8b82016-03-29 14:35:05 -04002672
2673 }
2674 // If a notification is updated to remove the actively playing sound or vibrate,
2675 // cancel that feedback now
2676 if (wasBeep && !hasValidSound) {
2677 clearSoundLocked();
2678 }
2679 if (wasBuzz && !hasValidVibrate) {
2680 clearVibrateLocked();
Chris Wrena3446562014-06-03 18:11:47 -04002681 }
2682
2683 // light
2684 // release the light
Chris Wren93bb8b82016-03-29 14:35:05 -04002685 boolean wasShowLights = mLights.remove(key);
Julia Reynoldsf612869ae2015-11-05 16:48:55 -05002686 if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0 && aboveThreshold
2687 && ((record.getSuppressedVisualEffects()
Julia Reynoldsd5607292016-02-05 15:25:58 -05002688 & NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_OFF) == 0)) {
Chris Wren93bb8b82016-03-29 14:35:05 -04002689 mLights.add(key);
Chris Wrena3446562014-06-03 18:11:47 -04002690 updateLightsLocked();
Chris Wren5116a822014-06-04 15:59:50 -04002691 if (mUseAttentionLight) {
2692 mAttentionLight.pulse();
2693 }
Chris Wren82ba59d2015-06-05 11:23:44 -04002694 blink = true;
Chris Wrena3446562014-06-03 18:11:47 -04002695 } else if (wasShowLights) {
2696 updateLightsLocked();
2697 }
Chris Wren82ba59d2015-06-05 11:23:44 -04002698 if (buzz || beep || blink) {
Julia Reynolds61721582016-01-05 08:35:25 -05002699 if (((record.getSuppressedVisualEffects()
Julia Reynoldsd5607292016-02-05 15:25:58 -05002700 & NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_OFF) != 0)) {
Julia Reynolds61721582016-01-05 08:35:25 -05002701 if (DBG) Slog.v(TAG, "Suppressed SystemUI from triggering screen on");
2702 } else {
Chris Wren93bb8b82016-03-29 14:35:05 -04002703 EventLogTags.writeNotificationAlert(key,
Julia Reynolds61721582016-01-05 08:35:25 -05002704 buzz ? 1 : 0, beep ? 1 : 0, blink ? 1 : 0);
2705 mHandler.post(mBuzzBeepBlinked);
2706 }
John Spurlockcad57682014-07-26 17:09:56 -04002707 }
Chris Wrena3446562014-06-03 18:11:47 -04002708 }
2709
John Spurlock7b414672014-07-18 13:02:39 -04002710 private static AudioAttributes audioAttributesForNotification(Notification n) {
Marco Nelissen1c066302014-11-18 10:48:12 -08002711 if (n.audioAttributes != null
2712 && !Notification.AUDIO_ATTRIBUTES_DEFAULT.equals(n.audioAttributes)) {
2713 // the audio attributes are set and different from the default, use them
John Spurlockbfa5dc42014-07-28 23:30:45 -04002714 return n.audioAttributes;
Jean-Michel Triviceb79bc2014-09-05 11:09:14 -07002715 } else if (n.audioStreamType >= 0 && n.audioStreamType < AudioSystem.getNumStreamTypes()) {
2716 // the stream type is valid, use it
2717 return new AudioAttributes.Builder()
2718 .setInternalLegacyStreamType(n.audioStreamType)
2719 .build();
2720 } else if (n.audioStreamType == AudioSystem.STREAM_DEFAULT) {
2721 return Notification.AUDIO_ATTRIBUTES_DEFAULT;
2722 } else {
2723 Log.w(TAG, String.format("Invalid stream type: %d", n.audioStreamType));
2724 return Notification.AUDIO_ATTRIBUTES_DEFAULT;
John Spurlockbfa5dc42014-07-28 23:30:45 -04002725 }
John Spurlock7b414672014-07-18 13:02:39 -04002726 }
2727
Adam Lesinski182f73f2013-12-05 16:48:06 -08002728 void showNextToastLocked() {
2729 ToastRecord record = mToastQueue.get(0);
2730 while (record != null) {
2731 if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback);
2732 try {
2733 record.callback.show();
2734 scheduleTimeoutLocked(record);
2735 return;
2736 } catch (RemoteException e) {
2737 Slog.w(TAG, "Object died trying to show notification " + record.callback
2738 + " in package " + record.pkg);
2739 // remove it from the list and let the process die
2740 int index = mToastQueue.indexOf(record);
2741 if (index >= 0) {
2742 mToastQueue.remove(index);
2743 }
2744 keepProcessAliveLocked(record.pid);
2745 if (mToastQueue.size() > 0) {
2746 record = mToastQueue.get(0);
2747 } else {
2748 record = null;
2749 }
2750 }
2751 }
2752 }
2753
2754 void cancelToastLocked(int index) {
2755 ToastRecord record = mToastQueue.get(index);
2756 try {
2757 record.callback.hide();
2758 } catch (RemoteException e) {
2759 Slog.w(TAG, "Object died trying to hide notification " + record.callback
2760 + " in package " + record.pkg);
2761 // don't worry about this, we're about to remove it from
2762 // the list anyway
2763 }
2764 mToastQueue.remove(index);
2765 keepProcessAliveLocked(record.pid);
2766 if (mToastQueue.size() > 0) {
2767 // Show the next one. If the callback fails, this will remove
2768 // it from the list, so don't assume that the list hasn't changed
2769 // after this point.
2770 showNextToastLocked();
2771 }
2772 }
2773
2774 private void scheduleTimeoutLocked(ToastRecord r)
2775 {
2776 mHandler.removeCallbacksAndMessages(r);
2777 Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r);
2778 long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY;
2779 mHandler.sendMessageDelayed(m, delay);
2780 }
2781
2782 private void handleTimeout(ToastRecord record)
2783 {
2784 if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback);
2785 synchronized (mToastQueue) {
2786 int index = indexOfToastLocked(record.pkg, record.callback);
2787 if (index >= 0) {
2788 cancelToastLocked(index);
2789 }
2790 }
2791 }
2792
2793 // lock on mToastQueue
2794 int indexOfToastLocked(String pkg, ITransientNotification callback)
2795 {
2796 IBinder cbak = callback.asBinder();
2797 ArrayList<ToastRecord> list = mToastQueue;
2798 int len = list.size();
2799 for (int i=0; i<len; i++) {
2800 ToastRecord r = list.get(i);
2801 if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) {
2802 return i;
2803 }
2804 }
2805 return -1;
2806 }
2807
2808 // lock on mToastQueue
2809 void keepProcessAliveLocked(int pid)
2810 {
2811 int toastCount = 0; // toasts from this pid
2812 ArrayList<ToastRecord> list = mToastQueue;
2813 int N = list.size();
2814 for (int i=0; i<N; i++) {
2815 ToastRecord r = list.get(i);
2816 if (r.pid == pid) {
2817 toastCount++;
2818 }
2819 }
2820 try {
2821 mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0);
2822 } catch (RemoteException e) {
2823 // Shouldn't happen.
2824 }
2825 }
2826
Chris Wrenf9536642014-04-17 10:01:54 -04002827 private void handleRankingReconsideration(Message message) {
Chris Wren470c1ac2014-05-21 15:28:10 -04002828 if (!(message.obj instanceof RankingReconsideration)) return;
2829 RankingReconsideration recon = (RankingReconsideration) message.obj;
2830 recon.run();
Chris Wren333a61c2014-05-28 16:40:57 -04002831 boolean changed;
Chris Wren470c1ac2014-05-21 15:28:10 -04002832 synchronized (mNotificationList) {
2833 final NotificationRecord record = mNotificationsByKey.get(recon.getKey());
2834 if (record == null) {
2835 return;
Chris Wrenf9536642014-04-17 10:01:54 -04002836 }
Chris Wren333a61c2014-05-28 16:40:57 -04002837 int indexBefore = findNotificationRecordIndexLocked(record);
2838 boolean interceptBefore = record.isIntercepted();
Chris Wren3ad4e3a2014-09-02 17:23:51 -04002839 int visibilityBefore = record.getPackageVisibilityOverride();
Chris Wren470c1ac2014-05-21 15:28:10 -04002840 recon.applyChangesLocked(record);
Chris Wren333a61c2014-05-28 16:40:57 -04002841 applyZenModeLocked(record);
Chris Wren54bbef42014-07-09 18:37:56 -04002842 mRankingHelper.sort(mNotificationList);
Chris Wren333a61c2014-05-28 16:40:57 -04002843 int indexAfter = findNotificationRecordIndexLocked(record);
2844 boolean interceptAfter = record.isIntercepted();
Chris Wren3ad4e3a2014-09-02 17:23:51 -04002845 int visibilityAfter = record.getPackageVisibilityOverride();
2846 changed = indexBefore != indexAfter || interceptBefore != interceptAfter
2847 || visibilityBefore != visibilityAfter;
Chris Wrena3446562014-06-03 18:11:47 -04002848 if (interceptBefore && !interceptAfter) {
2849 buzzBeepBlinkLocked(record);
2850 }
Chris Wrenf9536642014-04-17 10:01:54 -04002851 }
Chris Wren333a61c2014-05-28 16:40:57 -04002852 if (changed) {
Chris Wren470c1ac2014-05-21 15:28:10 -04002853 scheduleSendRankingUpdate();
2854 }
2855 }
2856
Chris Wren51017d02015-12-15 15:34:46 -05002857 private void handleRankingSort() {
Chris Wren54bbef42014-07-09 18:37:56 -04002858 synchronized (mNotificationList) {
2859 final int N = mNotificationList.size();
2860 ArrayList<String> orderBefore = new ArrayList<String>(N);
Chris Wren3ad4e3a2014-09-02 17:23:51 -04002861 int[] visibilities = new int[N];
Julia Reynolds69766692016-02-01 15:35:08 -05002862 int [] importances = new int[N];
Chris Wren54bbef42014-07-09 18:37:56 -04002863 for (int i = 0; i < N; i++) {
2864 final NotificationRecord r = mNotificationList.get(i);
2865 orderBefore.add(r.getKey());
Chris Wren3ad4e3a2014-09-02 17:23:51 -04002866 visibilities[i] = r.getPackageVisibilityOverride();
Julia Reynolds69766692016-02-01 15:35:08 -05002867 importances[i] = r.getImportance();
Chris Wren54bbef42014-07-09 18:37:56 -04002868 mRankingHelper.extractSignals(r);
2869 }
Chris Wren19a02b02015-12-22 10:34:22 -05002870 mRankingHelper.sort(mNotificationList);
Chris Wren54bbef42014-07-09 18:37:56 -04002871 for (int i = 0; i < N; i++) {
Chris Wren3ad4e3a2014-09-02 17:23:51 -04002872 final NotificationRecord r = mNotificationList.get(i);
2873 if (!orderBefore.get(i).equals(r.getKey())
Julia Reynolds69766692016-02-01 15:35:08 -05002874 || visibilities[i] != r.getPackageVisibilityOverride()
2875 || importances[i] != r.getImportance()) {
Chris Wren54bbef42014-07-09 18:37:56 -04002876 scheduleSendRankingUpdate();
2877 return;
2878 }
2879 }
2880 }
2881 }
2882
Christoph Studerd5092bc2014-07-03 17:47:58 +02002883 // let zen mode evaluate this record
Chris Wren333a61c2014-05-28 16:40:57 -04002884 private void applyZenModeLocked(NotificationRecord record) {
Christoph Studerd5092bc2014-07-03 17:47:58 +02002885 record.setIntercepted(mZenModeHelper.shouldIntercept(record));
Julia Reynoldsf612869ae2015-11-05 16:48:55 -05002886 if (record.isIntercepted()) {
Julia Reynoldsd5607292016-02-05 15:25:58 -05002887 int suppressed = (mZenModeHelper.shouldSuppressWhenScreenOff()
2888 ? SUPPRESSED_EFFECT_SCREEN_OFF : 0)
2889 | (mZenModeHelper.shouldSuppressWhenScreenOn()
2890 ? SUPPRESSED_EFFECT_SCREEN_ON : 0);
Julia Reynoldsf612869ae2015-11-05 16:48:55 -05002891 record.setSuppressedVisualEffects(suppressed);
2892 }
Chris Wren333a61c2014-05-28 16:40:57 -04002893 }
2894
Chris Wren470c1ac2014-05-21 15:28:10 -04002895 // lock on mNotificationList
2896 private int findNotificationRecordIndexLocked(NotificationRecord target) {
Chris Wren54bbef42014-07-09 18:37:56 -04002897 return mRankingHelper.indexOf(mNotificationList, target);
Chris Wrenf9536642014-04-17 10:01:54 -04002898 }
2899
2900 private void scheduleSendRankingUpdate() {
2901 mHandler.removeMessages(MESSAGE_SEND_RANKING_UPDATE);
2902 Message m = Message.obtain(mHandler, MESSAGE_SEND_RANKING_UPDATE);
2903 mHandler.sendMessage(m);
2904 }
2905
2906 private void handleSendRankingUpdate() {
2907 synchronized (mNotificationList) {
Chris Wren333a61c2014-05-28 16:40:57 -04002908 mListeners.notifyRankingUpdateLocked();
Chris Wrenf9536642014-04-17 10:01:54 -04002909 }
2910 }
2911
John Spurlockd8afe3c2014-08-01 14:04:07 -04002912 private void scheduleListenerHintsChanged(int state) {
2913 mHandler.removeMessages(MESSAGE_LISTENER_HINTS_CHANGED);
2914 mHandler.obtainMessage(MESSAGE_LISTENER_HINTS_CHANGED, state, 0).sendToTarget();
John Spurlock1fa865f2014-07-21 14:56:39 -04002915 }
2916
Christoph Studer85a384b2014-08-27 20:16:15 +02002917 private void scheduleInterruptionFilterChanged(int listenerInterruptionFilter) {
2918 mHandler.removeMessages(MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED);
2919 mHandler.obtainMessage(
2920 MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED,
2921 listenerInterruptionFilter,
2922 0).sendToTarget();
2923 }
2924
John Spurlockd8afe3c2014-08-01 14:04:07 -04002925 private void handleListenerHintsChanged(int hints) {
John Spurlock1fa865f2014-07-21 14:56:39 -04002926 synchronized (mNotificationList) {
John Spurlockd8afe3c2014-08-01 14:04:07 -04002927 mListeners.notifyListenerHintsChangedLocked(hints);
John Spurlock1fa865f2014-07-21 14:56:39 -04002928 }
2929 }
2930
Christoph Studer85a384b2014-08-27 20:16:15 +02002931 private void handleListenerInterruptionFilterChanged(int interruptionFilter) {
2932 synchronized (mNotificationList) {
2933 mListeners.notifyInterruptionFilterChanged(interruptionFilter);
2934 }
2935 }
2936
Adam Lesinski182f73f2013-12-05 16:48:06 -08002937 private final class WorkerHandler extends Handler
2938 {
2939 @Override
2940 public void handleMessage(Message msg)
2941 {
2942 switch (msg.what)
2943 {
2944 case MESSAGE_TIMEOUT:
2945 handleTimeout((ToastRecord)msg.obj);
2946 break;
John Spurlock056c5192014-04-20 21:52:01 -04002947 case MESSAGE_SAVE_POLICY_FILE:
2948 handleSavePolicyFile();
2949 break;
Chris Wrenf9536642014-04-17 10:01:54 -04002950 case MESSAGE_SEND_RANKING_UPDATE:
2951 handleSendRankingUpdate();
2952 break;
John Spurlockd8afe3c2014-08-01 14:04:07 -04002953 case MESSAGE_LISTENER_HINTS_CHANGED:
2954 handleListenerHintsChanged(msg.arg1);
John Spurlock1fa865f2014-07-21 14:56:39 -04002955 break;
Christoph Studer85a384b2014-08-27 20:16:15 +02002956 case MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED:
2957 handleListenerInterruptionFilterChanged(msg.arg1);
2958 break;
Chris Wrenf9536642014-04-17 10:01:54 -04002959 }
2960 }
2961
2962 }
2963
Chris Wren51017d02015-12-15 15:34:46 -05002964 private final class RankingHandlerWorker extends Handler implements RankingHandler
Chris Wrenf9536642014-04-17 10:01:54 -04002965 {
Chris Wren51017d02015-12-15 15:34:46 -05002966 public RankingHandlerWorker(Looper looper) {
Chris Wrenf9536642014-04-17 10:01:54 -04002967 super(looper);
2968 }
2969
2970 @Override
2971 public void handleMessage(Message msg) {
2972 switch (msg.what) {
2973 case MESSAGE_RECONSIDER_RANKING:
2974 handleRankingReconsideration(msg);
2975 break;
Chris Wren51017d02015-12-15 15:34:46 -05002976 case MESSAGE_RANKING_SORT:
2977 handleRankingSort();
Chris Wren54bbef42014-07-09 18:37:56 -04002978 break;
Adam Lesinski182f73f2013-12-05 16:48:06 -08002979 }
2980 }
Chris Wren51017d02015-12-15 15:34:46 -05002981
2982 public void requestSort() {
2983 removeMessages(MESSAGE_RANKING_SORT);
2984 sendEmptyMessage(MESSAGE_RANKING_SORT);
2985 }
2986
2987 public void requestReconsideration(RankingReconsideration recon) {
2988 Message m = Message.obtain(this,
2989 NotificationManagerService.MESSAGE_RECONSIDER_RANKING, recon);
2990 long delay = recon.getDelay(TimeUnit.MILLISECONDS);
2991 sendMessageDelayed(m, delay);
2992 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08002993 }
2994
Adam Lesinski182f73f2013-12-05 16:48:06 -08002995 // Notifications
2996 // ============================================================================
2997 static int clamp(int x, int low, int high) {
2998 return (x < low) ? low : ((x > high) ? high : x);
2999 }
3000
3001 void sendAccessibilityEvent(Notification notification, CharSequence packageName) {
3002 AccessibilityManager manager = AccessibilityManager.getInstance(getContext());
svetoslavganov75986cf2009-05-14 22:28:01 -07003003 if (!manager.isEnabled()) {
3004 return;
3005 }
3006
3007 AccessibilityEvent event =
3008 AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
3009 event.setPackageName(packageName);
3010 event.setClassName(Notification.class.getName());
3011 event.setParcelableData(notification);
3012 CharSequence tickerText = notification.tickerText;
3013 if (!TextUtils.isEmpty(tickerText)) {
3014 event.getText().add(tickerText);
3015 }
3016
3017 manager.sendAccessibilityEvent(event);
3018 }
3019
Christoph Studer546bec82014-03-14 12:17:12 +01003020 private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason) {
Joe Onorato46439ce2010-11-19 13:56:21 -08003021 // tell the app
3022 if (sendDelete) {
Daniel Sandlerfde19b12013-01-17 00:21:05 -05003023 if (r.getNotification().deleteIntent != null) {
Joe Onorato46439ce2010-11-19 13:56:21 -08003024 try {
Daniel Sandlerfde19b12013-01-17 00:21:05 -05003025 r.getNotification().deleteIntent.send();
Joe Onorato46439ce2010-11-19 13:56:21 -08003026 } catch (PendingIntent.CanceledException ex) {
3027 // do nothing - there's no relevant way to recover, and
3028 // no reason to let this propagate
Daniel Sandler4f91efd2013-04-25 16:38:41 -04003029 Slog.w(TAG, "canceled PendingIntent for " + r.sbn.getPackageName(), ex);
Joe Onorato46439ce2010-11-19 13:56:21 -08003030 }
3031 }
3032 }
3033
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003034 // status bar
Dan Sandlerd63f9322015-05-06 15:18:49 -04003035 if (r.getNotification().getSmallIcon() != null) {
Christoph Studer71f18fd2014-05-20 17:02:04 +02003036 r.isCanceled = true;
Chris Wren333a61c2014-05-28 16:40:57 -04003037 mListeners.notifyRemovedLocked(r.sbn);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003038 }
3039
Chris Wren6054e612014-11-25 17:16:46 -05003040 final String canceledKey = r.getKey();
3041
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003042 // sound
Chris Wren6054e612014-11-25 17:16:46 -05003043 if (canceledKey.equals(mSoundNotificationKey)) {
3044 mSoundNotificationKey = null;
Jeff Sharkey098d5802012-04-26 17:30:34 -07003045 final long identity = Binder.clearCallingIdentity();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003046 try {
Adam Lesinski182f73f2013-12-05 16:48:06 -08003047 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
Jeff Sharkey098d5802012-04-26 17:30:34 -07003048 if (player != null) {
3049 player.stopAsync();
3050 }
3051 } catch (RemoteException e) {
3052 } finally {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003053 Binder.restoreCallingIdentity(identity);
3054 }
3055 }
3056
3057 // vibrate
Chris Wren6054e612014-11-25 17:16:46 -05003058 if (canceledKey.equals(mVibrateNotificationKey)) {
3059 mVibrateNotificationKey = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003060 long identity = Binder.clearCallingIdentity();
3061 try {
3062 mVibrator.cancel();
3063 }
3064 finally {
3065 Binder.restoreCallingIdentity(identity);
3066 }
3067 }
3068
3069 // light
Chris Wren6054e612014-11-25 17:16:46 -05003070 mLights.remove(canceledKey);
Daniel Sandler23d7c702013-03-07 16:32:06 -05003071
Christoph Studer546bec82014-03-14 12:17:12 +01003072 // Record usage stats
3073 switch (reason) {
3074 case REASON_DELEGATE_CANCEL:
3075 case REASON_DELEGATE_CANCEL_ALL:
3076 case REASON_LISTENER_CANCEL:
3077 case REASON_LISTENER_CANCEL_ALL:
3078 mUsageStats.registerDismissedByUser(r);
3079 break;
Chris Wren9fa689f2015-11-20 16:44:53 -05003080 case REASON_APP_CANCEL:
3081 case REASON_APP_CANCEL_ALL:
Christoph Studer546bec82014-03-14 12:17:12 +01003082 mUsageStats.registerRemovedByApp(r);
3083 break;
Christoph Studer546bec82014-03-14 12:17:12 +01003084 }
3085
Christoph Studercef37cf2014-07-25 14:18:17 +02003086 mNotificationsByKey.remove(r.sbn.getKey());
Christoph Studer265c1052014-07-23 17:14:33 +02003087 String groupKey = r.getGroupKey();
3088 NotificationRecord groupSummary = mSummaryByGroupKey.get(groupKey);
3089 if (groupSummary != null && groupSummary.getKey().equals(r.getKey())) {
3090 mSummaryByGroupKey.remove(groupKey);
3091 }
Christoph Studercef37cf2014-07-25 14:18:17 +02003092
Daniel Sandler23d7c702013-03-07 16:32:06 -05003093 // Save it for users of getHistoricalNotifications()
3094 mArchive.record(r.sbn);
Christoph Studer81e5b5f2014-10-22 17:19:56 +02003095
Chris Wren6650e572015-05-15 17:19:25 -04003096 final long now = System.currentTimeMillis();
Chris Wrene6ddb8a2015-05-27 15:21:00 -04003097 EventLogTags.writeNotificationCanceled(canceledKey, reason,
3098 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003099 }
3100
3101 /**
Dianne Hackbornd8a43f62009-08-17 23:33:56 -07003102 * Cancels a notification ONLY if it has all of the {@code mustHaveFlags}
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003103 * and none of the {@code mustNotHaveFlags}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003104 */
John Spurlocke6a7d932014-03-13 12:29:00 -04003105 void cancelNotification(final int callingUid, final int callingPid,
3106 final String pkg, final String tag, final int id,
Svetoslav Ganov835835e2013-08-04 20:17:52 -07003107 final int mustHaveFlags, final int mustNotHaveFlags, final boolean sendDelete,
John Spurlock7340fc82014-04-24 18:50:12 -04003108 final int userId, final int reason, final ManagedServiceInfo listener) {
Svetoslav Ganov835835e2013-08-04 20:17:52 -07003109 // In enqueueNotificationInternal notifications are added by scheduling the
3110 // work on the worker handler. Hence, we also schedule the cancel on this
3111 // handler to avoid a scenario where an add notification call followed by a
3112 // remove notification call ends up in not removing the notification.
3113 mHandler.post(new Runnable() {
3114 @Override
3115 public void run() {
Christoph Studere4ef156b2014-07-04 18:41:57 +02003116 String listenerName = listener == null ? null : listener.component.toShortString();
Chris Wrenbddb5bc2015-03-04 08:47:46 -08003117 if (DBG) EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, id, tag,
3118 userId, mustHaveFlags, mustNotHaveFlags, reason, listenerName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003119
Svetoslav Ganov835835e2013-08-04 20:17:52 -07003120 synchronized (mNotificationList) {
3121 int index = indexOfNotificationLocked(pkg, tag, id, userId);
3122 if (index >= 0) {
3123 NotificationRecord r = mNotificationList.get(index);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003124
Christoph Studer546bec82014-03-14 12:17:12 +01003125 // Ideally we'd do this in the caller of this method. However, that would
3126 // require the caller to also find the notification.
3127 if (reason == REASON_DELEGATE_CLICK) {
3128 mUsageStats.registerClickedByUser(r);
3129 }
3130
Svetoslav Ganov835835e2013-08-04 20:17:52 -07003131 if ((r.getNotification().flags & mustHaveFlags) != mustHaveFlags) {
3132 return;
3133 }
3134 if ((r.getNotification().flags & mustNotHaveFlags) != 0) {
3135 return;
3136 }
3137
3138 mNotificationList.remove(index);
3139
Christoph Studer546bec82014-03-14 12:17:12 +01003140 cancelNotificationLocked(r, sendDelete, reason);
Christoph Studer265c1052014-07-23 17:14:33 +02003141 cancelGroupChildrenLocked(r, callingUid, callingPid, listenerName,
3142 REASON_GROUP_SUMMARY_CANCELED);
Svetoslav Ganov835835e2013-08-04 20:17:52 -07003143 updateLightsLocked();
3144 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003145 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003146 }
Svetoslav Ganov835835e2013-08-04 20:17:52 -07003147 });
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003148 }
3149
3150 /**
Daniel Sandler321e9c52012-10-12 10:59:26 -07003151 * Determine whether the userId applies to the notification in question, either because
3152 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard).
3153 */
3154 private boolean notificationMatchesUserId(NotificationRecord r, int userId) {
3155 return
3156 // looking for USER_ALL notifications? match everything
3157 userId == UserHandle.USER_ALL
3158 // a notification sent to USER_ALL matches any query
Daniel Sandlerfde19b12013-01-17 00:21:05 -05003159 || r.getUserId() == UserHandle.USER_ALL
Daniel Sandler321e9c52012-10-12 10:59:26 -07003160 // an exact user match
Daniel Sandlerfde19b12013-01-17 00:21:05 -05003161 || r.getUserId() == userId;
Daniel Sandler321e9c52012-10-12 10:59:26 -07003162 }
3163
3164 /**
Kenny Guy3a7c4a52014-03-03 18:24:03 +00003165 * Determine whether the userId applies to the notification in question, either because
3166 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard) or
Kenny Guy2a764942014-04-02 13:29:20 +01003167 * because it matches one of the users profiles.
Kenny Guy3a7c4a52014-03-03 18:24:03 +00003168 */
Kenny Guy2a764942014-04-02 13:29:20 +01003169 private boolean notificationMatchesCurrentProfiles(NotificationRecord r, int userId) {
Kenny Guya263e4e2014-03-03 18:24:03 +00003170 return notificationMatchesUserId(r, userId)
John Spurlockb408e8e2014-04-23 21:12:45 -04003171 || mUserProfiles.isCurrentProfile(r.getUserId());
Kenny Guy3a7c4a52014-03-03 18:24:03 +00003172 }
3173
3174 /**
Julia Reynoldsef37f282016-02-12 09:11:27 -05003175 * Cancels all notifications from a given package that have all of the
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003176 * {@code mustHaveFlags}.
3177 */
John Spurlocke6a7d932014-03-13 12:29:00 -04003178 boolean cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, int mustHaveFlags,
Julia Reynoldsef37f282016-02-12 09:11:27 -05003179 int mustNotHaveFlags, boolean doit, int userId, int reason,
John Spurlock7340fc82014-04-24 18:50:12 -04003180 ManagedServiceInfo listener) {
Christoph Studere4ef156b2014-07-04 18:41:57 +02003181 String listenerName = listener == null ? null : listener.component.toShortString();
John Spurlocke6a7d932014-03-13 12:29:00 -04003182 EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
3183 pkg, userId, mustHaveFlags, mustNotHaveFlags, reason,
Christoph Studere4ef156b2014-07-04 18:41:57 +02003184 listenerName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003185
3186 synchronized (mNotificationList) {
3187 final int N = mNotificationList.size();
Christoph Studere4ef156b2014-07-04 18:41:57 +02003188 ArrayList<NotificationRecord> canceledNotifications = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003189 for (int i = N-1; i >= 0; --i) {
3190 NotificationRecord r = mNotificationList.get(i);
Daniel Sandler321e9c52012-10-12 10:59:26 -07003191 if (!notificationMatchesUserId(r, userId)) {
Dianne Hackborn41203752012-08-31 14:05:51 -07003192 continue;
3193 }
Amith Yamasani5ec00e92012-11-07 16:58:30 -08003194 // Don't remove notifications to all, if there's no package name specified
Daniel Sandlerfde19b12013-01-17 00:21:05 -05003195 if (r.getUserId() == UserHandle.USER_ALL && pkg == null) {
Amith Yamasani5ec00e92012-11-07 16:58:30 -08003196 continue;
3197 }
Daniel Sandlerfde19b12013-01-17 00:21:05 -05003198 if ((r.getFlags() & mustHaveFlags) != mustHaveFlags) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003199 continue;
3200 }
Daniel Sandlerfde19b12013-01-17 00:21:05 -05003201 if ((r.getFlags() & mustNotHaveFlags) != 0) {
Dianne Hackbornd8a43f62009-08-17 23:33:56 -07003202 continue;
3203 }
Daniel Sandler4f91efd2013-04-25 16:38:41 -04003204 if (pkg != null && !r.sbn.getPackageName().equals(pkg)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003205 continue;
3206 }
Christoph Studere4ef156b2014-07-04 18:41:57 +02003207 if (canceledNotifications == null) {
3208 canceledNotifications = new ArrayList<>();
3209 }
3210 canceledNotifications.add(r);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08003211 if (!doit) {
3212 return true;
3213 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003214 mNotificationList.remove(i);
Christoph Studer546bec82014-03-14 12:17:12 +01003215 cancelNotificationLocked(r, false, reason);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003216 }
Christoph Studere4ef156b2014-07-04 18:41:57 +02003217 if (doit && canceledNotifications != null) {
3218 final int M = canceledNotifications.size();
3219 for (int i = 0; i < M; i++) {
3220 cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid,
Christoph Studer265c1052014-07-23 17:14:33 +02003221 listenerName, REASON_GROUP_SUMMARY_CANCELED);
Christoph Studere4ef156b2014-07-04 18:41:57 +02003222 }
3223 }
3224 if (canceledNotifications != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003225 updateLightsLocked();
3226 }
Christoph Studere4ef156b2014-07-04 18:41:57 +02003227 return canceledNotifications != null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003228 }
3229 }
3230
Adam Lesinski350159c2014-03-27 11:15:11 -07003231 void cancelAllLocked(int callingUid, int callingPid, int userId, int reason,
John Spurlock7340fc82014-04-24 18:50:12 -04003232 ManagedServiceInfo listener, boolean includeCurrentProfiles) {
Christoph Studere4ef156b2014-07-04 18:41:57 +02003233 String listenerName = listener == null ? null : listener.component.toShortString();
John Spurlocke6a7d932014-03-13 12:29:00 -04003234 EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
Christoph Studere4ef156b2014-07-04 18:41:57 +02003235 null, userId, 0, 0, reason, listenerName);
Christoph Studer546bec82014-03-14 12:17:12 +01003236
Christoph Studere4ef156b2014-07-04 18:41:57 +02003237 ArrayList<NotificationRecord> canceledNotifications = null;
Adam Lesinskie8240262014-03-26 16:01:00 -07003238 final int N = mNotificationList.size();
3239 for (int i=N-1; i>=0; i--) {
3240 NotificationRecord r = mNotificationList.get(i);
Kenny Guya263e4e2014-03-03 18:24:03 +00003241 if (includeCurrentProfiles) {
3242 if (!notificationMatchesCurrentProfiles(r, userId)) {
3243 continue;
3244 }
3245 } else {
3246 if (!notificationMatchesUserId(r, userId)) {
3247 continue;
3248 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003249 }
3250
Adam Lesinskie8240262014-03-26 16:01:00 -07003251 if ((r.getFlags() & (Notification.FLAG_ONGOING_EVENT
3252 | Notification.FLAG_NO_CLEAR)) == 0) {
3253 mNotificationList.remove(i);
Christoph Studer546bec82014-03-14 12:17:12 +01003254 cancelNotificationLocked(r, true, reason);
Christoph Studere4ef156b2014-07-04 18:41:57 +02003255 // Make a note so we can cancel children later.
3256 if (canceledNotifications == null) {
3257 canceledNotifications = new ArrayList<>();
3258 }
3259 canceledNotifications.add(r);
Adam Lesinskie8240262014-03-26 16:01:00 -07003260 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003261 }
Christoph Studere4ef156b2014-07-04 18:41:57 +02003262 int M = canceledNotifications != null ? canceledNotifications.size() : 0;
3263 for (int i = 0; i < M; i++) {
3264 cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid,
Christoph Studer265c1052014-07-23 17:14:33 +02003265 listenerName, REASON_GROUP_SUMMARY_CANCELED);
Christoph Studere4ef156b2014-07-04 18:41:57 +02003266 }
Adam Lesinskie8240262014-03-26 16:01:00 -07003267 updateLightsLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003268 }
3269
Christoph Studere4ef156b2014-07-04 18:41:57 +02003270 // Warning: The caller is responsible for invoking updateLightsLocked().
3271 private void cancelGroupChildrenLocked(NotificationRecord r, int callingUid, int callingPid,
Christoph Studer265c1052014-07-23 17:14:33 +02003272 String listenerName, int reason) {
Christoph Studere4ef156b2014-07-04 18:41:57 +02003273 Notification n = r.getNotification();
Christoph Studer3f31f5d2014-07-31 16:55:32 +02003274 if (!n.isGroupSummary()) {
Christoph Studere4ef156b2014-07-04 18:41:57 +02003275 return;
3276 }
3277
3278 String pkg = r.sbn.getPackageName();
3279 int userId = r.getUserId();
3280
3281 if (pkg == null) {
3282 if (DBG) Log.e(TAG, "No package for group summary: " + r.getKey());
3283 return;
3284 }
3285
3286 final int N = mNotificationList.size();
3287 for (int i = N - 1; i >= 0; i--) {
3288 NotificationRecord childR = mNotificationList.get(i);
Christoph Studere4ef156b2014-07-04 18:41:57 +02003289 StatusBarNotification childSbn = childR.sbn;
Christoph Studerc44caa92014-08-22 19:16:00 +02003290 if (childR.getNotification().isGroupChild() &&
3291 childR.getGroupKey().equals(r.getGroupKey())) {
Christoph Studer265c1052014-07-23 17:14:33 +02003292 EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, childSbn.getId(),
3293 childSbn.getTag(), userId, 0, 0, reason, listenerName);
Christoph Studere4ef156b2014-07-04 18:41:57 +02003294 mNotificationList.remove(i);
Christoph Studer265c1052014-07-23 17:14:33 +02003295 cancelNotificationLocked(childR, false, reason);
Christoph Studere4ef156b2014-07-04 18:41:57 +02003296 }
3297 }
3298 }
3299
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003300 // lock on mNotificationList
Adam Lesinski182f73f2013-12-05 16:48:06 -08003301 void updateLightsLocked()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003302 {
The Android Open Source Project10592532009-03-18 17:39:46 -07003303 // handle notification lights
Chris Wren6054e612014-11-25 17:16:46 -05003304 NotificationRecord ledNotification = null;
3305 while (ledNotification == null && !mLights.isEmpty()) {
3306 final String owner = mLights.get(mLights.size() - 1);
3307 ledNotification = mNotificationsByKey.get(owner);
3308 if (ledNotification == null) {
3309 Slog.wtfStack(TAG, "LED Notification does not exist: " + owner);
3310 mLights.remove(owner);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003311 }
3312 }
Mike Lockwoodc22404a2009-12-02 11:15:02 -05003313
Mike Lockwood63b5ad92011-08-30 09:55:30 -04003314 // Don't flash while we are in a call or screen is on
Chris Wren6054e612014-11-25 17:16:46 -05003315 if (ledNotification == null || mInCall || mScreenOn) {
Mike Lockwood3cb67a32009-11-27 14:25:58 -05003316 mNotificationLight.turnOff();
Wei Liu97e56662016-03-04 10:52:33 -08003317 if (mStatusBar != null) {
3318 mStatusBar.notificationLightOff();
3319 }
The Android Open Source Project10592532009-03-18 17:39:46 -07003320 } else {
Chris Wren6054e612014-11-25 17:16:46 -05003321 final Notification ledno = ledNotification.sbn.getNotification();
Daniel Sandlerfde19b12013-01-17 00:21:05 -05003322 int ledARGB = ledno.ledARGB;
3323 int ledOnMS = ledno.ledOnMS;
3324 int ledOffMS = ledno.ledOffMS;
3325 if ((ledno.defaults & Notification.DEFAULT_LIGHTS) != 0) {
Mike Lockwood670f9322010-01-20 12:13:36 -05003326 ledARGB = mDefaultNotificationColor;
3327 ledOnMS = mDefaultNotificationLedOn;
3328 ledOffMS = mDefaultNotificationLedOff;
3329 }
3330 if (mNotificationPulseEnabled) {
3331 // pulse repeatedly
Adam Lesinski182f73f2013-12-05 16:48:06 -08003332 mNotificationLight.setFlashing(ledARGB, Light.LIGHT_FLASH_TIMED,
Mike Lockwood670f9322010-01-20 12:13:36 -05003333 ledOnMS, ledOffMS);
Mike Lockwood670f9322010-01-20 12:13:36 -05003334 }
Wei Liu97e56662016-03-04 10:52:33 -08003335 if (mStatusBar != null) {
3336 // let SystemUI make an independent decision
3337 mStatusBar.notificationLightPulse(ledARGB, ledOnMS, ledOffMS);
3338 }
The Android Open Source Project10592532009-03-18 17:39:46 -07003339 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003340 }
3341
3342 // lock on mNotificationList
Adam Lesinski182f73f2013-12-05 16:48:06 -08003343 int indexOfNotificationLocked(String pkg, String tag, int id, int userId)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003344 {
3345 ArrayList<NotificationRecord> list = mNotificationList;
3346 final int len = list.size();
3347 for (int i=0; i<len; i++) {
3348 NotificationRecord r = list.get(i);
Vladimir Marko2526f332013-09-11 11:13:55 +01003349 if (notificationMatchesUserId(r, userId) && r.sbn.getId() == id &&
3350 TextUtils.equals(r.sbn.getTag(), tag) && r.sbn.getPackageName().equals(pkg)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003351 return i;
3352 }
3353 }
3354 return -1;
3355 }
3356
Christoph Studer71f18fd2014-05-20 17:02:04 +02003357 // lock on mNotificationList
3358 int indexOfNotificationLocked(String key) {
Christoph Studerc5115552014-06-12 20:22:31 +02003359 final int N = mNotificationList.size();
3360 for (int i = 0; i < N; i++) {
3361 if (key.equals(mNotificationList.get(i).getKey())) {
3362 return i;
3363 }
Christoph Studer71f18fd2014-05-20 17:02:04 +02003364 }
Christoph Studerc5115552014-06-12 20:22:31 +02003365 return -1;
Christoph Studer71f18fd2014-05-20 17:02:04 +02003366 }
3367
Mike Lockwoodc22404a2009-12-02 11:15:02 -05003368 private void updateNotificationPulse() {
3369 synchronized (mNotificationList) {
3370 updateLightsLocked();
3371 }
3372 }
John Spurlocke677d712014-02-13 12:52:19 -05003373
John Spurlock7340fc82014-04-24 18:50:12 -04003374 private static boolean isUidSystem(int uid) {
3375 final int appid = UserHandle.getAppId(uid);
3376 return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0);
3377 }
John Spurlockb408e8e2014-04-23 21:12:45 -04003378
John Spurlock7340fc82014-04-24 18:50:12 -04003379 private static boolean isCallerSystem() {
3380 return isUidSystem(Binder.getCallingUid());
3381 }
3382
3383 private static void checkCallerIsSystem() {
3384 if (isCallerSystem()) {
3385 return;
3386 }
3387 throw new SecurityException("Disallowed call for uid " + Binder.getCallingUid());
3388 }
3389
3390 private static void checkCallerIsSystemOrSameApp(String pkg) {
3391 if (isCallerSystem()) {
3392 return;
3393 }
3394 final int uid = Binder.getCallingUid();
3395 try {
3396 ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo(
3397 pkg, 0, UserHandle.getCallingUserId());
Dan Sandler09afc2e2014-07-18 14:29:20 -04003398 if (ai == null) {
3399 throw new SecurityException("Unknown package " + pkg);
3400 }
John Spurlock7340fc82014-04-24 18:50:12 -04003401 if (!UserHandle.isSameApp(ai.uid, uid)) {
3402 throw new SecurityException("Calling uid " + uid + " gave package"
3403 + pkg + " which is owned by uid " + ai.uid);
3404 }
3405 } catch (RemoteException re) {
3406 throw new SecurityException("Unknown package " + pkg + "\n" + re);
3407 }
3408 }
3409
John Spurlock32fe4c62014-10-02 12:16:02 -04003410 private static String callStateToString(int state) {
3411 switch (state) {
3412 case TelephonyManager.CALL_STATE_IDLE: return "CALL_STATE_IDLE";
3413 case TelephonyManager.CALL_STATE_RINGING: return "CALL_STATE_RINGING";
3414 case TelephonyManager.CALL_STATE_OFFHOOK: return "CALL_STATE_OFFHOOK";
3415 default: return "CALL_STATE_UNKNOWN_" + state;
3416 }
3417 }
3418
3419 private void listenForCallState() {
3420 TelephonyManager.from(getContext()).listen(new PhoneStateListener() {
3421 @Override
3422 public void onCallStateChanged(int state, String incomingNumber) {
3423 if (mCallState == state) return;
3424 if (DBG) Slog.d(TAG, "Call state changed: " + callStateToString(state));
3425 mCallState = state;
3426 }
3427 }, PhoneStateListener.LISTEN_CALL_STATE);
3428 }
3429
Christoph Studer05ad4822014-05-16 14:16:03 +02003430 /**
3431 * Generates a NotificationRankingUpdate from 'sbns', considering only
3432 * notifications visible to the given listener.
Chris Wren333a61c2014-05-28 16:40:57 -04003433 *
3434 * <p>Caller must hold a lock on mNotificationList.</p>
Christoph Studer05ad4822014-05-16 14:16:03 +02003435 */
Chris Wren333a61c2014-05-28 16:40:57 -04003436 private NotificationRankingUpdate makeRankingUpdateLocked(ManagedServiceInfo info) {
Chris Wren333a61c2014-05-28 16:40:57 -04003437 final int N = mNotificationList.size();
3438 ArrayList<String> keys = new ArrayList<String>(N);
Christoph Studer1d599da2014-06-12 15:25:59 +02003439 ArrayList<String> interceptedKeys = new ArrayList<String>(N);
Chris Wrenbdf33762015-12-04 15:50:51 -05003440 ArrayList<Integer> importance = new ArrayList<>(N);
Chris Wren3ad4e3a2014-09-02 17:23:51 -04003441 Bundle visibilityOverrides = new Bundle();
Julia Reynoldsf612869ae2015-11-05 16:48:55 -05003442 Bundle suppressedVisualEffects = new Bundle();
Chris Wrenbdf33762015-12-04 15:50:51 -05003443 Bundle explanation = new Bundle();
Chris Wren333a61c2014-05-28 16:40:57 -04003444 for (int i = 0; i < N; i++) {
3445 NotificationRecord record = mNotificationList.get(i);
Christoph Studercef37cf2014-07-25 14:18:17 +02003446 if (!isVisibleToListener(record.sbn, info)) {
Christoph Studer05ad4822014-05-16 14:16:03 +02003447 continue;
3448 }
Chris Wrenbdf33762015-12-04 15:50:51 -05003449 final String key = record.sbn.getKey();
3450 keys.add(key);
3451 importance.add(record.getImportance());
3452 if (record.getImportanceExplanation() != null) {
3453 explanation.putCharSequence(key, record.getImportanceExplanation());
3454 }
Chris Wren333a61c2014-05-28 16:40:57 -04003455 if (record.isIntercepted()) {
Chris Wrenbdf33762015-12-04 15:50:51 -05003456 interceptedKeys.add(key);
Julia Reynoldsf612869ae2015-11-05 16:48:55 -05003457
Christoph Studer05ad4822014-05-16 14:16:03 +02003458 }
Chris Wrenbdf33762015-12-04 15:50:51 -05003459 suppressedVisualEffects.putInt(key, record.getSuppressedVisualEffects());
Chris Wren3ad4e3a2014-09-02 17:23:51 -04003460 if (record.getPackageVisibilityOverride()
3461 != NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE) {
Chris Wrenbdf33762015-12-04 15:50:51 -05003462 visibilityOverrides.putInt(key, record.getPackageVisibilityOverride());
Chris Wren3ad4e3a2014-09-02 17:23:51 -04003463 }
Christoph Studer05ad4822014-05-16 14:16:03 +02003464 }
Chris Wrenbdf33762015-12-04 15:50:51 -05003465 final int M = keys.size();
3466 String[] keysAr = keys.toArray(new String[M]);
Christoph Studer1d599da2014-06-12 15:25:59 +02003467 String[] interceptedKeysAr = interceptedKeys.toArray(new String[interceptedKeys.size()]);
Chris Wrenbdf33762015-12-04 15:50:51 -05003468 int[] importanceAr = new int[M];
3469 for (int i = 0; i < M; i++) {
3470 importanceAr[i] = importance.get(i);
3471 }
Chris Wren3ad4e3a2014-09-02 17:23:51 -04003472 return new NotificationRankingUpdate(keysAr, interceptedKeysAr, visibilityOverrides,
Julia Reynolds0421e6d2016-01-08 09:51:24 -05003473 suppressedVisualEffects, importanceAr, explanation);
Christoph Studer05ad4822014-05-16 14:16:03 +02003474 }
3475
Christoph Studercef37cf2014-07-25 14:18:17 +02003476 private boolean isVisibleToListener(StatusBarNotification sbn, ManagedServiceInfo listener) {
3477 if (!listener.enabledAndUserMatches(sbn.getUserId())) {
3478 return false;
3479 }
Justin Koh8d11a5a2014-08-04 18:29:49 -07003480 // TODO: remove this for older listeners.
Christoph Studercef37cf2014-07-25 14:18:17 +02003481 return true;
3482 }
3483
Andrei Stingaceanu355b2322016-02-12 16:43:51 +00003484 private boolean isPackageSuspendedForUser(String pkg, int uid) {
Andrei Stingaceanu0122f6512016-01-22 15:33:03 +00003485 int userId = UserHandle.getUserId(uid);
Andrei Stingaceanu0122f6512016-01-22 15:33:03 +00003486 try {
Andrei Stingaceanu355b2322016-02-12 16:43:51 +00003487 return AppGlobals.getPackageManager().isPackageSuspendedForUser(pkg, userId);
Andrei Stingaceanu0122f6512016-01-22 15:33:03 +00003488 } catch (RemoteException re) {
3489 throw new SecurityException("Could not talk to package manager service");
Andrei Stingaceanuefc4a342016-03-22 14:43:01 +00003490 } catch (IllegalArgumentException ex) {
3491 // Package not found.
3492 return false;
Andrei Stingaceanu0122f6512016-01-22 15:33:03 +00003493 }
Andrei Stingaceanu0122f6512016-01-22 15:33:03 +00003494 }
3495
Chris Wren47633422016-01-22 09:56:59 -05003496 private class TrimCache {
3497 StatusBarNotification heavy;
3498 StatusBarNotification sbnClone;
3499 StatusBarNotification sbnCloneLight;
3500
3501 TrimCache(StatusBarNotification sbn) {
3502 heavy = sbn;
3503 }
3504
3505 StatusBarNotification ForListener(ManagedServiceInfo info) {
3506 if (mListeners.getOnNotificationPostedTrim(info) == TRIM_LIGHT) {
3507 if (sbnCloneLight == null) {
3508 sbnCloneLight = heavy.cloneLight();
3509 }
3510 return sbnCloneLight;
3511 } else {
3512 if (sbnClone == null) {
3513 sbnClone = heavy.clone();
3514 }
3515 return sbnClone;
3516 }
3517 }
3518 }
3519
Chris Wren0efdb882016-03-01 17:17:47 -05003520 public class NotificationRankers extends ManagedServices {
Chris Wren51017d02015-12-15 15:34:46 -05003521
Chris Wren0efdb882016-03-01 17:17:47 -05003522 public NotificationRankers() {
Chris Wren51017d02015-12-15 15:34:46 -05003523 super(getContext(), mHandler, mNotificationList, mUserProfiles);
3524 }
3525
3526 @Override
3527 protected Config getConfig() {
3528 Config c = new Config();
Chris Wrene0ba7eb2016-03-04 17:30:43 -05003529 c.caption = "notification ranker service";
3530 c.serviceInterface = NotificationRankerService.SERVICE_INTERFACE;
3531 c.secureSettingName = null;
3532 c.bindPermission = Manifest.permission.BIND_NOTIFICATION_RANKER_SERVICE;
Chris Wren51017d02015-12-15 15:34:46 -05003533 c.settingsAction = Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS;
Chris Wrene0ba7eb2016-03-04 17:30:43 -05003534 c.clientLabel = R.string.notification_ranker_binding_label;
Chris Wren51017d02015-12-15 15:34:46 -05003535 return c;
3536 }
3537
3538 @Override
3539 protected IInterface asInterface(IBinder binder) {
3540 return INotificationListener.Stub.asInterface(binder);
3541 }
3542
3543 @Override
3544 protected boolean checkType(IInterface service) {
3545 return service instanceof INotificationListener;
3546 }
3547
3548 @Override
3549 protected void onServiceAdded(ManagedServiceInfo info) {
3550 mListeners.registerGuestService(info);
3551 }
3552
3553 @Override
3554 protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
3555 mListeners.unregisterService(removed.service, removed.userid);
3556 }
Chris Wren47633422016-01-22 09:56:59 -05003557
3558 public void onNotificationEnqueued(final NotificationRecord r) {
3559 final StatusBarNotification sbn = r.sbn;
3560 TrimCache trimCache = new TrimCache(sbn);
3561
Chris Wrene0ba7eb2016-03-04 17:30:43 -05003562 // mServices is the list inside ManagedServices of all the rankers,
Chris Wren47633422016-01-22 09:56:59 -05003563 // There should be only one, but it's a list, so while we enforce
3564 // singularity elsewhere, we keep it general here, to avoid surprises.
Chris Wren0efdb882016-03-01 17:17:47 -05003565 for (final ManagedServiceInfo info : NotificationRankers.this.mServices) {
Chris Wren47633422016-01-22 09:56:59 -05003566 boolean sbnVisible = isVisibleToListener(sbn, info);
3567 if (!sbnVisible) {
3568 continue;
3569 }
3570
3571 final int importance = r.getImportance();
3572 final boolean fromUser = r.isImportanceFromUser();
3573 final StatusBarNotification sbnToPost = trimCache.ForListener(info);
Chris Wrene0ba7eb2016-03-04 17:30:43 -05003574 mHandler.post(new Runnable() {
Chris Wren47633422016-01-22 09:56:59 -05003575 @Override
3576 public void run() {
3577 notifyEnqueued(info, sbnToPost, importance, fromUser);
3578 }
3579 });
3580 }
3581 }
3582
3583 private void notifyEnqueued(final ManagedServiceInfo info,
3584 final StatusBarNotification sbn, int importance, boolean fromUser) {
Chris Wrene0ba7eb2016-03-04 17:30:43 -05003585 final INotificationListener ranker = (INotificationListener) info.service;
Chris Wren47633422016-01-22 09:56:59 -05003586 StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
3587 try {
Chris Wrene0ba7eb2016-03-04 17:30:43 -05003588 ranker.onNotificationEnqueued(sbnHolder, importance, fromUser);
Chris Wren47633422016-01-22 09:56:59 -05003589 } catch (RemoteException ex) {
Chris Wrene0ba7eb2016-03-04 17:30:43 -05003590 Log.e(TAG, "unable to notify ranker (enqueued): " + ranker, ex);
Chris Wren47633422016-01-22 09:56:59 -05003591 }
3592 }
3593
3594 public boolean isEnabled() {
3595 return !mServices.isEmpty();
3596 }
Julia Reynolds1c9bd422016-03-15 09:25:56 -04003597
3598 @Override
3599 public void onUserSwitched(int user) {
3600 for (ManagedServiceInfo info : mServices) {
3601 unregisterService(info.service, info.userid);
3602 }
3603 registerRanker();
3604 }
3605
3606 @Override
3607 public void onPackagesChanged(boolean queryReplace, String[] pkgList) {
3608 if (DEBUG) Slog.d(TAG, "onPackagesChanged queryReplace=" + queryReplace
3609 + " pkgList=" + (pkgList == null ? null : Arrays.asList(pkgList)));
Chris Wren88d2f6d2016-03-17 15:47:09 -04003610 if (mRankerServicePackageName == null) {
3611 return;
3612 }
Julia Reynolds1c9bd422016-03-15 09:25:56 -04003613
3614 if (pkgList != null && (pkgList.length > 0)) {
3615 for (String pkgName : pkgList) {
3616 if (mRankerServicePackageName.equals(pkgName)) {
3617 registerRanker();
3618 }
3619 }
3620 }
3621 }
3622
3623 protected void registerRanker() {
3624 // Find the updatable ranker and register it.
Chris Wren88d2f6d2016-03-17 15:47:09 -04003625 if (mRankerServicePackageName == null) {
3626 Slog.w(TAG, "could not start ranker service: no package specified!");
3627 return;
3628 }
Julia Reynolds1c9bd422016-03-15 09:25:56 -04003629 Set<ComponentName> rankerComponents = queryPackageForServices(
3630 mRankerServicePackageName, UserHandle.USER_SYSTEM, null);
3631 Iterator<ComponentName> iterator = rankerComponents.iterator();
3632 if (iterator.hasNext()) {
3633 ComponentName rankerComponent = iterator.next();
3634 if (iterator.hasNext()) {
3635 Slog.e(TAG, "found multiple ranker services:" + rankerComponents);
3636 } else {
3637 registerSystemService(rankerComponent, UserHandle.USER_SYSTEM);
3638 }
3639 } else {
3640 Slog.w(TAG, "could not start ranker service: none found");
3641 }
3642 }
Chris Wren51017d02015-12-15 15:34:46 -05003643 }
3644
John Spurlock7340fc82014-04-24 18:50:12 -04003645 public class NotificationListeners extends ManagedServices {
3646
Christoph Studerb82bc782014-08-20 14:29:43 +02003647 private final ArraySet<ManagedServiceInfo> mLightTrimListeners = new ArraySet<>();
Christoph Studer265c1052014-07-23 17:14:33 +02003648 private boolean mNotificationGroupsDesired;
Christoph Studerb82bc782014-08-20 14:29:43 +02003649
John Spurlock7340fc82014-04-24 18:50:12 -04003650 public NotificationListeners() {
3651 super(getContext(), mHandler, mNotificationList, mUserProfiles);
3652 }
3653
3654 @Override
3655 protected Config getConfig() {
3656 Config c = new Config();
3657 c.caption = "notification listener";
3658 c.serviceInterface = NotificationListenerService.SERVICE_INTERFACE;
3659 c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_LISTENERS;
3660 c.bindPermission = android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE;
3661 c.settingsAction = Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS;
3662 c.clientLabel = R.string.notification_listener_binding_label;
3663 return c;
3664 }
3665
3666 @Override
3667 protected IInterface asInterface(IBinder binder) {
3668 return INotificationListener.Stub.asInterface(binder);
3669 }
3670
3671 @Override
Chris Wren51017d02015-12-15 15:34:46 -05003672 protected boolean checkType(IInterface service) {
3673 return service instanceof INotificationListener;
3674 }
3675
3676 @Override
John Spurlock3b98b3f2014-05-01 09:08:48 -04003677 public void onServiceAdded(ManagedServiceInfo info) {
3678 final INotificationListener listener = (INotificationListener) info.service;
Chris Wren333a61c2014-05-28 16:40:57 -04003679 final NotificationRankingUpdate update;
Christoph Studer05ad4822014-05-16 14:16:03 +02003680 synchronized (mNotificationList) {
Christoph Studer265c1052014-07-23 17:14:33 +02003681 updateNotificationGroupsDesiredLocked();
Chris Wren333a61c2014-05-28 16:40:57 -04003682 update = makeRankingUpdateLocked(info);
Christoph Studer05ad4822014-05-16 14:16:03 +02003683 }
John Spurlock7340fc82014-04-24 18:50:12 -04003684 try {
Chris Wren333a61c2014-05-28 16:40:57 -04003685 listener.onListenerConnected(update);
John Spurlock7340fc82014-04-24 18:50:12 -04003686 } catch (RemoteException e) {
3687 // we tried
3688 }
3689 }
3690
John Spurlock1fa865f2014-07-21 14:56:39 -04003691 @Override
3692 protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
John Spurlockd8afe3c2014-08-01 14:04:07 -04003693 if (mListenersDisablingEffects.remove(removed)) {
3694 updateListenerHintsLocked();
Christoph Studer0d6ef4b2014-12-02 15:00:48 +01003695 updateEffectsSuppressorLocked();
John Spurlock1fa865f2014-07-21 14:56:39 -04003696 }
Christoph Studerb82bc782014-08-20 14:29:43 +02003697 mLightTrimListeners.remove(removed);
Christoph Studer265c1052014-07-23 17:14:33 +02003698 updateNotificationGroupsDesiredLocked();
Christoph Studerb82bc782014-08-20 14:29:43 +02003699 }
3700
3701 public void setOnNotificationPostedTrimLocked(ManagedServiceInfo info, int trim) {
3702 if (trim == TRIM_LIGHT) {
3703 mLightTrimListeners.add(info);
3704 } else {
3705 mLightTrimListeners.remove(info);
3706 }
3707 }
3708
3709 public int getOnNotificationPostedTrim(ManagedServiceInfo info) {
3710 return mLightTrimListeners.contains(info) ? TRIM_LIGHT : TRIM_FULL;
John Spurlock1fa865f2014-07-21 14:56:39 -04003711 }
3712
John Spurlock7340fc82014-04-24 18:50:12 -04003713 /**
3714 * asynchronously notify all listeners about a new notification
Christoph Studercef37cf2014-07-25 14:18:17 +02003715 *
3716 * <p>
3717 * Also takes care of removing a notification that has been visible to a listener before,
3718 * but isn't anymore.
John Spurlock7340fc82014-04-24 18:50:12 -04003719 */
Christoph Studercef37cf2014-07-25 14:18:17 +02003720 public void notifyPostedLocked(StatusBarNotification sbn, StatusBarNotification oldSbn) {
Christoph Studerb82bc782014-08-20 14:29:43 +02003721 // Lazily initialized snapshots of the notification.
Chris Wren47633422016-01-22 09:56:59 -05003722 TrimCache trimCache = new TrimCache(sbn);
Christoph Studerb82bc782014-08-20 14:29:43 +02003723
John Spurlock7340fc82014-04-24 18:50:12 -04003724 for (final ManagedServiceInfo info : mServices) {
Christoph Studercef37cf2014-07-25 14:18:17 +02003725 boolean sbnVisible = isVisibleToListener(sbn, info);
3726 boolean oldSbnVisible = oldSbn != null ? isVisibleToListener(oldSbn, info) : false;
3727 // This notification hasn't been and still isn't visible -> ignore.
3728 if (!oldSbnVisible && !sbnVisible) {
Christoph Studer05ad4822014-05-16 14:16:03 +02003729 continue;
Chris Wrenf9536642014-04-17 10:01:54 -04003730 }
Chris Wren333a61c2014-05-28 16:40:57 -04003731 final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
Christoph Studercef37cf2014-07-25 14:18:17 +02003732
3733 // This notification became invisible -> remove the old one.
3734 if (oldSbnVisible && !sbnVisible) {
3735 final StatusBarNotification oldSbnLightClone = oldSbn.cloneLight();
3736 mHandler.post(new Runnable() {
3737 @Override
3738 public void run() {
3739 notifyRemoved(info, oldSbnLightClone, update);
3740 }
3741 });
Christoph Studer05ad4822014-05-16 14:16:03 +02003742 continue;
3743 }
Christoph Studercef37cf2014-07-25 14:18:17 +02003744
Chris Wren47633422016-01-22 09:56:59 -05003745 final StatusBarNotification sbnToPost = trimCache.ForListener(info);
Christoph Studer05ad4822014-05-16 14:16:03 +02003746 mHandler.post(new Runnable() {
3747 @Override
3748 public void run() {
Christoph Studerb82bc782014-08-20 14:29:43 +02003749 notifyPosted(info, sbnToPost, update);
Christoph Studer05ad4822014-05-16 14:16:03 +02003750 }
3751 });
Kenny Guy3a7c4a52014-03-03 18:24:03 +00003752 }
3753 }
Kenny Guy3a7c4a52014-03-03 18:24:03 +00003754
John Spurlock7340fc82014-04-24 18:50:12 -04003755 /**
3756 * asynchronously notify all listeners about a removed notification
3757 */
Chris Wren333a61c2014-05-28 16:40:57 -04003758 public void notifyRemovedLocked(StatusBarNotification sbn) {
John Spurlock7340fc82014-04-24 18:50:12 -04003759 // make a copy in case changes are made to the underlying Notification object
3760 // NOTE: this copy is lightweight: it doesn't include heavyweight parts of the
3761 // notification
3762 final StatusBarNotification sbnLight = sbn.cloneLight();
Chris Wrenf9536642014-04-17 10:01:54 -04003763 for (final ManagedServiceInfo info : mServices) {
Christoph Studercef37cf2014-07-25 14:18:17 +02003764 if (!isVisibleToListener(sbn, info)) {
Christoph Studer05ad4822014-05-16 14:16:03 +02003765 continue;
Chris Wrenf9536642014-04-17 10:01:54 -04003766 }
Chris Wren333a61c2014-05-28 16:40:57 -04003767 final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
Christoph Studer05ad4822014-05-16 14:16:03 +02003768 mHandler.post(new Runnable() {
3769 @Override
3770 public void run() {
Christoph Studercef37cf2014-07-25 14:18:17 +02003771 notifyRemoved(info, sbnLight, update);
Christoph Studer05ad4822014-05-16 14:16:03 +02003772 }
3773 });
Chris Wrenf9536642014-04-17 10:01:54 -04003774 }
3775 }
3776
3777 /**
3778 * asynchronously notify all listeners about a reordering of notifications
Chris Wrenf9536642014-04-17 10:01:54 -04003779 */
Chris Wren333a61c2014-05-28 16:40:57 -04003780 public void notifyRankingUpdateLocked() {
Chris Wrenf9536642014-04-17 10:01:54 -04003781 for (final ManagedServiceInfo serviceInfo : mServices) {
Christoph Studer05ad4822014-05-16 14:16:03 +02003782 if (!serviceInfo.isEnabledForCurrentProfiles()) {
3783 continue;
3784 }
Christoph Studercef37cf2014-07-25 14:18:17 +02003785 final NotificationRankingUpdate update = makeRankingUpdateLocked(serviceInfo);
John Spurlock7340fc82014-04-24 18:50:12 -04003786 mHandler.post(new Runnable() {
3787 @Override
3788 public void run() {
Chris Wren333a61c2014-05-28 16:40:57 -04003789 notifyRankingUpdate(serviceInfo, update);
John Spurlock7340fc82014-04-24 18:50:12 -04003790 }
3791 });
Kenny Guya263e4e2014-03-03 18:24:03 +00003792 }
Kenny Guya263e4e2014-03-03 18:24:03 +00003793 }
Kenny Guya263e4e2014-03-03 18:24:03 +00003794
John Spurlockd8afe3c2014-08-01 14:04:07 -04003795 public void notifyListenerHintsChangedLocked(final int hints) {
John Spurlock1fa865f2014-07-21 14:56:39 -04003796 for (final ManagedServiceInfo serviceInfo : mServices) {
3797 if (!serviceInfo.isEnabledForCurrentProfiles()) {
3798 continue;
3799 }
3800 mHandler.post(new Runnable() {
3801 @Override
3802 public void run() {
John Spurlockd8afe3c2014-08-01 14:04:07 -04003803 notifyListenerHintsChanged(serviceInfo, hints);
John Spurlock1fa865f2014-07-21 14:56:39 -04003804 }
3805 });
3806 }
3807 }
3808
Christoph Studer85a384b2014-08-27 20:16:15 +02003809 public void notifyInterruptionFilterChanged(final int interruptionFilter) {
3810 for (final ManagedServiceInfo serviceInfo : mServices) {
3811 if (!serviceInfo.isEnabledForCurrentProfiles()) {
3812 continue;
3813 }
3814 mHandler.post(new Runnable() {
3815 @Override
3816 public void run() {
3817 notifyInterruptionFilterChanged(serviceInfo, interruptionFilter);
3818 }
3819 });
3820 }
3821 }
3822
Christoph Studercef37cf2014-07-25 14:18:17 +02003823 private void notifyPosted(final ManagedServiceInfo info,
Christoph Studer05ad4822014-05-16 14:16:03 +02003824 final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) {
John Spurlock7340fc82014-04-24 18:50:12 -04003825 final INotificationListener listener = (INotificationListener)info.service;
Griff Hazen84a00ea2014-09-02 17:10:47 -07003826 StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
John Spurlock7340fc82014-04-24 18:50:12 -04003827 try {
Griff Hazen84a00ea2014-09-02 17:10:47 -07003828 listener.onNotificationPosted(sbnHolder, rankingUpdate);
John Spurlock7340fc82014-04-24 18:50:12 -04003829 } catch (RemoteException ex) {
3830 Log.e(TAG, "unable to notify listener (posted): " + listener, ex);
3831 }
3832 }
3833
Christoph Studercef37cf2014-07-25 14:18:17 +02003834 private void notifyRemoved(ManagedServiceInfo info, StatusBarNotification sbn,
Christoph Studer05ad4822014-05-16 14:16:03 +02003835 NotificationRankingUpdate rankingUpdate) {
John Spurlock7340fc82014-04-24 18:50:12 -04003836 if (!info.enabledAndUserMatches(sbn.getUserId())) {
3837 return;
3838 }
Christoph Studer05ad4822014-05-16 14:16:03 +02003839 final INotificationListener listener = (INotificationListener) info.service;
Griff Hazen84a00ea2014-09-02 17:10:47 -07003840 StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
John Spurlock7340fc82014-04-24 18:50:12 -04003841 try {
Griff Hazen84a00ea2014-09-02 17:10:47 -07003842 listener.onNotificationRemoved(sbnHolder, rankingUpdate);
John Spurlock7340fc82014-04-24 18:50:12 -04003843 } catch (RemoteException ex) {
3844 Log.e(TAG, "unable to notify listener (removed): " + listener, ex);
John Spurlockb408e8e2014-04-23 21:12:45 -04003845 }
Kenny Guya263e4e2014-03-03 18:24:03 +00003846 }
Chris Wrenf9536642014-04-17 10:01:54 -04003847
Christoph Studer05ad4822014-05-16 14:16:03 +02003848 private void notifyRankingUpdate(ManagedServiceInfo info,
3849 NotificationRankingUpdate rankingUpdate) {
3850 final INotificationListener listener = (INotificationListener) info.service;
Chris Wrenf9536642014-04-17 10:01:54 -04003851 try {
Christoph Studer05ad4822014-05-16 14:16:03 +02003852 listener.onNotificationRankingUpdate(rankingUpdate);
Chris Wrenf9536642014-04-17 10:01:54 -04003853 } catch (RemoteException ex) {
3854 Log.e(TAG, "unable to notify listener (ranking update): " + listener, ex);
3855 }
3856 }
John Spurlock1fa865f2014-07-21 14:56:39 -04003857
John Spurlockd8afe3c2014-08-01 14:04:07 -04003858 private void notifyListenerHintsChanged(ManagedServiceInfo info, int hints) {
John Spurlock1fa865f2014-07-21 14:56:39 -04003859 final INotificationListener listener = (INotificationListener) info.service;
3860 try {
John Spurlockd8afe3c2014-08-01 14:04:07 -04003861 listener.onListenerHintsChanged(hints);
John Spurlock1fa865f2014-07-21 14:56:39 -04003862 } catch (RemoteException ex) {
John Spurlockd8afe3c2014-08-01 14:04:07 -04003863 Log.e(TAG, "unable to notify listener (listener hints): " + listener, ex);
John Spurlock1fa865f2014-07-21 14:56:39 -04003864 }
3865 }
Justin Koh38156c52014-06-04 13:57:49 -07003866
Christoph Studer85a384b2014-08-27 20:16:15 +02003867 private void notifyInterruptionFilterChanged(ManagedServiceInfo info,
3868 int interruptionFilter) {
3869 final INotificationListener listener = (INotificationListener) info.service;
3870 try {
3871 listener.onInterruptionFilterChanged(interruptionFilter);
3872 } catch (RemoteException ex) {
3873 Log.e(TAG, "unable to notify listener (interruption filter): " + listener, ex);
3874 }
3875 }
3876
Justin Koh38156c52014-06-04 13:57:49 -07003877 private boolean isListenerPackage(String packageName) {
3878 if (packageName == null) {
3879 return false;
3880 }
3881 // TODO: clean up locking object later
3882 synchronized (mNotificationList) {
3883 for (final ManagedServiceInfo serviceInfo : mServices) {
3884 if (packageName.equals(serviceInfo.component.getPackageName())) {
3885 return true;
3886 }
3887 }
3888 }
3889 return false;
3890 }
Christoph Studer265c1052014-07-23 17:14:33 +02003891
3892 /**
3893 * Returns whether any of the currently registered listeners wants to receive notification
3894 * groups.
3895 *
3896 * <p>Currently we assume groups are desired by non-SystemUI listeners.</p>
3897 */
3898 public boolean notificationGroupsDesired() {
3899 return mNotificationGroupsDesired;
3900 }
3901
3902 private void updateNotificationGroupsDesiredLocked() {
3903 mNotificationGroupsDesired = true;
3904 // No listeners, no groups.
3905 if (mServices.isEmpty()) {
3906 mNotificationGroupsDesired = false;
3907 return;
3908 }
3909 // One listener: Check whether it's SysUI.
3910 if (mServices.size() == 1 &&
3911 mServices.get(0).component.getPackageName().equals("com.android.systemui")) {
3912 mNotificationGroupsDesired = false;
3913 return;
3914 }
3915 }
Kenny Guya263e4e2014-03-03 18:24:03 +00003916 }
John Spurlock25e2d242014-06-27 13:58:23 -04003917
3918 public static final class DumpFilter {
Dan Sandlera1770312015-07-10 13:59:29 -04003919 public boolean filtered = false;
John Spurlock25e2d242014-06-27 13:58:23 -04003920 public String pkgFilter;
John Spurlock50806fc2014-07-15 10:22:02 -04003921 public boolean zen;
Chris Wrene4b38802015-07-07 15:54:19 -04003922 public long since;
3923 public boolean stats;
Dan Sandlera1770312015-07-10 13:59:29 -04003924 public boolean redact = true;
John Spurlock25e2d242014-06-27 13:58:23 -04003925
3926 public static DumpFilter parseFromArguments(String[] args) {
Dan Sandlera1770312015-07-10 13:59:29 -04003927 final DumpFilter filter = new DumpFilter();
3928 for (int ai = 0; ai < args.length; ai++) {
3929 final String a = args[ai];
3930 if ("--noredact".equals(a) || "--reveal".equals(a)) {
3931 filter.redact = false;
3932 } else if ("p".equals(a) || "pkg".equals(a) || "--package".equals(a)) {
3933 if (ai < args.length-1) {
3934 ai++;
3935 filter.pkgFilter = args[ai].trim().toLowerCase();
3936 if (filter.pkgFilter.isEmpty()) {
3937 filter.pkgFilter = null;
3938 } else {
3939 filter.filtered = true;
3940 }
3941 }
3942 } else if ("--zen".equals(a) || "zen".equals(a)) {
3943 filter.filtered = true;
3944 filter.zen = true;
3945 } else if ("--stats".equals(a)) {
3946 filter.stats = true;
3947 if (ai < args.length-1) {
3948 ai++;
3949 filter.since = Long.valueOf(args[ai]);
3950 } else {
3951 filter.since = 0;
3952 }
3953 }
John Spurlock25e2d242014-06-27 13:58:23 -04003954 }
Dan Sandlera1770312015-07-10 13:59:29 -04003955 return filter;
John Spurlock25e2d242014-06-27 13:58:23 -04003956 }
3957
3958 public boolean matches(StatusBarNotification sbn) {
Dan Sandlera1770312015-07-10 13:59:29 -04003959 if (!filtered) return true;
3960 return zen ? true : sbn != null
John Spurlock50806fc2014-07-15 10:22:02 -04003961 && (matches(sbn.getPackageName()) || matches(sbn.getOpPkg()));
John Spurlock25e2d242014-06-27 13:58:23 -04003962 }
3963
3964 public boolean matches(ComponentName component) {
Dan Sandlera1770312015-07-10 13:59:29 -04003965 if (!filtered) return true;
3966 return zen ? true : component != null && matches(component.getPackageName());
John Spurlock25e2d242014-06-27 13:58:23 -04003967 }
3968
3969 public boolean matches(String pkg) {
Dan Sandlera1770312015-07-10 13:59:29 -04003970 if (!filtered) return true;
3971 return zen ? true : pkg != null && pkg.toLowerCase().contains(pkgFilter);
John Spurlock50806fc2014-07-15 10:22:02 -04003972 }
3973
3974 @Override
3975 public String toString() {
Chris Wrene4b38802015-07-07 15:54:19 -04003976 return stats ? "stats" : zen ? "zen" : ('\'' + pkgFilter + '\'');
John Spurlock25e2d242014-06-27 13:58:23 -04003977 }
3978 }
Griff Hazen84a00ea2014-09-02 17:10:47 -07003979
3980 /**
3981 * Wrapper for a StatusBarNotification object that allows transfer across a oneway
3982 * binder without sending large amounts of data over a oneway transaction.
3983 */
3984 private static final class StatusBarNotificationHolder
3985 extends IStatusBarNotificationHolder.Stub {
Griff Hazene9aac5f2014-09-05 20:04:09 -07003986 private StatusBarNotification mValue;
Griff Hazen84a00ea2014-09-02 17:10:47 -07003987
3988 public StatusBarNotificationHolder(StatusBarNotification value) {
3989 mValue = value;
3990 }
3991
Griff Hazene9aac5f2014-09-05 20:04:09 -07003992 /** Get the held value and clear it. This function should only be called once per holder */
Griff Hazen84a00ea2014-09-02 17:10:47 -07003993 @Override
3994 public StatusBarNotification get() {
Griff Hazene9aac5f2014-09-05 20:04:09 -07003995 StatusBarNotification value = mValue;
3996 mValue = null;
3997 return value;
Griff Hazen84a00ea2014-09-02 17:10:47 -07003998 }
3999 }
John Spurlock7c74f782015-06-04 13:01:42 -04004000
4001 private final class PolicyAccess {
4002 private static final String SEPARATOR = ":";
4003 private final String[] PERM = {
4004 android.Manifest.permission.ACCESS_NOTIFICATION_POLICY
4005 };
4006
4007 public boolean isPackageGranted(String pkg) {
4008 return pkg != null && getGrantedPackages().contains(pkg);
4009 }
4010
4011 public void put(String pkg, boolean granted) {
4012 if (pkg == null) return;
4013 final ArraySet<String> pkgs = getGrantedPackages();
4014 boolean changed;
4015 if (granted) {
4016 changed = pkgs.add(pkg);
4017 } else {
4018 changed = pkgs.remove(pkg);
4019 }
4020 if (!changed) return;
4021 final String setting = TextUtils.join(SEPARATOR, pkgs);
4022 final int currentUser = ActivityManager.getCurrentUser();
4023 Settings.Secure.putStringForUser(getContext().getContentResolver(),
4024 Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES,
4025 setting,
4026 currentUser);
4027 getContext().sendBroadcastAsUser(new Intent(NotificationManager
4028 .ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED)
4029 .setPackage(pkg)
4030 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), new UserHandle(currentUser), null);
4031 }
4032
4033 public ArraySet<String> getGrantedPackages() {
4034 final ArraySet<String> pkgs = new ArraySet<>();
Julia Reynoldsea6c4482015-08-13 09:01:33 -04004035
4036 long identity = Binder.clearCallingIdentity();
4037 try {
4038 final String setting = Settings.Secure.getStringForUser(
4039 getContext().getContentResolver(),
4040 Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES,
4041 ActivityManager.getCurrentUser());
4042 if (setting != null) {
4043 final String[] tokens = setting.split(SEPARATOR);
4044 for (int i = 0; i < tokens.length; i++) {
4045 String token = tokens[i];
4046 if (token != null) {
Andreas Gampe1ed71f32015-12-11 15:49:07 -08004047 token = token.trim();
Julia Reynoldsea6c4482015-08-13 09:01:33 -04004048 }
4049 if (TextUtils.isEmpty(token)) {
4050 continue;
4051 }
4052 pkgs.add(token);
John Spurlock7c74f782015-06-04 13:01:42 -04004053 }
John Spurlock7c74f782015-06-04 13:01:42 -04004054 }
Julia Reynoldsea6c4482015-08-13 09:01:33 -04004055 } finally {
4056 Binder.restoreCallingIdentity(identity);
John Spurlock7c74f782015-06-04 13:01:42 -04004057 }
4058 return pkgs;
4059 }
4060
4061 public String[] getRequestingPackages() throws RemoteException {
4062 final ParceledListSlice list = AppGlobals.getPackageManager()
4063 .getPackagesHoldingPermissions(PERM, 0 /*flags*/,
4064 ActivityManager.getCurrentUser());
4065 final List<PackageInfo> pkgs = list.getList();
4066 if (pkgs == null || pkgs.isEmpty()) return new String[0];
4067 final int N = pkgs.size();
4068 final String[] rt = new String[N];
4069 for (int i = 0; i < N; i++) {
4070 rt[i] = pkgs.get(i).packageName;
4071 }
4072 return rt;
4073 }
4074 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004075}