blob: c83012c0d2d6a700dd6133cf7948b362aa99e250 [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
John Spurlockd8afe3c2014-08-01 14:04:07 -040019import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_EFFECTS;
Chris Wren9fa689f2015-11-20 16:44:53 -050020import static android.service.notification.NotificationAssistantService.REASON_DELEGATE_CLICK;
21import static android.service.notification.NotificationAssistantService.REASON_DELEGATE_CANCEL;
22import static android.service.notification.NotificationAssistantService.REASON_DELEGATE_CANCEL_ALL;
23import static android.service.notification.NotificationAssistantService.REASON_DELEGATE_ERROR;
24import static android.service.notification.NotificationAssistantService.REASON_PACKAGE_CHANGED;
25import static android.service.notification.NotificationAssistantService.REASON_USER_STOPPED;
26import static android.service.notification.NotificationAssistantService.REASON_PACKAGE_BANNED;
27import static android.service.notification.NotificationAssistantService.REASON_APP_CANCEL;
28import static android.service.notification.NotificationAssistantService.REASON_APP_CANCEL_ALL;
29import static android.service.notification.NotificationAssistantService.REASON_LISTENER_CANCEL;
30import static android.service.notification.NotificationAssistantService.REASON_LISTENER_CANCEL_ALL;
31import static android.service.notification.NotificationAssistantService.REASON_GROUP_SUMMARY_CANCELED;
32import static android.service.notification.NotificationAssistantService.REASON_GROUP_OPTIMIZATION;
Julia Reynoldsf612869ae2015-11-05 16:48:55 -050033import static android.service.notification.NotificationListenerService.SUPPRESSED_EFFECT_LIGHTS;
34import static android.service.notification.NotificationListenerService.SUPPRESSED_EFFECT_PEEK;
Christoph Studerb82bc782014-08-20 14:29:43 +020035import static android.service.notification.NotificationListenerService.TRIM_FULL;
36import static android.service.notification.NotificationListenerService.TRIM_LIGHT;
Jeff Sharkey098d5802012-04-26 17:30:34 -070037import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
38import static org.xmlpull.v1.XmlPullParser.END_TAG;
39import static org.xmlpull.v1.XmlPullParser.START_TAG;
svetoslavganov75986cf2009-05-14 22:28:01 -070040
Dianne Hackborn41203752012-08-31 14:05:51 -070041import android.app.ActivityManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080042import android.app.ActivityManagerNative;
John Spurlock7340fc82014-04-24 18:50:12 -040043import android.app.AppGlobals;
Daniel Sandler4a900ac2013-01-30 14:04:10 -050044import android.app.AppOpsManager;
Julia Reynoldsa47a27f2015-08-24 08:31:47 -040045import android.app.AutomaticZenRule;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080046import android.app.IActivityManager;
47import android.app.INotificationManager;
48import android.app.ITransientNotification;
49import android.app.Notification;
John Spurlockb4782522014-08-22 14:54:46 -040050import android.app.NotificationManager;
John Spurlock1fc476d2015-04-14 16:05:20 -040051import android.app.NotificationManager.Policy;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080052import android.app.PendingIntent;
53import android.app.StatusBarManager;
John Spurlock35ef0a62015-05-28 11:24:10 -040054import android.app.backup.BackupManager;
Amith Yamasanif47e51e2015-04-17 10:02:15 -070055import android.app.usage.UsageEvents;
Amith Yamasanif47e51e2015-04-17 10:02:15 -070056import android.app.usage.UsageStatsManagerInternal;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080057import android.content.BroadcastReceiver;
Daniel Sandler5feceeb2013-03-22 18:29:23 -070058import android.content.ComponentName;
Dianne Hackborn1dac2772009-06-26 18:16:48 -070059import android.content.ContentResolver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080060import android.content.Context;
61import android.content.Intent;
62import android.content.IntentFilter;
John Spurlock7340fc82014-04-24 18:50:12 -040063import android.content.pm.ApplicationInfo;
Kenny Guy70058402014-10-28 20:45:06 +000064import android.content.pm.IPackageManager;
Daniel Sandler4a900ac2013-01-30 14:04:10 -050065import android.content.pm.PackageInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080066import android.content.pm.PackageManager;
67import android.content.pm.PackageManager.NameNotFoundException;
Christoph Studercee44ba2014-05-20 18:36:43 +020068import android.content.pm.ParceledListSlice;
Chris Wren66189fc2015-06-25 14:04:33 -040069import android.content.pm.UserInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080070import android.content.res.Resources;
Dianne Hackborn1dac2772009-06-26 18:16:48 -070071import android.database.ContentObserver;
John Spurlock7b414672014-07-18 13:02:39 -040072import android.media.AudioAttributes;
svetoslavganov75986cf2009-05-14 22:28:01 -070073import android.media.AudioManager;
John Spurlockcdb57ae2015-02-11 19:04:11 -050074import android.media.AudioManagerInternal;
Jean-Michel Triviceb79bc2014-09-05 11:09:14 -070075import android.media.AudioSystem;
Jeff Sharkey098d5802012-04-26 17:30:34 -070076import android.media.IRingtonePlayer;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080077import android.net.Uri;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080078import android.os.Binder;
Selim Cinekb5605e52015-02-20 18:21:41 +010079import android.os.Build;
John Spurlock2b122f42014-08-27 16:29:47 -040080import android.os.Bundle;
John Spurlock056c5192014-04-20 21:52:01 -040081import android.os.Environment;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080082import android.os.Handler;
Chris Wrenf9536642014-04-17 10:01:54 -040083import android.os.HandlerThread;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080084import android.os.IBinder;
John Spurlock7340fc82014-04-24 18:50:12 -040085import android.os.IInterface;
Chris Wrenf9536642014-04-17 10:01:54 -040086import android.os.Looper;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080087import android.os.Message;
Dianne Hackbornd8a43f62009-08-17 23:33:56 -070088import android.os.Process;
svetoslavganov75986cf2009-05-14 22:28:01 -070089import android.os.RemoteException;
Selim Cinekb5605e52015-02-20 18:21:41 +010090import android.os.SystemProperties;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -070091import android.os.UserHandle;
Chris Wren66189fc2015-06-25 14:04:33 -040092import android.os.UserManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080093import android.os.Vibrator;
94import android.provider.Settings;
Chris Wren333a61c2014-05-28 16:40:57 -040095import android.service.notification.Condition;
John Spurlock7340fc82014-04-24 18:50:12 -040096import android.service.notification.IConditionProvider;
Chris Wren333a61c2014-05-28 16:40:57 -040097import android.service.notification.INotificationListener;
Griff Hazen84a00ea2014-09-02 17:10:47 -070098import android.service.notification.IStatusBarNotificationHolder;
John Spurlock7340fc82014-04-24 18:50:12 -040099import android.service.notification.NotificationListenerService;
Christoph Studer05ad4822014-05-16 14:16:03 +0200100import android.service.notification.NotificationRankingUpdate;
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700101import android.service.notification.StatusBarNotification;
John Spurlock056c5192014-04-20 21:52:01 -0400102import android.service.notification.ZenModeConfig;
John Spurlock32fe4c62014-10-02 12:16:02 -0400103import android.telephony.PhoneStateListener;
Daniel Sandlere96ffb12010-03-11 13:38:06 -0500104import android.telephony.TelephonyManager;
svetoslavganov75986cf2009-05-14 22:28:01 -0700105import android.text.TextUtils;
John Spurlocka4294292014-03-24 18:02:32 -0400106import android.util.ArrayMap;
John Spurlock1fa865f2014-07-21 14:56:39 -0400107import android.util.ArraySet;
Dianne Hackborn39606a02012-07-31 17:54:35 -0700108import android.util.AtomicFile;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800109import android.util.Log;
Andy Stadler110988c2010-12-03 14:29:16 -0800110import android.util.Slog;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400111import android.util.Xml;
svetoslavganov75986cf2009-05-14 22:28:01 -0700112import android.view.accessibility.AccessibilityEvent;
113import android.view.accessibility.AccessibilityManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800114import android.widget.Toast;
115
Scott Greenwald9a05b312013-06-28 00:37:54 -0400116import com.android.internal.R;
Chris Wrend1dbc922015-06-19 17:51:16 -0400117import com.android.internal.statusbar.NotificationVisibility;
John Spurlock056c5192014-04-20 21:52:01 -0400118import com.android.internal.util.FastXmlSerializer;
Julia Reynoldsa47a27f2015-08-24 08:31:47 -0400119import com.android.internal.util.Preconditions;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800120import com.android.server.EventLogTags;
Amith Yamasanif47e51e2015-04-17 10:02:15 -0700121import com.android.server.LocalServices;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800122import com.android.server.SystemService;
123import com.android.server.lights.Light;
124import com.android.server.lights.LightsManager;
John Spurlock7340fc82014-04-24 18:50:12 -0400125import com.android.server.notification.ManagedServices.ManagedServiceInfo;
126import com.android.server.notification.ManagedServices.UserProfiles;
John Spurlockb408e8e2014-04-23 21:12:45 -0400127import com.android.server.statusbar.StatusBarManagerInternal;
128
129import libcore.io.IoUtils;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800130
Chris Wrene4b38802015-07-07 15:54:19 -0400131import org.json.JSONArray;
132import org.json.JSONException;
133import org.json.JSONObject;
Jeff Sharkey098d5802012-04-26 17:30:34 -0700134import org.xmlpull.v1.XmlPullParser;
135import org.xmlpull.v1.XmlPullParserException;
John Spurlock056c5192014-04-20 21:52:01 -0400136import org.xmlpull.v1.XmlSerializer;
Jeff Sharkey098d5802012-04-26 17:30:34 -0700137
John Spurlock35ef0a62015-05-28 11:24:10 -0400138import java.io.ByteArrayInputStream;
139import java.io.ByteArrayOutputStream;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400140import java.io.File;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800141import java.io.FileDescriptor;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400142import java.io.FileInputStream;
143import java.io.FileNotFoundException;
John Spurlock056c5192014-04-20 21:52:01 -0400144import java.io.FileOutputStream;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400145import java.io.IOException;
John Spurlock35ef0a62015-05-28 11:24:10 -0400146import java.io.InputStream;
147import java.io.OutputStream;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800148import java.io.PrintWriter;
Wojciech Staszkiewicz9e9e2e72015-05-08 14:58:46 +0100149import java.nio.charset.StandardCharsets;
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500150import java.util.ArrayDeque;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800151import java.util.ArrayList;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400152import java.util.HashSet;
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500153import java.util.Iterator;
John Spurlock7c74f782015-06-04 13:01:42 -0400154import java.util.List;
Christoph Studer265c1052014-07-23 17:14:33 +0200155import java.util.Map.Entry;
John Spurlockb4782522014-08-22 14:54:46 -0400156import java.util.Objects;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400157
Daniel Sandlerd0a2f862010-08-03 15:29:31 -0400158/** {@hide} */
Adam Lesinski182f73f2013-12-05 16:48:06 -0800159public class NotificationManagerService extends SystemService {
160 static final String TAG = "NotificationService";
Christoph Studer1f32c652014-11-26 15:32:20 +0100161 static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
Selim Cinek40412492015-12-08 18:03:22 -0800162 public static final boolean ENABLE_CHILD_NOTIFICATIONS
163 = SystemProperties.getBoolean("debug.child_notifs", true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800164
Adam Lesinski182f73f2013-12-05 16:48:06 -0800165 static final int MAX_PACKAGE_NOTIFICATIONS = 50;
Joe Onoratobd73d012010-06-04 11:44:54 -0700166
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800167 // message codes
Adam Lesinski182f73f2013-12-05 16:48:06 -0800168 static final int MESSAGE_TIMEOUT = 2;
John Spurlock056c5192014-04-20 21:52:01 -0400169 static final int MESSAGE_SAVE_POLICY_FILE = 3;
Chris Wrenf9536642014-04-17 10:01:54 -0400170 static final int MESSAGE_RECONSIDER_RANKING = 4;
Chris Wren54bbef42014-07-09 18:37:56 -0400171 static final int MESSAGE_RANKING_CONFIG_CHANGE = 5;
172 static final int MESSAGE_SEND_RANKING_UPDATE = 6;
John Spurlockd8afe3c2014-08-01 14:04:07 -0400173 static final int MESSAGE_LISTENER_HINTS_CHANGED = 7;
Christoph Studer85a384b2014-08-27 20:16:15 +0200174 static final int MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED = 8;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800175
Adam Lesinski182f73f2013-12-05 16:48:06 -0800176 static final int LONG_DELAY = 3500; // 3.5 seconds
177 static final int SHORT_DELAY = 2000; // 2 seconds
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800178
Adam Lesinski182f73f2013-12-05 16:48:06 -0800179 static final long[] DEFAULT_VIBRATE_PATTERN = {0, 250, 250, 250};
Christoph Studer265c1052014-07-23 17:14:33 +0200180
Adam Lesinski182f73f2013-12-05 16:48:06 -0800181 static final int VIBRATE_PATTERN_MAXLEN = 8 * 2 + 1; // up to eight bumps
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800182
Adam Lesinski182f73f2013-12-05 16:48:06 -0800183 static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_NOTIFICATION;
184 static final boolean SCORE_ONGOING_HIGHER = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800185
Adam Lesinski182f73f2013-12-05 16:48:06 -0800186 static final int JUNK_SCORE = -1000;
187 static final int NOTIFICATION_PRIORITY_MULTIPLIER = 10;
188 static final int SCORE_DISPLAY_THRESHOLD = Notification.PRIORITY_MIN * NOTIFICATION_PRIORITY_MULTIPLIER;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400189
Daniel Sandler526fa0e2012-12-04 14:51:50 -0500190 // Notifications with scores below this will not interrupt the user, either via LED or
191 // sound or vibration
Adam Lesinski182f73f2013-12-05 16:48:06 -0800192 static final int SCORE_INTERRUPTION_THRESHOLD =
Daniel Sandler526fa0e2012-12-04 14:51:50 -0500193 Notification.PRIORITY_LOW * NOTIFICATION_PRIORITY_MULTIPLIER;
194
Adam Lesinski182f73f2013-12-05 16:48:06 -0800195 static final boolean ENABLE_BLOCKED_NOTIFICATIONS = true;
196 static final boolean ENABLE_BLOCKED_TOASTS = true;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400197
Christoph Studer12aeda82014-09-23 19:08:56 +0200198 // When #matchesCallFilter is called from the ringer, wait at most
199 // 3s to resolve the contacts. This timeout is required since
200 // ContactsProvider might take a long time to start up.
201 //
202 // Return STARRED_CONTACT when the timeout is hit in order to avoid
203 // missed calls in ZEN mode "Important".
204 static final int MATCHES_CALL_FILTER_CONTACTS_TIMEOUT_MS = 3000;
205 static final float MATCHES_CALL_FILTER_TIMEOUT_AFFINITY =
206 ValidateNotificationPeople.STARRED_CONTACT;
207
Christoph Studer265c1052014-07-23 17:14:33 +0200208 /** notification_enqueue status value for a newly enqueued notification. */
209 private static final int EVENTLOG_ENQUEUE_STATUS_NEW = 0;
210
211 /** notification_enqueue status value for an existing notification. */
212 private static final int EVENTLOG_ENQUEUE_STATUS_UPDATE = 1;
213
214 /** notification_enqueue status value for an ignored notification. */
215 private static final int EVENTLOG_ENQUEUE_STATUS_IGNORED = 2;
216
Adam Lesinski182f73f2013-12-05 16:48:06 -0800217 private IActivityManager mAm;
218 AudioManager mAudioManager;
John Spurlockcdb57ae2015-02-11 19:04:11 -0500219 AudioManagerInternal mAudioManagerInternal;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800220 StatusBarManagerInternal mStatusBar;
221 Vibrator mVibrator;
222
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800223 final IBinder mForegroundToken = new Binder();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800224 private WorkerHandler mHandler;
Chris Wrenf9536642014-04-17 10:01:54 -0400225 private final HandlerThread mRankingThread = new HandlerThread("ranker",
226 Process.THREAD_PRIORITY_BACKGROUND);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800227
Adam Lesinski182f73f2013-12-05 16:48:06 -0800228 private Light mNotificationLight;
229 Light mAttentionLight;
Mike Lockwood670f9322010-01-20 12:13:36 -0500230 private int mDefaultNotificationColor;
231 private int mDefaultNotificationLedOn;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800232
Mike Lockwood670f9322010-01-20 12:13:36 -0500233 private int mDefaultNotificationLedOff;
Daniel Sandleredbb3802012-11-13 20:49:47 -0800234 private long[] mDefaultVibrationPattern;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800235
Daniel Sandleredbb3802012-11-13 20:49:47 -0800236 private long[] mFallbackVibrationPattern;
Chris Wren5116a822014-06-04 15:59:50 -0400237 private boolean mUseAttentionLight;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800238 boolean mSystemReady;
Daniel Sandleredbb3802012-11-13 20:49:47 -0800239
John Spurlockd8afe3c2014-08-01 14:04:07 -0400240 private boolean mDisableNotificationEffects;
John Spurlock32fe4c62014-10-02 12:16:02 -0400241 private int mCallState;
Chris Wren6054e612014-11-25 17:16:46 -0500242 private String mSoundNotificationKey;
243 private String mVibrateNotificationKey;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800244
John Spurlockd8afe3c2014-08-01 14:04:07 -0400245 private final ArraySet<ManagedServiceInfo> mListenersDisablingEffects = new ArraySet<>();
John Spurlockb4782522014-08-22 14:54:46 -0400246 private ComponentName mEffectsSuppressor;
John Spurlockd8afe3c2014-08-01 14:04:07 -0400247 private int mListenerHints; // right now, all hints are global
John Spurlock83104102015-02-12 23:25:12 -0500248 private int mInterruptionFilter = NotificationListenerService.INTERRUPTION_FILTER_UNKNOWN;
John Spurlock1fa865f2014-07-21 14:56:39 -0400249
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500250 // for enabling and disabling notification pulse behavior
Mike Lockwood63b5ad92011-08-30 09:55:30 -0400251 private boolean mScreenOn = true;
Daniel Sandlere96ffb12010-03-11 13:38:06 -0500252 private boolean mInCall = false;
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500253 private boolean mNotificationPulseEnabled;
254
Daniel Sandler09a247e2013-02-14 10:24:17 -0500255 // used as a mutex for access to all active notifications & listeners
Adam Lesinski182f73f2013-12-05 16:48:06 -0800256 final ArrayList<NotificationRecord> mNotificationList =
Fred Quintana6ecaff12009-09-25 14:23:13 -0700257 new ArrayList<NotificationRecord>();
John Spurlocka4294292014-03-24 18:02:32 -0400258 final ArrayMap<String, NotificationRecord> mNotificationsByKey =
259 new ArrayMap<String, NotificationRecord>();
Adam Lesinski182f73f2013-12-05 16:48:06 -0800260 final ArrayList<ToastRecord> mToastQueue = new ArrayList<ToastRecord>();
Christoph Studer265c1052014-07-23 17:14:33 +0200261 final ArrayMap<String, NotificationRecord> mSummaryByGroupKey = new ArrayMap<>();
John Spurlock7c74f782015-06-04 13:01:42 -0400262 final PolicyAccess mPolicyAccess = new PolicyAccess();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800263
Chris Wren6054e612014-11-25 17:16:46 -0500264 // The last key in this list owns the hardware.
265 ArrayList<String> mLights = new ArrayList<>();
svetoslavganov75986cf2009-05-14 22:28:01 -0700266
Adam Lesinski182f73f2013-12-05 16:48:06 -0800267 private AppOpsManager mAppOps;
Amith Yamasanif47e51e2015-04-17 10:02:15 -0700268 private UsageStatsManagerInternal mAppUsageStats;
Daniel Sandler4a900ac2013-01-30 14:04:10 -0500269
Griff Hazen9f637d12014-06-10 11:13:51 -0700270 private Archive mArchive;
271
John Spurlock21258a32015-05-27 18:22:55 -0400272 // Persistent storage for notification policy
Daniel Sandler0da673f2012-04-11 12:33:16 -0400273 private AtomicFile mPolicyFile;
John Spurlock21258a32015-05-27 18:22:55 -0400274
275 // Temporary holder for <blocked-packages> config coming from old policy files.
Daniel Sandler0da673f2012-04-11 12:33:16 -0400276 private HashSet<String> mBlockedPackages = new HashSet<String>();
277
278 private static final int DB_VERSION = 1;
279
John Spurlock21258a32015-05-27 18:22:55 -0400280 private static final String TAG_NOTIFICATION_POLICY = "notification-policy";
Daniel Sandler0da673f2012-04-11 12:33:16 -0400281 private static final String ATTR_VERSION = "version";
282
John Spurlock21258a32015-05-27 18:22:55 -0400283 // Obsolete: converted if present, but not resaved to disk.
Daniel Sandler0da673f2012-04-11 12:33:16 -0400284 private static final String TAG_BLOCKED_PKGS = "blocked-packages";
285 private static final String TAG_PACKAGE = "package";
286 private static final String ATTR_NAME = "name";
287
Chris Wren54bbef42014-07-09 18:37:56 -0400288 private RankingHelper mRankingHelper;
Scott Greenwald9a05b312013-06-28 00:37:54 -0400289
John Spurlockb408e8e2014-04-23 21:12:45 -0400290 private final UserProfiles mUserProfiles = new UserProfiles();
John Spurlock7340fc82014-04-24 18:50:12 -0400291 private NotificationListeners mListeners;
292 private ConditionProviders mConditionProviders;
Christoph Studer1c3f81f2014-04-16 15:05:56 +0200293 private NotificationUsageStats mUsageStats;
Christoph Studer546bec82014-03-14 12:17:12 +0100294
John Spurlocke6a7d932014-03-13 12:29:00 -0400295 private static final int MY_UID = Process.myUid();
296 private static final int MY_PID = Process.myPid();
John Spurlocke6a7d932014-03-13 12:29:00 -0400297
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500298 private static class Archive {
Griff Hazen9f637d12014-06-10 11:13:51 -0700299 final int mBufferSize;
300 final ArrayDeque<StatusBarNotification> mBuffer;
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500301
Griff Hazen9f637d12014-06-10 11:13:51 -0700302 public Archive(int size) {
303 mBufferSize = size;
304 mBuffer = new ArrayDeque<StatusBarNotification>(mBufferSize);
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500305 }
Jeff Sharkey0c1baf92013-04-03 13:08:52 -0700306
Daniel Sandler5e62e3a2013-04-15 20:57:02 -0400307 public String toString() {
308 final StringBuilder sb = new StringBuilder();
309 final int N = mBuffer.size();
310 sb.append("Archive (");
311 sb.append(N);
312 sb.append(" notification");
313 sb.append((N==1)?")":"s)");
314 return sb.toString();
315 }
316
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500317 public void record(StatusBarNotification nr) {
Griff Hazen9f637d12014-06-10 11:13:51 -0700318 if (mBuffer.size() == mBufferSize) {
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500319 mBuffer.removeFirst();
320 }
Daniel Sandler26b81d52013-05-20 20:56:43 -0400321
322 // We don't want to store the heavy bits of the notification in the archive,
323 // but other clients in the system process might be using the object, so we
324 // store a (lightened) copy.
325 mBuffer.addLast(nr.cloneLight());
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500326 }
327
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500328 public Iterator<StatusBarNotification> descendingIterator() {
329 return mBuffer.descendingIterator();
330 }
Daniel Sandler78d0d252013-02-12 08:14:52 -0500331
332 public StatusBarNotification[] getArray(int count) {
Griff Hazen9f637d12014-06-10 11:13:51 -0700333 if (count == 0) count = mBufferSize;
Daniel Sandler78d0d252013-02-12 08:14:52 -0500334 final StatusBarNotification[] a
335 = new StatusBarNotification[Math.min(count, mBuffer.size())];
336 Iterator<StatusBarNotification> iter = descendingIterator();
337 int i=0;
338 while (iter.hasNext() && i < count) {
339 a[i++] = iter.next();
340 }
341 return a;
342 }
343
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500344 }
345
John Spurlock35ef0a62015-05-28 11:24:10 -0400346 private void readPolicyXml(InputStream stream, boolean forRestore)
347 throws XmlPullParserException, NumberFormatException, IOException {
348 final XmlPullParser parser = Xml.newPullParser();
349 parser.setInput(stream, StandardCharsets.UTF_8.name());
350
351 int type;
352 String tag;
353 int version = DB_VERSION;
354 while ((type = parser.next()) != END_DOCUMENT) {
355 tag = parser.getName();
356 if (type == START_TAG) {
357 if (TAG_NOTIFICATION_POLICY.equals(tag)) {
358 version = Integer.parseInt(
359 parser.getAttributeValue(null, ATTR_VERSION));
360 } else if (TAG_BLOCKED_PKGS.equals(tag)) {
361 while ((type = parser.next()) != END_DOCUMENT) {
362 tag = parser.getName();
363 if (TAG_PACKAGE.equals(tag)) {
364 mBlockedPackages.add(
365 parser.getAttributeValue(null, ATTR_NAME));
366 } else if (TAG_BLOCKED_PKGS.equals(tag) && type == END_TAG) {
367 break;
368 }
369 }
370 }
371 }
372 mZenModeHelper.readXml(parser, forRestore);
373 mRankingHelper.readXml(parser, forRestore);
374 }
375 }
376
John Spurlock056c5192014-04-20 21:52:01 -0400377 private void loadPolicyFile() {
John Spurlock21258a32015-05-27 18:22:55 -0400378 if (DBG) Slog.d(TAG, "loadPolicyFile");
John Spurlock056c5192014-04-20 21:52:01 -0400379 synchronized(mPolicyFile) {
380 mBlockedPackages.clear();
Daniel Sandler0da673f2012-04-11 12:33:16 -0400381
John Spurlock056c5192014-04-20 21:52:01 -0400382 FileInputStream infile = null;
383 try {
384 infile = mPolicyFile.openRead();
John Spurlock35ef0a62015-05-28 11:24:10 -0400385 readPolicyXml(infile, false /*forRestore*/);
John Spurlock056c5192014-04-20 21:52:01 -0400386 } catch (FileNotFoundException e) {
387 // No data yet
388 } catch (IOException e) {
389 Log.wtf(TAG, "Unable to read notification policy", e);
390 } catch (NumberFormatException e) {
391 Log.wtf(TAG, "Unable to parse notification policy", e);
392 } catch (XmlPullParserException e) {
393 Log.wtf(TAG, "Unable to parse notification policy", e);
394 } finally {
395 IoUtils.closeQuietly(infile);
396 }
397 }
398 }
399
400 public void savePolicyFile() {
401 mHandler.removeMessages(MESSAGE_SAVE_POLICY_FILE);
402 mHandler.sendEmptyMessage(MESSAGE_SAVE_POLICY_FILE);
403 }
404
405 private void handleSavePolicyFile() {
John Spurlock21258a32015-05-27 18:22:55 -0400406 if (DBG) Slog.d(TAG, "handleSavePolicyFile");
John Spurlock056c5192014-04-20 21:52:01 -0400407 synchronized (mPolicyFile) {
408 final FileOutputStream stream;
409 try {
410 stream = mPolicyFile.startWrite();
411 } catch (IOException e) {
412 Slog.w(TAG, "Failed to save policy file", e);
413 return;
414 }
415
416 try {
John Spurlock35ef0a62015-05-28 11:24:10 -0400417 writePolicyXml(stream, false /*forBackup*/);
John Spurlock056c5192014-04-20 21:52:01 -0400418 mPolicyFile.finishWrite(stream);
419 } catch (IOException e) {
420 Slog.w(TAG, "Failed to save policy file, restoring backup", e);
421 mPolicyFile.failWrite(stream);
Daniel Sandler0da673f2012-04-11 12:33:16 -0400422 }
423 }
John Spurlock35ef0a62015-05-28 11:24:10 -0400424 BackupManager.dataChanged(getContext().getPackageName());
425 }
426
427 private void writePolicyXml(OutputStream stream, boolean forBackup) throws IOException {
428 final XmlSerializer out = new FastXmlSerializer();
429 out.setOutput(stream, StandardCharsets.UTF_8.name());
430 out.startDocument(null, true);
431 out.startTag(null, TAG_NOTIFICATION_POLICY);
432 out.attribute(null, ATTR_VERSION, Integer.toString(DB_VERSION));
433 mZenModeHelper.writeXml(out, forBackup);
434 mRankingHelper.writeXml(out, forBackup);
435 out.endTag(null, TAG_NOTIFICATION_POLICY);
436 out.endDocument();
Daniel Sandler0da673f2012-04-11 12:33:16 -0400437 }
438
Daniel Sandler4a900ac2013-01-30 14:04:10 -0500439 /** Use this when you actually want to post a notification or toast.
440 *
441 * Unchecked. Not exposed via Binder, but can be called in the course of enqueue*().
442 */
443 private boolean noteNotificationOp(String pkg, int uid) {
444 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
445 != AppOpsManager.MODE_ALLOWED) {
446 Slog.v(TAG, "notifications are disabled by AppOps for " + pkg);
447 return false;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400448 }
Daniel Sandler4a900ac2013-01-30 14:04:10 -0500449 return true;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400450 }
451
Chris Wren66189fc2015-06-25 14:04:33 -0400452 /** Use this to check if a package can post a notification or toast. */
453 private boolean checkNotificationOp(String pkg, int uid) {
454 return mAppOps.checkOp(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
455 == AppOpsManager.MODE_ALLOWED;
456 }
457
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800458 private static final class ToastRecord
459 {
460 final int pid;
461 final String pkg;
462 final ITransientNotification callback;
463 int duration;
464
465 ToastRecord(int pid, String pkg, ITransientNotification callback, int duration)
466 {
467 this.pid = pid;
468 this.pkg = pkg;
469 this.callback = callback;
470 this.duration = duration;
471 }
472
473 void update(int duration) {
474 this.duration = duration;
475 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800476
John Spurlock25e2d242014-06-27 13:58:23 -0400477 void dump(PrintWriter pw, String prefix, DumpFilter filter) {
478 if (filter != null && !filter.matches(pkg)) return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800479 pw.println(prefix + this);
480 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800481
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800482 @Override
483 public final String toString()
484 {
485 return "ToastRecord{"
486 + Integer.toHexString(System.identityHashCode(this))
487 + " pkg=" + pkg
488 + " callback=" + callback
489 + " duration=" + duration;
490 }
491 }
492
Adam Lesinski182f73f2013-12-05 16:48:06 -0800493 private final NotificationDelegate mNotificationDelegate = new NotificationDelegate() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800494
Adam Lesinski182f73f2013-12-05 16:48:06 -0800495 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800496 public void onSetDisabled(int status) {
497 synchronized (mNotificationList) {
John Spurlockd8afe3c2014-08-01 14:04:07 -0400498 mDisableNotificationEffects =
499 (status & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0;
John Spurlock32fe4c62014-10-02 12:16:02 -0400500 if (disableNotificationEffects(null) != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800501 // cancel whatever's going on
502 long identity = Binder.clearCallingIdentity();
503 try {
Adam Lesinski182f73f2013-12-05 16:48:06 -0800504 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
Jeff Sharkey098d5802012-04-26 17:30:34 -0700505 if (player != null) {
506 player.stopAsync();
507 }
508 } catch (RemoteException e) {
509 } finally {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800510 Binder.restoreCallingIdentity(identity);
511 }
512
513 identity = Binder.clearCallingIdentity();
514 try {
515 mVibrator.cancel();
Jeff Sharkey098d5802012-04-26 17:30:34 -0700516 } finally {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800517 Binder.restoreCallingIdentity(identity);
518 }
519 }
520 }
521 }
522
Adam Lesinski182f73f2013-12-05 16:48:06 -0800523 @Override
John Spurlocke6a7d932014-03-13 12:29:00 -0400524 public void onClearAll(int callingUid, int callingPid, int userId) {
Adam Lesinskie8240262014-03-26 16:01:00 -0700525 synchronized (mNotificationList) {
Kenny Guya263e4e2014-03-03 18:24:03 +0000526 cancelAllLocked(callingUid, callingPid, userId, REASON_DELEGATE_CANCEL_ALL, null,
527 /*includeCurrentProfiles*/ true);
Adam Lesinskie8240262014-03-26 16:01:00 -0700528 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800529 }
530
Adam Lesinski182f73f2013-12-05 16:48:06 -0800531 @Override
Christoph Studer03b87a22014-04-30 17:33:27 +0200532 public void onNotificationClick(int callingUid, int callingPid, String key) {
533 synchronized (mNotificationList) {
Christoph Studer03b87a22014-04-30 17:33:27 +0200534 NotificationRecord r = mNotificationsByKey.get(key);
535 if (r == null) {
536 Log.w(TAG, "No notification with key: " + key);
537 return;
538 }
Chris Wrene6ddb8a2015-05-27 15:21:00 -0400539 final long now = System.currentTimeMillis();
540 EventLogTags.writeNotificationClicked(key,
541 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
542
Christoph Studer03b87a22014-04-30 17:33:27 +0200543 StatusBarNotification sbn = r.sbn;
544 cancelNotification(callingUid, callingPid, sbn.getPackageName(), sbn.getTag(),
545 sbn.getId(), Notification.FLAG_AUTO_CANCEL,
546 Notification.FLAG_FOREGROUND_SERVICE, false, r.getUserId(),
547 REASON_DELEGATE_CLICK, null);
548 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800549 }
550
Adam Lesinski182f73f2013-12-05 16:48:06 -0800551 @Override
Christoph Studer4da84cd2014-10-21 17:24:20 +0200552 public void onNotificationActionClick(int callingUid, int callingPid, String key,
553 int actionIndex) {
554 synchronized (mNotificationList) {
Christoph Studer4da84cd2014-10-21 17:24:20 +0200555 NotificationRecord r = mNotificationsByKey.get(key);
556 if (r == null) {
557 Log.w(TAG, "No notification with key: " + key);
558 return;
559 }
Chris Wrene6ddb8a2015-05-27 15:21:00 -0400560 final long now = System.currentTimeMillis();
561 EventLogTags.writeNotificationActionClicked(key, actionIndex,
562 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
Christoph Studer4da84cd2014-10-21 17:24:20 +0200563 // TODO: Log action click via UsageStats.
564 }
565 }
566
567 @Override
John Spurlocke6a7d932014-03-13 12:29:00 -0400568 public void onNotificationClear(int callingUid, int callingPid,
569 String pkg, String tag, int id, int userId) {
570 cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
Kenny Guy3a7c4a52014-03-03 18:24:03 +0000571 Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
John Spurlocke6a7d932014-03-13 12:29:00 -0400572 true, userId, REASON_DELEGATE_CANCEL, null);
Daniel Sandler0f0b11c2010-08-04 15:54:58 -0400573 }
574
Adam Lesinski182f73f2013-12-05 16:48:06 -0800575 @Override
Chris Wrenb659c4f2015-06-25 17:12:27 -0400576 public void onPanelRevealed(boolean clearEffects, int items) {
577 EventLogTags.writeNotificationPanelRevealed(items);
Christoph Studer1f32c652014-11-26 15:32:20 +0100578 if (clearEffects) {
579 clearEffects();
580 }
581 }
582
583 @Override
584 public void onPanelHidden() {
585 EventLogTags.writeNotificationPanelHidden();
586 }
587
588 @Override
589 public void clearEffects() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800590 synchronized (mNotificationList) {
Christoph Studer1f32c652014-11-26 15:32:20 +0100591 if (DBG) Slog.d(TAG, "clearEffects");
592
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800593 // sound
Chris Wren6054e612014-11-25 17:16:46 -0500594 mSoundNotificationKey = null;
Jeff Sharkey098d5802012-04-26 17:30:34 -0700595
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800596 long identity = Binder.clearCallingIdentity();
597 try {
Adam Lesinski182f73f2013-12-05 16:48:06 -0800598 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
Jeff Sharkey098d5802012-04-26 17:30:34 -0700599 if (player != null) {
600 player.stopAsync();
601 }
602 } catch (RemoteException e) {
603 } finally {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800604 Binder.restoreCallingIdentity(identity);
605 }
606
607 // vibrate
Chris Wren6054e612014-11-25 17:16:46 -0500608 mVibrateNotificationKey = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800609 identity = Binder.clearCallingIdentity();
610 try {
611 mVibrator.cancel();
Jeff Sharkey098d5802012-04-26 17:30:34 -0700612 } finally {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800613 Binder.restoreCallingIdentity(identity);
614 }
615
616 // light
617 mLights.clear();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800618 updateLightsLocked();
619 }
620 }
Joe Onorato005847b2010-06-04 16:08:02 -0400621
Adam Lesinski182f73f2013-12-05 16:48:06 -0800622 @Override
John Spurlocke6a7d932014-03-13 12:29:00 -0400623 public void onNotificationError(int callingUid, int callingPid, String pkg, String tag, int id,
Kenny Guy3a7c4a52014-03-03 18:24:03 +0000624 int uid, int initialPid, String message, int userId) {
Daniel Sandlerd0a2f862010-08-03 15:29:31 -0400625 Slog.d(TAG, "onNotification error pkg=" + pkg + " tag=" + tag + " id=" + id
626 + "; will crashApplication(uid=" + uid + ", pid=" + initialPid + ")");
John Spurlocke6a7d932014-03-13 12:29:00 -0400627 cancelNotification(callingUid, callingPid, pkg, tag, id, 0, 0, false, userId,
628 REASON_DELEGATE_ERROR, null);
Dianne Hackborn9d39d0c2010-06-24 15:57:42 -0700629 long ident = Binder.clearCallingIdentity();
630 try {
631 ActivityManagerNative.getDefault().crashApplication(uid, initialPid, pkg,
632 "Bad notification posted from package " + pkg
633 + ": " + message);
634 } catch (RemoteException e) {
635 }
636 Binder.restoreCallingIdentity(ident);
Joe Onorato005847b2010-06-04 16:08:02 -0400637 }
John Spurlocke677d712014-02-13 12:52:19 -0500638
639 @Override
Chris Wrend1dbc922015-06-19 17:51:16 -0400640 public void onNotificationVisibilityChanged(NotificationVisibility[] newlyVisibleKeys,
641 NotificationVisibility[] noLongerVisibleKeys) {
Christoph Studerffeb0c32014-05-07 22:23:56 +0200642 synchronized (mNotificationList) {
Chris Wrend1dbc922015-06-19 17:51:16 -0400643 for (NotificationVisibility nv : newlyVisibleKeys) {
644 NotificationRecord r = mNotificationsByKey.get(nv.key);
Christoph Studerffeb0c32014-05-07 22:23:56 +0200645 if (r == null) continue;
Chris Wrend1dbc922015-06-19 17:51:16 -0400646 r.setVisibility(true, nv.rank);
647 nv.recycle();
Christoph Studerffeb0c32014-05-07 22:23:56 +0200648 }
649 // Note that we might receive this event after notifications
650 // have already left the system, e.g. after dismissing from the
651 // shade. Hence not finding notifications in
652 // mNotificationsByKey is not an exceptional condition.
Chris Wrend1dbc922015-06-19 17:51:16 -0400653 for (NotificationVisibility nv : noLongerVisibleKeys) {
654 NotificationRecord r = mNotificationsByKey.get(nv.key);
Christoph Studerffeb0c32014-05-07 22:23:56 +0200655 if (r == null) continue;
Chris Wrend1dbc922015-06-19 17:51:16 -0400656 r.setVisibility(false, nv.rank);
657 nv.recycle();
Christoph Studerffeb0c32014-05-07 22:23:56 +0200658 }
659 }
Christoph Studer92b389d2014-04-01 18:44:40 +0200660 }
Chris Wren78403d72014-07-28 10:23:24 +0100661
662 @Override
663 public void onNotificationExpansionChanged(String key,
664 boolean userAction, boolean expanded) {
Chris Wren78403d72014-07-28 10:23:24 +0100665 synchronized (mNotificationList) {
666 NotificationRecord r = mNotificationsByKey.get(key);
667 if (r != null) {
668 r.stats.onExpansionChanged(userAction, expanded);
Chris Wrene6ddb8a2015-05-27 15:21:00 -0400669 final long now = System.currentTimeMillis();
670 EventLogTags.writeNotificationExpansion(key,
671 userAction ? 1 : 0, expanded ? 1 : 0,
672 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
Chris Wren78403d72014-07-28 10:23:24 +0100673 }
674 }
675 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800676 };
677
Kenny Guy70058402014-10-28 20:45:06 +0000678 private final BroadcastReceiver mPackageIntentReceiver = new BroadcastReceiver() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800679 @Override
680 public void onReceive(Context context, Intent intent) {
681 String action = intent.getAction();
Dianne Hackborn29cd7f12015-01-08 10:37:05 -0800682 if (action == null) {
683 return;
684 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800685
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800686 boolean queryRestart = false;
Chris Wrenae9bb572013-05-15 14:50:28 -0400687 boolean queryRemove = false;
Daniel Sandler26ece572012-06-01 15:38:46 -0400688 boolean packageChanged = false;
John Spurlock79f78922013-05-16 09:10:05 -0400689 boolean cancelNotifications = true;
Chris Wrenf9536642014-04-17 10:01:54 -0400690
Chris Wren3da73022013-05-10 14:41:21 -0400691 if (action.equals(Intent.ACTION_PACKAGE_ADDED)
Chris Wrenae9bb572013-05-15 14:50:28 -0400692 || (queryRemove=action.equals(Intent.ACTION_PACKAGE_REMOVED))
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800693 || action.equals(Intent.ACTION_PACKAGE_RESTARTED)
Daniel Sandler26ece572012-06-01 15:38:46 -0400694 || (packageChanged=action.equals(Intent.ACTION_PACKAGE_CHANGED))
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800695 || (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART))
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800696 || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
Kenny Guy70058402014-10-28 20:45:06 +0000697 int changeUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
698 UserHandle.USER_ALL);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800699 String pkgList[] = null;
Chris Wrenae9bb572013-05-15 14:50:28 -0400700 boolean queryReplace = queryRemove &&
701 intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
John Spurlocke77bb362014-04-26 10:24:59 -0400702 if (DBG) Slog.i(TAG, "action=" + action + " queryReplace=" + queryReplace);
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800703 if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800704 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800705 } else if (queryRestart) {
706 pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800707 } else {
708 Uri uri = intent.getData();
709 if (uri == null) {
710 return;
711 }
712 String pkgName = uri.getSchemeSpecificPart();
713 if (pkgName == null) {
714 return;
715 }
Daniel Sandler26ece572012-06-01 15:38:46 -0400716 if (packageChanged) {
717 // We cancel notifications for packages which have just been disabled
Christopher Tate06e5fed2013-10-09 14:39:15 -0700718 try {
Kenny Guy70058402014-10-28 20:45:06 +0000719 final IPackageManager pm = AppGlobals.getPackageManager();
720 final int enabled = pm.getApplicationEnabledSetting(pkgName,
721 changeUserId != UserHandle.USER_ALL ? changeUserId :
Xiaohui Chenddbe4ca2015-08-13 16:20:56 -0700722 UserHandle.USER_SYSTEM);
Christopher Tate06e5fed2013-10-09 14:39:15 -0700723 if (enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED
724 || enabled == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
725 cancelNotifications = false;
726 }
727 } catch (IllegalArgumentException e) {
728 // Package doesn't exist; probably racing with uninstall.
729 // cancelNotifications is already true, so nothing to do here.
730 if (DBG) {
731 Slog.i(TAG, "Exception trying to look up app enabled setting", e);
732 }
Kenny Guy70058402014-10-28 20:45:06 +0000733 } catch (RemoteException e) {
734 // Failed to talk to PackageManagerService Should never happen!
Daniel Sandler26ece572012-06-01 15:38:46 -0400735 }
736 }
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800737 pkgList = new String[]{pkgName};
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800738 }
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700739
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800740 if (pkgList != null && (pkgList.length > 0)) {
741 for (String pkgName : pkgList) {
John Spurlock79f78922013-05-16 09:10:05 -0400742 if (cancelNotifications) {
John Spurlocke6a7d932014-03-13 12:29:00 -0400743 cancelAllNotificationsInt(MY_UID, MY_PID, pkgName, 0, 0, !queryRestart,
Kenny Guy70058402014-10-28 20:45:06 +0000744 changeUserId, REASON_PACKAGE_CHANGED, null);
John Spurlock79f78922013-05-16 09:10:05 -0400745 }
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800746 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800747 }
John Spurlockb408e8e2014-04-23 21:12:45 -0400748 mListeners.onPackagesChanged(queryReplace, pkgList);
John Spurlock7340fc82014-04-24 18:50:12 -0400749 mConditionProviders.onPackagesChanged(queryReplace, pkgList);
John Spurlock35ef0a62015-05-28 11:24:10 -0400750 mRankingHelper.onPackagesChanged(queryReplace, pkgList);
Kenny Guy70058402014-10-28 20:45:06 +0000751 }
752 }
753 };
754
755 private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
756 @Override
757 public void onReceive(Context context, Intent intent) {
758 String action = intent.getAction();
759
760 if (action.equals(Intent.ACTION_SCREEN_ON)) {
Mike Lockwood63b5ad92011-08-30 09:55:30 -0400761 // Keep track of screen on/off state, but do not turn off the notification light
762 // until user passes through the lock screen or views the notification.
763 mScreenOn = true;
Christoph Studer1f32c652014-11-26 15:32:20 +0100764 updateNotificationPulse();
Mike Lockwood63b5ad92011-08-30 09:55:30 -0400765 } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
766 mScreenOn = false;
Christoph Studer1f32c652014-11-26 15:32:20 +0100767 updateNotificationPulse();
Daniel Sandlere96ffb12010-03-11 13:38:06 -0500768 } else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) {
John Spurlock5d2eeb12014-01-16 10:46:36 -0500769 mInCall = TelephonyManager.EXTRA_STATE_OFFHOOK
770 .equals(intent.getStringExtra(TelephonyManager.EXTRA_STATE));
Daniel Sandlere96ffb12010-03-11 13:38:06 -0500771 updateNotificationPulse();
Dianne Hackborn80a4af22012-08-27 19:18:31 -0700772 } else if (action.equals(Intent.ACTION_USER_STOPPED)) {
773 int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
774 if (userHandle >= 0) {
John Spurlocke6a7d932014-03-13 12:29:00 -0400775 cancelAllNotificationsInt(MY_UID, MY_PID, null, 0, 0, true, userHandle,
776 REASON_USER_STOPPED, null);
Dianne Hackborn80a4af22012-08-27 19:18:31 -0700777 }
Mike Lockwood63b5ad92011-08-30 09:55:30 -0400778 } else if (action.equals(Intent.ACTION_USER_PRESENT)) {
779 // turn off LED when user passes through lock screen
780 mNotificationLight.turnOff();
John Spurlockcb566aa2014-08-03 22:58:28 -0400781 mStatusBar.notificationLightOff();
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400782 } else if (action.equals(Intent.ACTION_USER_SWITCHED)) {
John Spurlock1b8b22b2015-05-20 09:47:13 -0400783 final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400784 // reload per-user settings
785 mSettingsObserver.update(null);
John Spurlockb408e8e2014-04-23 21:12:45 -0400786 mUserProfiles.updateCache(context);
Christoph Studerb53dfd42014-09-12 14:45:59 +0200787 // Refresh managed services
John Spurlock1b8b22b2015-05-20 09:47:13 -0400788 mConditionProviders.onUserSwitched(user);
789 mListeners.onUserSwitched(user);
John Spurlock21258a32015-05-27 18:22:55 -0400790 mZenModeHelper.onUserSwitched(user);
Kenny Guy3a7c4a52014-03-03 18:24:03 +0000791 } else if (action.equals(Intent.ACTION_USER_ADDED)) {
John Spurlockb408e8e2014-04-23 21:12:45 -0400792 mUserProfiles.updateCache(context);
John Spurlock21258a32015-05-27 18:22:55 -0400793 } else if (action.equals(Intent.ACTION_USER_REMOVED)) {
794 final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
795 mZenModeHelper.onUserRemoved(user);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800796 }
797 }
798 };
799
John Spurlock7c74f782015-06-04 13:01:42 -0400800 private final class SettingsObserver extends ContentObserver {
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400801 private final Uri NOTIFICATION_LIGHT_PULSE_URI
802 = Settings.System.getUriFor(Settings.System.NOTIFICATION_LIGHT_PULSE);
803
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700804 SettingsObserver(Handler handler) {
805 super(handler);
806 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800807
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700808 void observe() {
Adam Lesinski182f73f2013-12-05 16:48:06 -0800809 ContentResolver resolver = getContext().getContentResolver();
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400810 resolver.registerContentObserver(NOTIFICATION_LIGHT_PULSE_URI,
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700811 false, this, UserHandle.USER_ALL);
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400812 update(null);
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700813 }
814
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400815 @Override public void onChange(boolean selfChange, Uri uri) {
816 update(uri);
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700817 }
818
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400819 public void update(Uri uri) {
Adam Lesinski182f73f2013-12-05 16:48:06 -0800820 ContentResolver resolver = getContext().getContentResolver();
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400821 if (uri == null || NOTIFICATION_LIGHT_PULSE_URI.equals(uri)) {
822 boolean pulseEnabled = Settings.System.getInt(resolver,
823 Settings.System.NOTIFICATION_LIGHT_PULSE, 0) != 0;
824 if (mNotificationPulseEnabled != pulseEnabled) {
825 mNotificationPulseEnabled = pulseEnabled;
826 updateNotificationPulse();
827 }
828 }
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700829 }
830 }
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500831
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400832 private SettingsObserver mSettingsObserver;
John Spurlock056c5192014-04-20 21:52:01 -0400833 private ZenModeHelper mZenModeHelper;
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400834
John Spurlockcad57682014-07-26 17:09:56 -0400835 private final Runnable mBuzzBeepBlinked = new Runnable() {
836 @Override
837 public void run() {
838 mStatusBar.buzzBeepBlinked();
839 }
840 };
841
Daniel Sandleredbb3802012-11-13 20:49:47 -0800842 static long[] getLongArray(Resources r, int resid, int maxlen, long[] def) {
843 int[] ar = r.getIntArray(resid);
844 if (ar == null) {
845 return def;
846 }
847 final int len = ar.length > maxlen ? maxlen : ar.length;
848 long[] out = new long[len];
849 for (int i=0; i<len; i++) {
850 out[i] = ar[i];
851 }
852 return out;
853 }
854
Jeff Brownb880d882014-02-10 19:47:07 -0800855 public NotificationManagerService(Context context) {
856 super(context);
857 }
858
Adam Lesinski182f73f2013-12-05 16:48:06 -0800859 @Override
860 public void onStart() {
Chris Wren54bbef42014-07-09 18:37:56 -0400861 Resources resources = getContext().getResources();
862
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800863 mAm = ActivityManagerNative.getDefault();
Adam Lesinski182f73f2013-12-05 16:48:06 -0800864 mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
865 mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
Amith Yamasanif47e51e2015-04-17 10:02:15 -0700866 mAppUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
San Mehat3ee13172010-02-04 20:54:43 -0800867
Adam Lesinski182f73f2013-12-05 16:48:06 -0800868 mHandler = new WorkerHandler();
Chris Wrenf9536642014-04-17 10:01:54 -0400869 mRankingThread.start();
Chris Wren54bbef42014-07-09 18:37:56 -0400870 String[] extractorNames;
871 try {
872 extractorNames = resources.getStringArray(R.array.config_notificationSignalExtractors);
873 } catch (Resources.NotFoundException e) {
874 extractorNames = new String[0];
875 }
Chris Wren5eab2b72015-06-16 13:56:22 -0400876 mUsageStats = new NotificationUsageStats(getContext());
Chris Wren54bbef42014-07-09 18:37:56 -0400877 mRankingHelper = new RankingHelper(getContext(),
878 new RankingWorkerHandler(mRankingThread.getLooper()),
Chris Wren5eab2b72015-06-16 13:56:22 -0400879 mUsageStats,
Chris Wren54bbef42014-07-09 18:37:56 -0400880 extractorNames);
John Spurlockb2278d62015-04-07 12:47:12 -0400881 mConditionProviders = new ConditionProviders(getContext(), mHandler, mUserProfiles);
882 mZenModeHelper = new ZenModeHelper(getContext(), mHandler.getLooper(), mConditionProviders);
John Spurlock1c923a32014-04-27 16:42:29 -0400883 mZenModeHelper.addCallback(new ZenModeHelper.Callback() {
John Spurlock056c5192014-04-20 21:52:01 -0400884 @Override
885 public void onConfigChanged() {
886 savePolicyFile();
887 }
John Spurlockd8afe3c2014-08-01 14:04:07 -0400888
889 @Override
890 void onZenModeChanged() {
John Spurlock80774932015-05-07 17:38:50 -0400891 sendRegisteredOnlyBroadcast(NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED);
John Spurlockd8afe3c2014-08-01 14:04:07 -0400892 synchronized(mNotificationList) {
Christoph Studer85a384b2014-08-27 20:16:15 +0200893 updateInterruptionFilterLocked();
John Spurlockd8afe3c2014-08-01 14:04:07 -0400894 }
895 }
John Spurlock1fc476d2015-04-14 16:05:20 -0400896
897 @Override
898 void onPolicyChanged() {
John Spurlock80774932015-05-07 17:38:50 -0400899 sendRegisteredOnlyBroadcast(NotificationManager.ACTION_NOTIFICATION_POLICY_CHANGED);
900 }
John Spurlock056c5192014-04-20 21:52:01 -0400901 });
902 final File systemDir = new File(Environment.getDataDirectory(), "system");
903 mPolicyFile = new AtomicFile(new File(systemDir, "notification_policy.xml"));
Daniel Sandler4a900ac2013-01-30 14:04:10 -0500904
905 importOldBlockDb();
Daniel Sandler0da673f2012-04-11 12:33:16 -0400906
John Spurlock7340fc82014-04-24 18:50:12 -0400907 mListeners = new NotificationListeners();
Adam Lesinski182f73f2013-12-05 16:48:06 -0800908 mStatusBar = getLocalService(StatusBarManagerInternal.class);
909 mStatusBar.setNotificationDelegate(mNotificationDelegate);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800910
Adam Lesinski182f73f2013-12-05 16:48:06 -0800911 final LightsManager lights = getLocalService(LightsManager.class);
912 mNotificationLight = lights.getLight(LightsManager.LIGHT_ID_NOTIFICATIONS);
913 mAttentionLight = lights.getLight(LightsManager.LIGHT_ID_ATTENTION);
Mike Lockwood3cb67a32009-11-27 14:25:58 -0500914
Mike Lockwood670f9322010-01-20 12:13:36 -0500915 mDefaultNotificationColor = resources.getColor(
Scott Greenwald9a05b312013-06-28 00:37:54 -0400916 R.color.config_defaultNotificationColor);
Mike Lockwood670f9322010-01-20 12:13:36 -0500917 mDefaultNotificationLedOn = resources.getInteger(
Scott Greenwald9a05b312013-06-28 00:37:54 -0400918 R.integer.config_defaultNotificationLedOn);
Mike Lockwood670f9322010-01-20 12:13:36 -0500919 mDefaultNotificationLedOff = resources.getInteger(
Scott Greenwald9a05b312013-06-28 00:37:54 -0400920 R.integer.config_defaultNotificationLedOff);
Mike Lockwood670f9322010-01-20 12:13:36 -0500921
Daniel Sandleredbb3802012-11-13 20:49:47 -0800922 mDefaultVibrationPattern = getLongArray(resources,
Scott Greenwald9a05b312013-06-28 00:37:54 -0400923 R.array.config_defaultNotificationVibePattern,
Daniel Sandleredbb3802012-11-13 20:49:47 -0800924 VIBRATE_PATTERN_MAXLEN,
925 DEFAULT_VIBRATE_PATTERN);
926
927 mFallbackVibrationPattern = getLongArray(resources,
Scott Greenwald9a05b312013-06-28 00:37:54 -0400928 R.array.config_notificationFallbackVibePattern,
Daniel Sandleredbb3802012-11-13 20:49:47 -0800929 VIBRATE_PATTERN_MAXLEN,
930 DEFAULT_VIBRATE_PATTERN);
931
Chris Wren5116a822014-06-04 15:59:50 -0400932 mUseAttentionLight = resources.getBoolean(R.bool.config_useAttentionLight);
933
Joe Onorato39f5b6a2009-07-23 12:29:19 -0400934 // Don't start allowing notifications until the setup wizard has run once.
935 // After that, including subsequent boots, init with notifications turned on.
936 // This works on the first boot because the setup wizard will toggle this
937 // flag at least once and we'll go back to 0 after that.
Adam Lesinski182f73f2013-12-05 16:48:06 -0800938 if (0 == Settings.Global.getInt(getContext().getContentResolver(),
Jeff Brownbf6f6f92012-09-25 15:03:20 -0700939 Settings.Global.DEVICE_PROVISIONED, 0)) {
John Spurlockd8afe3c2014-08-01 14:04:07 -0400940 mDisableNotificationEffects = true;
Joe Onorato39f5b6a2009-07-23 12:29:19 -0400941 }
John Spurlockb2278d62015-04-07 12:47:12 -0400942 mZenModeHelper.initZenMode();
John Spurlockf3701772015-02-12 13:29:37 -0500943 mInterruptionFilter = mZenModeHelper.getZenModeListenerInterruptionFilter();
Joe Onorato39f5b6a2009-07-23 12:29:19 -0400944
John Spurlockb408e8e2014-04-23 21:12:45 -0400945 mUserProfiles.updateCache(getContext());
John Spurlock32fe4c62014-10-02 12:16:02 -0400946 listenForCallState();
Kenny Guya263e4e2014-03-03 18:24:03 +0000947
Mike Lockwood35e16bf2010-11-30 19:53:36 -0500948 // register for various Intents
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800949 IntentFilter filter = new IntentFilter();
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500950 filter.addAction(Intent.ACTION_SCREEN_ON);
951 filter.addAction(Intent.ACTION_SCREEN_OFF);
Daniel Sandlere96ffb12010-03-11 13:38:06 -0500952 filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
Mike Lockwood63b5ad92011-08-30 09:55:30 -0400953 filter.addAction(Intent.ACTION_USER_PRESENT);
Dianne Hackborn80a4af22012-08-27 19:18:31 -0700954 filter.addAction(Intent.ACTION_USER_STOPPED);
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400955 filter.addAction(Intent.ACTION_USER_SWITCHED);
Kenny Guy3a7c4a52014-03-03 18:24:03 +0000956 filter.addAction(Intent.ACTION_USER_ADDED);
John Spurlock21258a32015-05-27 18:22:55 -0400957 filter.addAction(Intent.ACTION_USER_REMOVED);
Adam Lesinski182f73f2013-12-05 16:48:06 -0800958 getContext().registerReceiver(mIntentReceiver, filter);
Kenny Guy70058402014-10-28 20:45:06 +0000959
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800960 IntentFilter pkgFilter = new IntentFilter();
Chris Wren3da73022013-05-10 14:41:21 -0400961 pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800962 pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
Daniel Sandleraac0eb02011-08-06 22:51:56 -0400963 pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800964 pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
965 pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
966 pkgFilter.addDataScheme("package");
Kenny Guy70058402014-10-28 20:45:06 +0000967 getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, pkgFilter, null,
968 null);
969
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800970 IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
Kenny Guy70058402014-10-28 20:45:06 +0000971 getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, sdFilter, null,
972 null);
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800973
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400974 mSettingsObserver = new SettingsObserver(mHandler);
Scott Greenwald9a05b312013-06-28 00:37:54 -0400975
Griff Hazen9f637d12014-06-10 11:13:51 -0700976 mArchive = new Archive(resources.getInteger(
977 R.integer.config_notificationServiceArchiveSize));
978
Adam Lesinski182f73f2013-12-05 16:48:06 -0800979 publishBinderService(Context.NOTIFICATION_SERVICE, mService);
980 publishLocalService(NotificationManagerInternal.class, mInternalService);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800981 }
982
John Spurlocke7a835b2015-05-13 10:47:05 -0400983 private void sendRegisteredOnlyBroadcast(String action) {
984 getContext().sendBroadcastAsUser(new Intent(action)
985 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), UserHandle.ALL, null);
986 }
987
Daniel Sandler4a900ac2013-01-30 14:04:10 -0500988 /**
989 * Read the old XML-based app block database and import those blockages into the AppOps system.
990 */
991 private void importOldBlockDb() {
John Spurlock056c5192014-04-20 21:52:01 -0400992 loadPolicyFile();
Daniel Sandler4a900ac2013-01-30 14:04:10 -0500993
Adam Lesinski182f73f2013-12-05 16:48:06 -0800994 PackageManager pm = getContext().getPackageManager();
Daniel Sandler4a900ac2013-01-30 14:04:10 -0500995 for (String pkg : mBlockedPackages) {
996 PackageInfo info = null;
997 try {
998 info = pm.getPackageInfo(pkg, 0);
Adam Lesinski182f73f2013-12-05 16:48:06 -0800999 setNotificationsEnabledForPackageImpl(pkg, info.applicationInfo.uid, false);
Daniel Sandler4a900ac2013-01-30 14:04:10 -05001000 } catch (NameNotFoundException e) {
1001 // forget you
1002 }
1003 }
1004 mBlockedPackages.clear();
Daniel Sandler4a900ac2013-01-30 14:04:10 -05001005 }
1006
Adam Lesinski182f73f2013-12-05 16:48:06 -08001007 @Override
1008 public void onBootPhase(int phase) {
1009 if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
1010 // no beeping until we're basically done booting
1011 mSystemReady = true;
Jeff Sharkey098d5802012-04-26 17:30:34 -07001012
Adam Lesinski182f73f2013-12-05 16:48:06 -08001013 // Grab our optional AudioService
1014 mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
John Spurlockcdb57ae2015-02-11 19:04:11 -05001015 mAudioManagerInternal = getLocalService(AudioManagerInternal.class);
John Spurlock661f2cf2014-11-17 10:29:10 -05001016 mZenModeHelper.onSystemReady();
Adam Lesinskia6db4ab2014-03-24 12:31:45 -07001017 } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
1018 // This observer will force an update when observe is called, causing us to
1019 // bind to listener services.
1020 mSettingsObserver.observe();
John Spurlockb408e8e2014-04-23 21:12:45 -04001021 mListeners.onBootPhaseAppsCanStart();
John Spurlock7340fc82014-04-24 18:50:12 -04001022 mConditionProviders.onBootPhaseAppsCanStart();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001023 }
1024 }
1025
Adam Lesinski182f73f2013-12-05 16:48:06 -08001026 void setNotificationsEnabledForPackageImpl(String pkg, int uid, boolean enabled) {
1027 Slog.v(TAG, (enabled?"en":"dis") + "abling notifications for " + pkg);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001028
Adam Lesinski182f73f2013-12-05 16:48:06 -08001029 mAppOps.setMode(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg,
1030 enabled ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001031
Adam Lesinski182f73f2013-12-05 16:48:06 -08001032 // Now, cancel any outstanding notifications that are part of a just-disabled app
1033 if (ENABLE_BLOCKED_NOTIFICATIONS && !enabled) {
John Spurlocke6a7d932014-03-13 12:29:00 -04001034 cancelAllNotificationsInt(MY_UID, MY_PID, pkg, 0, 0, true, UserHandle.getUserId(uid),
1035 REASON_PACKAGE_BANNED, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001036 }
1037 }
1038
John Spurlockd8afe3c2014-08-01 14:04:07 -04001039 private void updateListenerHintsLocked() {
Christoph Studer85a384b2014-08-27 20:16:15 +02001040 final int hints = mListenersDisablingEffects.isEmpty() ? 0 : HINT_HOST_DISABLE_EFFECTS;
John Spurlockd8afe3c2014-08-01 14:04:07 -04001041 if (hints == mListenerHints) return;
John Spurlocka7082992015-03-09 12:19:23 -04001042 ZenLog.traceListenerHintsChanged(mListenerHints, hints, mListenersDisablingEffects.size());
John Spurlockd8afe3c2014-08-01 14:04:07 -04001043 mListenerHints = hints;
1044 scheduleListenerHintsChanged(hints);
John Spurlock1fa865f2014-07-21 14:56:39 -04001045 }
1046
John Spurlockb4782522014-08-22 14:54:46 -04001047 private void updateEffectsSuppressorLocked() {
1048 final ComponentName suppressor = !mListenersDisablingEffects.isEmpty()
1049 ? mListenersDisablingEffects.valueAt(0).component : null;
1050 if (Objects.equals(suppressor, mEffectsSuppressor)) return;
John Spurlocka7082992015-03-09 12:19:23 -04001051 ZenLog.traceEffectsSuppressorChanged(mEffectsSuppressor, suppressor);
John Spurlockb4782522014-08-22 14:54:46 -04001052 mEffectsSuppressor = suppressor;
John Spurlock8403b752014-12-10 12:47:01 -05001053 mZenModeHelper.setEffectsSuppressed(suppressor != null);
John Spurlocke7a835b2015-05-13 10:47:05 -04001054 sendRegisteredOnlyBroadcast(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED);
John Spurlockb4782522014-08-22 14:54:46 -04001055 }
1056
Christoph Studer85a384b2014-08-27 20:16:15 +02001057 private void updateInterruptionFilterLocked() {
1058 int interruptionFilter = mZenModeHelper.getZenModeListenerInterruptionFilter();
1059 if (interruptionFilter == mInterruptionFilter) return;
1060 mInterruptionFilter = interruptionFilter;
1061 scheduleInterruptionFilterChanged(interruptionFilter);
1062 }
1063
Adam Lesinski182f73f2013-12-05 16:48:06 -08001064 private final IBinder mService = new INotificationManager.Stub() {
1065 // Toasts
1066 // ============================================================================
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001067
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001068 @Override
Adam Lesinski182f73f2013-12-05 16:48:06 -08001069 public void enqueueToast(String pkg, ITransientNotification callback, int duration)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001070 {
Adam Lesinski182f73f2013-12-05 16:48:06 -08001071 if (DBG) {
1072 Slog.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback
1073 + " duration=" + duration);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001074 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001075
1076 if (pkg == null || callback == null) {
1077 Slog.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback);
1078 return ;
1079 }
1080
John Spurlock7340fc82014-04-24 18:50:12 -04001081 final boolean isSystemToast = isCallerSystem() || ("android".equals(pkg));
Adam Lesinski182f73f2013-12-05 16:48:06 -08001082
1083 if (ENABLE_BLOCKED_TOASTS && !noteNotificationOp(pkg, Binder.getCallingUid())) {
1084 if (!isSystemToast) {
1085 Slog.e(TAG, "Suppressing toast from package " + pkg + " by user request.");
1086 return;
1087 }
1088 }
1089
1090 synchronized (mToastQueue) {
1091 int callingPid = Binder.getCallingPid();
1092 long callingId = Binder.clearCallingIdentity();
1093 try {
1094 ToastRecord record;
1095 int index = indexOfToastLocked(pkg, callback);
1096 // If it's already in the queue, we update it in place, we don't
1097 // move it to the end of the queue.
1098 if (index >= 0) {
1099 record = mToastQueue.get(index);
1100 record.update(duration);
1101 } else {
1102 // Limit the number of toasts that any given package except the android
1103 // package can enqueue. Prevents DOS attacks and deals with leaks.
1104 if (!isSystemToast) {
1105 int count = 0;
1106 final int N = mToastQueue.size();
1107 for (int i=0; i<N; i++) {
1108 final ToastRecord r = mToastQueue.get(i);
1109 if (r.pkg.equals(pkg)) {
1110 count++;
1111 if (count >= MAX_PACKAGE_NOTIFICATIONS) {
1112 Slog.e(TAG, "Package has already posted " + count
1113 + " toasts. Not showing more. Package=" + pkg);
1114 return;
1115 }
1116 }
1117 }
1118 }
1119
1120 record = new ToastRecord(callingPid, pkg, callback, duration);
1121 mToastQueue.add(record);
1122 index = mToastQueue.size() - 1;
1123 keepProcessAliveLocked(callingPid);
1124 }
1125 // If it's at index 0, it's the current toast. It doesn't matter if it's
1126 // new or just been updated. Call back and tell it to show itself.
1127 // If the callback fails, this will remove it from the list, so don't
1128 // assume that it's valid after this.
1129 if (index == 0) {
1130 showNextToastLocked();
1131 }
1132 } finally {
1133 Binder.restoreCallingIdentity(callingId);
1134 }
1135 }
1136 }
1137
1138 @Override
1139 public void cancelToast(String pkg, ITransientNotification callback) {
1140 Slog.i(TAG, "cancelToast pkg=" + pkg + " callback=" + callback);
1141
1142 if (pkg == null || callback == null) {
1143 Slog.e(TAG, "Not cancelling notification. pkg=" + pkg + " callback=" + callback);
1144 return ;
1145 }
1146
1147 synchronized (mToastQueue) {
1148 long callingId = Binder.clearCallingIdentity();
1149 try {
1150 int index = indexOfToastLocked(pkg, callback);
1151 if (index >= 0) {
1152 cancelToastLocked(index);
1153 } else {
1154 Slog.w(TAG, "Toast already cancelled. pkg=" + pkg
1155 + " callback=" + callback);
1156 }
1157 } finally {
1158 Binder.restoreCallingIdentity(callingId);
1159 }
1160 }
1161 }
1162
1163 @Override
Christoph Studer8fd7f1e2014-04-11 17:35:05 -04001164 public void enqueueNotificationWithTag(String pkg, String opPkg, String tag, int id,
Adam Lesinski182f73f2013-12-05 16:48:06 -08001165 Notification notification, int[] idOut, int userId) throws RemoteException {
Christoph Studer8fd7f1e2014-04-11 17:35:05 -04001166 enqueueNotificationInternal(pkg, opPkg, Binder.getCallingUid(),
Adam Lesinski182f73f2013-12-05 16:48:06 -08001167 Binder.getCallingPid(), tag, id, notification, idOut, userId);
1168 }
1169
1170 @Override
1171 public void cancelNotificationWithTag(String pkg, String tag, int id, int userId) {
John Spurlock7340fc82014-04-24 18:50:12 -04001172 checkCallerIsSystemOrSameApp(pkg);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001173 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1174 Binder.getCallingUid(), userId, true, false, "cancelNotificationWithTag", pkg);
1175 // Don't allow client applications to cancel foreground service notis.
John Spurlocke6a7d932014-03-13 12:29:00 -04001176 cancelNotification(Binder.getCallingUid(), Binder.getCallingPid(), pkg, tag, id, 0,
Adam Lesinski182f73f2013-12-05 16:48:06 -08001177 Binder.getCallingUid() == Process.SYSTEM_UID
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04001178 ? 0 : Notification.FLAG_FOREGROUND_SERVICE, false, userId,
Chris Wren9fa689f2015-11-20 16:44:53 -05001179 REASON_APP_CANCEL, null);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001180 }
1181
1182 @Override
1183 public void cancelAllNotifications(String pkg, int userId) {
John Spurlock7340fc82014-04-24 18:50:12 -04001184 checkCallerIsSystemOrSameApp(pkg);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001185
1186 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1187 Binder.getCallingUid(), userId, true, false, "cancelAllNotifications", pkg);
1188
1189 // Calling from user space, don't allow the canceling of actively
1190 // running foreground services.
John Spurlocke6a7d932014-03-13 12:29:00 -04001191 cancelAllNotificationsInt(Binder.getCallingUid(), Binder.getCallingPid(),
1192 pkg, 0, Notification.FLAG_FOREGROUND_SERVICE, true, userId,
Chris Wren9fa689f2015-11-20 16:44:53 -05001193 REASON_APP_CANCEL_ALL, null);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001194 }
1195
1196 @Override
1197 public void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) {
John Spurlock7340fc82014-04-24 18:50:12 -04001198 checkCallerIsSystem();
Adam Lesinski182f73f2013-12-05 16:48:06 -08001199
1200 setNotificationsEnabledForPackageImpl(pkg, uid, enabled);
1201 }
1202
1203 /**
1204 * Use this when you just want to know if notifications are OK for this package.
1205 */
1206 @Override
1207 public boolean areNotificationsEnabledForPackage(String pkg, int uid) {
John Spurlock7340fc82014-04-24 18:50:12 -04001208 checkCallerIsSystem();
Adam Lesinski182f73f2013-12-05 16:48:06 -08001209 return (mAppOps.checkOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
1210 == AppOpsManager.MODE_ALLOWED);
1211 }
1212
Chris Wren54bbef42014-07-09 18:37:56 -04001213 @Override
Julia Reynolds233a5f92015-10-19 13:51:23 -04001214 public ParceledListSlice<Notification.Topic> getTopics(String pkg, int uid) {
Chris Wren54bbef42014-07-09 18:37:56 -04001215 checkCallerIsSystem();
Julia Reynolds233a5f92015-10-19 13:51:23 -04001216 return new ParceledListSlice<Notification.Topic>(mRankingHelper.getTopics(pkg, uid));
1217 }
1218
1219 @Override
1220 public void setTopicPriority(String pkg, int uid, Notification.Topic topic, int priority) {
1221 checkCallerIsSystem();
1222 mRankingHelper.setTopicPriority(pkg, uid, topic, priority);
Chris Wren54bbef42014-07-09 18:37:56 -04001223 savePolicyFile();
1224 }
1225
1226 @Override
Julia Reynolds233a5f92015-10-19 13:51:23 -04001227 public int getTopicPriority(String pkg, int uid, Notification.Topic topic) {
Chris Wren54bbef42014-07-09 18:37:56 -04001228 checkCallerIsSystem();
Julia Reynolds233a5f92015-10-19 13:51:23 -04001229 return mRankingHelper.getTopicPriority(pkg, uid, topic);
Chris Wren54bbef42014-07-09 18:37:56 -04001230 }
1231
Chris Wren3ad4e3a2014-09-02 17:23:51 -04001232 @Override
Julia Reynolds233a5f92015-10-19 13:51:23 -04001233 public void setTopicVisibilityOverride(String pkg, int uid, Notification.Topic topic,
1234 int visibility) {
Chris Wren3ad4e3a2014-09-02 17:23:51 -04001235 checkCallerIsSystem();
Julia Reynolds233a5f92015-10-19 13:51:23 -04001236 mRankingHelper.setTopicVisibilityOverride(pkg, uid, topic, visibility);
Chris Wren3ad4e3a2014-09-02 17:23:51 -04001237 savePolicyFile();
1238 }
1239
1240 @Override
Julia Reynolds233a5f92015-10-19 13:51:23 -04001241 public int getTopicVisibilityOverride(String pkg, int uid, Notification.Topic topic) {
Chris Wren3ad4e3a2014-09-02 17:23:51 -04001242 checkCallerIsSystem();
Julia Reynolds233a5f92015-10-19 13:51:23 -04001243 return mRankingHelper.getTopicVisibilityOverride(pkg, uid, topic);
Chris Wren3ad4e3a2014-09-02 17:23:51 -04001244 }
1245
Julia Reynolds5d25ee72015-11-20 15:38:20 -05001246 @Override
1247 public void setTopicImportance(String pkg, int uid, Notification.Topic topic,
1248 int importance) {
1249 checkCallerIsSystem();
1250 mRankingHelper.setTopicImportance(pkg, uid, topic, importance);
1251 savePolicyFile();
1252 }
1253
1254 @Override
1255 public int getTopicImportance(String pkg, int uid, Notification.Topic topic) {
1256 checkCallerIsSystem();
1257 return mRankingHelper.getTopicImportance(pkg, uid, topic);
1258 }
1259
Adam Lesinski182f73f2013-12-05 16:48:06 -08001260 /**
1261 * System-only API for getting a list of current (i.e. not cleared) notifications.
1262 *
1263 * Requires ACCESS_NOTIFICATIONS which is signature|system.
Chris Wrenf9536642014-04-17 10:01:54 -04001264 * @returns A list of all the notifications, in natural order.
Adam Lesinski182f73f2013-12-05 16:48:06 -08001265 */
1266 @Override
1267 public StatusBarNotification[] getActiveNotifications(String callingPkg) {
1268 // enforce() will ensure the calling uid has the correct permission
1269 getContext().enforceCallingOrSelfPermission(
1270 android.Manifest.permission.ACCESS_NOTIFICATIONS,
1271 "NotificationManagerService.getActiveNotifications");
1272
1273 StatusBarNotification[] tmp = null;
1274 int uid = Binder.getCallingUid();
1275
1276 // noteOp will check to make sure the callingPkg matches the uid
1277 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
1278 == AppOpsManager.MODE_ALLOWED) {
1279 synchronized (mNotificationList) {
1280 tmp = new StatusBarNotification[mNotificationList.size()];
1281 final int N = mNotificationList.size();
1282 for (int i=0; i<N; i++) {
1283 tmp[i] = mNotificationList.get(i).sbn;
1284 }
1285 }
1286 }
1287 return tmp;
1288 }
1289
1290 /**
Dan Sandler994349c2015-04-15 11:02:54 -04001291 * Public API for getting a list of current notifications for the calling package/uid.
1292 *
1293 * @returns A list of all the package's notifications, in natural order.
1294 */
1295 @Override
1296 public ParceledListSlice<StatusBarNotification> getAppActiveNotifications(String pkg,
1297 int incomingUserId) {
1298 checkCallerIsSystemOrSameApp(pkg);
1299 int userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1300 Binder.getCallingUid(), incomingUserId, true, false,
1301 "getAppActiveNotifications", pkg);
1302
Erik Wolsheimer2242b4d2015-11-24 13:22:04 -08001303 final ArrayList<StatusBarNotification> list
1304 = new ArrayList<StatusBarNotification>(mNotificationList.size());
Dan Sandler994349c2015-04-15 11:02:54 -04001305
1306 synchronized (mNotificationList) {
Erik Wolsheimer2242b4d2015-11-24 13:22:04 -08001307 final int N = mNotificationList.size();
Dan Sandler994349c2015-04-15 11:02:54 -04001308 for (int i = 0; i < N; i++) {
1309 final StatusBarNotification sbn = mNotificationList.get(i).sbn;
1310 if (sbn.getPackageName().equals(pkg) && sbn.getUserId() == userId) {
1311 // We could pass back a cloneLight() but clients might get confused and
1312 // try to send this thing back to notify() again, which would not work
1313 // very well.
1314 final StatusBarNotification sbnOut = new StatusBarNotification(
1315 sbn.getPackageName(),
1316 sbn.getOpPkg(),
1317 sbn.getId(), sbn.getTag(), sbn.getUid(), sbn.getInitialPid(),
1318 0, // hide score from apps
1319 sbn.getNotification().clone(),
1320 sbn.getUser(), sbn.getPostTime());
1321 list.add(sbnOut);
1322 }
1323 }
1324 }
1325
1326 return new ParceledListSlice<StatusBarNotification>(list);
1327 }
1328
1329 /**
Adam Lesinski182f73f2013-12-05 16:48:06 -08001330 * System-only API for getting a list of recent (cleared, no longer shown) notifications.
1331 *
1332 * Requires ACCESS_NOTIFICATIONS which is signature|system.
1333 */
1334 @Override
1335 public StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count) {
1336 // enforce() will ensure the calling uid has the correct permission
1337 getContext().enforceCallingOrSelfPermission(
1338 android.Manifest.permission.ACCESS_NOTIFICATIONS,
1339 "NotificationManagerService.getHistoricalNotifications");
1340
1341 StatusBarNotification[] tmp = null;
1342 int uid = Binder.getCallingUid();
1343
1344 // noteOp will check to make sure the callingPkg matches the uid
1345 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
1346 == AppOpsManager.MODE_ALLOWED) {
1347 synchronized (mArchive) {
1348 tmp = mArchive.getArray(count);
1349 }
1350 }
1351 return tmp;
1352 }
1353
1354 /**
1355 * Register a listener binder directly with the notification manager.
1356 *
1357 * Only works with system callers. Apps should extend
1358 * {@link android.service.notification.NotificationListenerService}.
1359 */
1360 @Override
1361 public void registerListener(final INotificationListener listener,
1362 final ComponentName component, final int userid) {
Christoph Studer3e144d32014-05-22 16:48:40 +02001363 enforceSystemOrSystemUI("INotificationManager.registerListener");
John Spurlock7340fc82014-04-24 18:50:12 -04001364 mListeners.registerService(listener, component, userid);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001365 }
1366
1367 /**
1368 * Remove a listener binder directly
1369 */
1370 @Override
1371 public void unregisterListener(INotificationListener listener, int userid) {
John Spurlock7340fc82014-04-24 18:50:12 -04001372 mListeners.unregisterService(listener, userid);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001373 }
1374
1375 /**
1376 * Allow an INotificationListener to simulate a "clear all" operation.
1377 *
1378 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onClearAllNotifications}
1379 *
1380 * @param token The binder for the listener, to check that the caller is allowed
1381 */
1382 @Override
John Spurlocka4294292014-03-24 18:02:32 -04001383 public void cancelNotificationsFromListener(INotificationListener token, String[] keys) {
John Spurlocke6a7d932014-03-13 12:29:00 -04001384 final int callingUid = Binder.getCallingUid();
1385 final int callingPid = Binder.getCallingPid();
Adam Lesinski182f73f2013-12-05 16:48:06 -08001386 long identity = Binder.clearCallingIdentity();
1387 try {
Adam Lesinskie8240262014-03-26 16:01:00 -07001388 synchronized (mNotificationList) {
John Spurlock7340fc82014-04-24 18:50:12 -04001389 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
John Spurlocka4294292014-03-24 18:02:32 -04001390 if (keys != null) {
1391 final int N = keys.length;
1392 for (int i = 0; i < N; i++) {
1393 NotificationRecord r = mNotificationsByKey.get(keys[i]);
Griff Hazen335e1f02014-09-11 14:49:31 -07001394 if (r == null) continue;
Kenny Guya263e4e2014-03-03 18:24:03 +00001395 final int userId = r.sbn.getUserId();
1396 if (userId != info.userid && userId != UserHandle.USER_ALL &&
John Spurlockb408e8e2014-04-23 21:12:45 -04001397 !mUserProfiles.isCurrentProfile(userId)) {
Kenny Guya263e4e2014-03-03 18:24:03 +00001398 throw new SecurityException("Disallowed call from listener: "
John Spurlock7340fc82014-04-24 18:50:12 -04001399 + info.service);
Kenny Guya263e4e2014-03-03 18:24:03 +00001400 }
Griff Hazen335e1f02014-09-11 14:49:31 -07001401 cancelNotificationFromListenerLocked(info, callingUid, callingPid,
1402 r.sbn.getPackageName(), r.sbn.getTag(), r.sbn.getId(),
1403 userId);
John Spurlocka4294292014-03-24 18:02:32 -04001404 }
1405 } else {
1406 cancelAllLocked(callingUid, callingPid, info.userid,
Kenny Guya263e4e2014-03-03 18:24:03 +00001407 REASON_LISTENER_CANCEL_ALL, info, info.supportsProfiles());
John Spurlocka4294292014-03-24 18:02:32 -04001408 }
Adam Lesinskie8240262014-03-26 16:01:00 -07001409 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001410 } finally {
1411 Binder.restoreCallingIdentity(identity);
1412 }
1413 }
1414
Amith Yamasanif47e51e2015-04-17 10:02:15 -07001415 @Override
1416 public void setNotificationsShownFromListener(INotificationListener token, String[] keys) {
Amith Yamasanif47e51e2015-04-17 10:02:15 -07001417 long identity = Binder.clearCallingIdentity();
1418 try {
1419 synchronized (mNotificationList) {
1420 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1421 if (keys != null) {
1422 final int N = keys.length;
1423 for (int i = 0; i < N; i++) {
1424 NotificationRecord r = mNotificationsByKey.get(keys[i]);
1425 if (r == null) continue;
1426 final int userId = r.sbn.getUserId();
1427 if (userId != info.userid && userId != UserHandle.USER_ALL &&
1428 !mUserProfiles.isCurrentProfile(userId)) {
1429 throw new SecurityException("Disallowed call from listener: "
1430 + info.service);
1431 }
1432 if (!r.isSeen()) {
1433 if (DBG) Slog.d(TAG, "Marking notification as seen " + keys[i]);
1434 mAppUsageStats.reportEvent(r.sbn.getPackageName(),
Xiaohui Chenddbe4ca2015-08-13 16:20:56 -07001435 userId == UserHandle.USER_ALL ? UserHandle.USER_SYSTEM
Amith Yamasanif47e51e2015-04-17 10:02:15 -07001436 : userId,
Adam Lesinskic8e87292015-06-10 15:33:45 -07001437 UsageEvents.Event.USER_INTERACTION);
Amith Yamasanif47e51e2015-04-17 10:02:15 -07001438 r.setSeen();
1439 }
1440 }
1441 }
1442 }
1443 } finally {
1444 Binder.restoreCallingIdentity(identity);
1445 }
1446 }
1447
John Spurlock7340fc82014-04-24 18:50:12 -04001448 private void cancelNotificationFromListenerLocked(ManagedServiceInfo info,
Kenny Guya263e4e2014-03-03 18:24:03 +00001449 int callingUid, int callingPid, String pkg, String tag, int id, int userId) {
John Spurlocka4294292014-03-24 18:02:32 -04001450 cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
1451 Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
1452 true,
Kenny Guya263e4e2014-03-03 18:24:03 +00001453 userId, REASON_LISTENER_CANCEL, info);
John Spurlocka4294292014-03-24 18:02:32 -04001454 }
1455
Adam Lesinski182f73f2013-12-05 16:48:06 -08001456 /**
1457 * Allow an INotificationListener to simulate clearing (dismissing) a single notification.
1458 *
1459 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onNotificationClear}
1460 *
1461 * @param token The binder for the listener, to check that the caller is allowed
1462 */
1463 @Override
1464 public void cancelNotificationFromListener(INotificationListener token, String pkg,
1465 String tag, int id) {
John Spurlocke6a7d932014-03-13 12:29:00 -04001466 final int callingUid = Binder.getCallingUid();
1467 final int callingPid = Binder.getCallingPid();
Adam Lesinski182f73f2013-12-05 16:48:06 -08001468 long identity = Binder.clearCallingIdentity();
1469 try {
Adam Lesinskie8240262014-03-26 16:01:00 -07001470 synchronized (mNotificationList) {
John Spurlock7340fc82014-04-24 18:50:12 -04001471 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
Kenny Guya263e4e2014-03-03 18:24:03 +00001472 if (info.supportsProfiles()) {
1473 Log.e(TAG, "Ignoring deprecated cancelNotification(pkg, tag, id) "
1474 + "from " + info.component
1475 + " use cancelNotification(key) instead.");
1476 } else {
1477 cancelNotificationFromListenerLocked(info, callingUid, callingPid,
1478 pkg, tag, id, info.userid);
1479 }
Adam Lesinskie8240262014-03-26 16:01:00 -07001480 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001481 } finally {
1482 Binder.restoreCallingIdentity(identity);
1483 }
1484 }
1485
1486 /**
1487 * Allow an INotificationListener to request the list of outstanding notifications seen by
1488 * the current user. Useful when starting up, after which point the listener callbacks
1489 * should be used.
1490 *
1491 * @param token The binder for the listener, to check that the caller is allowed
Dan Sandlerea75fdd2014-08-12 12:29:19 -04001492 * @param keys An array of notification keys to fetch, or null to fetch everything
Chris Wrenf9536642014-04-17 10:01:54 -04001493 * @returns The return value will contain the notifications specified in keys, in that
1494 * order, or if keys is null, all the notifications, in natural order.
Adam Lesinski182f73f2013-12-05 16:48:06 -08001495 */
1496 @Override
Christoph Studercee44ba2014-05-20 18:36:43 +02001497 public ParceledListSlice<StatusBarNotification> getActiveNotificationsFromListener(
Christoph Studerb82bc782014-08-20 14:29:43 +02001498 INotificationListener token, String[] keys, int trim) {
Adam Lesinski182f73f2013-12-05 16:48:06 -08001499 synchronized (mNotificationList) {
John Spurlock7340fc82014-04-24 18:50:12 -04001500 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
Dan Sandlerea75fdd2014-08-12 12:29:19 -04001501 final boolean getKeys = keys != null;
1502 final int N = getKeys ? keys.length : mNotificationList.size();
Christoph Studerb82bc782014-08-20 14:29:43 +02001503 final ArrayList<StatusBarNotification> list
1504 = new ArrayList<StatusBarNotification>(N);
Christoph Studercee44ba2014-05-20 18:36:43 +02001505 for (int i=0; i<N; i++) {
Dan Sandlerea75fdd2014-08-12 12:29:19 -04001506 final NotificationRecord r = getKeys
1507 ? mNotificationsByKey.get(keys[i])
1508 : mNotificationList.get(i);
Christoph Studerb82bc782014-08-20 14:29:43 +02001509 if (r == null) continue;
1510 StatusBarNotification sbn = r.sbn;
1511 if (!isVisibleToListener(sbn, info)) continue;
1512 StatusBarNotification sbnToSend =
1513 (trim == TRIM_FULL) ? sbn : sbn.cloneLight();
1514 list.add(sbnToSend);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001515 }
Christoph Studercee44ba2014-05-20 18:36:43 +02001516 return new ParceledListSlice<StatusBarNotification>(list);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001517 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001518 }
1519
1520 @Override
John Spurlockd8afe3c2014-08-01 14:04:07 -04001521 public void requestHintsFromListener(INotificationListener token, int hints) {
1522 final long identity = Binder.clearCallingIdentity();
1523 try {
1524 synchronized (mNotificationList) {
1525 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1526 final boolean disableEffects = (hints & HINT_HOST_DISABLE_EFFECTS) != 0;
1527 if (disableEffects) {
1528 mListenersDisablingEffects.add(info);
1529 } else {
1530 mListenersDisablingEffects.remove(info);
1531 }
John Spurlockd8afe3c2014-08-01 14:04:07 -04001532 updateListenerHintsLocked();
John Spurlockb4782522014-08-22 14:54:46 -04001533 updateEffectsSuppressorLocked();
John Spurlock1fa865f2014-07-21 14:56:39 -04001534 }
John Spurlockd8afe3c2014-08-01 14:04:07 -04001535 } finally {
1536 Binder.restoreCallingIdentity(identity);
John Spurlock1fa865f2014-07-21 14:56:39 -04001537 }
1538 }
1539
1540 @Override
John Spurlockd8afe3c2014-08-01 14:04:07 -04001541 public int getHintsFromListener(INotificationListener token) {
John Spurlock1fa865f2014-07-21 14:56:39 -04001542 synchronized (mNotificationList) {
John Spurlockd8afe3c2014-08-01 14:04:07 -04001543 return mListenerHints;
John Spurlock1fa865f2014-07-21 14:56:39 -04001544 }
1545 }
1546
1547 @Override
Christoph Studer85a384b2014-08-27 20:16:15 +02001548 public void requestInterruptionFilterFromListener(INotificationListener token,
1549 int interruptionFilter) throws RemoteException {
1550 final long identity = Binder.clearCallingIdentity();
1551 try {
1552 synchronized (mNotificationList) {
John Spurlock661f2cf2014-11-17 10:29:10 -05001553 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1554 mZenModeHelper.requestFromListener(info.component, interruptionFilter);
Christoph Studer85a384b2014-08-27 20:16:15 +02001555 updateInterruptionFilterLocked();
1556 }
1557 } finally {
1558 Binder.restoreCallingIdentity(identity);
1559 }
1560 }
1561
1562 @Override
1563 public int getInterruptionFilterFromListener(INotificationListener token)
1564 throws RemoteException {
1565 synchronized (mNotificationLight) {
1566 return mInterruptionFilter;
1567 }
1568 }
1569
1570 @Override
Christoph Studerb82bc782014-08-20 14:29:43 +02001571 public void setOnNotificationPostedTrimFromListener(INotificationListener token, int trim)
1572 throws RemoteException {
1573 synchronized (mNotificationList) {
1574 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1575 if (info == null) return;
1576 mListeners.setOnNotificationPostedTrimLocked(info, trim);
1577 }
1578 }
1579
1580 @Override
John Spurlockb2278d62015-04-07 12:47:12 -04001581 public int getZenMode() {
1582 return mZenModeHelper.getZenMode();
1583 }
1584
1585 @Override
John Spurlock056c5192014-04-20 21:52:01 -04001586 public ZenModeConfig getZenModeConfig() {
John Spurlockcdb57ae2015-02-11 19:04:11 -05001587 enforceSystemOrSystemUIOrVolume("INotificationManager.getZenModeConfig");
John Spurlock056c5192014-04-20 21:52:01 -04001588 return mZenModeHelper.getConfig();
1589 }
1590
1591 @Override
John Spurlockb2278d62015-04-07 12:47:12 -04001592 public void setZenMode(int mode, Uri conditionId, String reason) throws RemoteException {
John Spurlockcdb57ae2015-02-11 19:04:11 -05001593 enforceSystemOrSystemUIOrVolume("INotificationManager.setZenMode");
1594 final long identity = Binder.clearCallingIdentity();
1595 try {
John Spurlockb2278d62015-04-07 12:47:12 -04001596 mZenModeHelper.setManualZenMode(mode, conditionId, reason);
John Spurlockcdb57ae2015-02-11 19:04:11 -05001597 } finally {
1598 Binder.restoreCallingIdentity(identity);
1599 }
1600 }
1601
1602 @Override
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04001603 public List<AutomaticZenRule> getAutomaticZenRules() throws RemoteException {
1604 enforcePolicyAccess(Binder.getCallingUid(), "getAutomaticZenRules");
1605 return mZenModeHelper.getAutomaticZenRules();
1606 }
1607
1608 @Override
Julia Reynolds4fe98d62015-10-06 16:23:41 -04001609 public AutomaticZenRule getAutomaticZenRule(String id) throws RemoteException {
1610 Preconditions.checkNotNull(id, "Id is null");
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04001611 enforcePolicyAccess(Binder.getCallingUid(), "getAutomaticZenRule");
Julia Reynolds4fe98d62015-10-06 16:23:41 -04001612 return mZenModeHelper.getAutomaticZenRule(id);
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04001613 }
1614
1615 @Override
Julia Reynolds4fe98d62015-10-06 16:23:41 -04001616 public AutomaticZenRule addAutomaticZenRule(AutomaticZenRule automaticZenRule)
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04001617 throws RemoteException {
1618 Preconditions.checkNotNull(automaticZenRule, "automaticZenRule is null");
1619 Preconditions.checkNotNull(automaticZenRule.getName(), "Name is null");
1620 Preconditions.checkNotNull(automaticZenRule.getOwner(), "Owner is null");
1621 Preconditions.checkNotNull(automaticZenRule.getConditionId(), "ConditionId is null");
Julia Reynolds4fe98d62015-10-06 16:23:41 -04001622 enforcePolicyAccess(Binder.getCallingUid(), "addAutomaticZenRule");
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04001623
Julia Reynolds4fe98d62015-10-06 16:23:41 -04001624 return mZenModeHelper.addAutomaticZenRule(automaticZenRule,
1625 "addAutomaticZenRule");
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04001626 }
1627
1628 @Override
Julia Reynolds4fe98d62015-10-06 16:23:41 -04001629 public boolean updateAutomaticZenRule(AutomaticZenRule automaticZenRule)
1630 throws RemoteException {
1631 Preconditions.checkNotNull(automaticZenRule, "automaticZenRule is null");
1632 Preconditions.checkNotNull(automaticZenRule.getName(), "Name is null");
1633 Preconditions.checkNotNull(automaticZenRule.getOwner(), "Owner is null");
1634 Preconditions.checkNotNull(automaticZenRule.getConditionId(), "ConditionId is null");
1635 enforcePolicyAccess(Binder.getCallingUid(), "updateAutomaticZenRule");
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04001636
Julia Reynolds4fe98d62015-10-06 16:23:41 -04001637 return mZenModeHelper.updateAutomaticZenRule(automaticZenRule,
1638 "updateAutomaticZenRule");
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04001639 }
1640
1641 @Override
Julia Reynolds4fe98d62015-10-06 16:23:41 -04001642 public boolean removeAutomaticZenRule(String id) throws RemoteException {
1643 Preconditions.checkNotNull(id, "Id is null");
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04001644 // Verify that they can modify zen rules.
1645 enforcePolicyAccess(Binder.getCallingUid(), "removeAutomaticZenRule");
1646
Julia Reynolds4fe98d62015-10-06 16:23:41 -04001647 return mZenModeHelper.removeAutomaticZenRule(id, "removeAutomaticZenRule");
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04001648 }
1649
1650 @Override
Julia Reynoldsc8e54e82015-11-30 16:43:05 -05001651 public boolean removeAutomaticZenRules(String packageName) throws RemoteException {
1652 Preconditions.checkNotNull(packageName, "Package name is null");
1653 enforceSystemOrSystemUI("removeAutomaticZenRules");
1654
1655 return mZenModeHelper.removeAutomaticZenRules(packageName, "removeAutomaticZenRules");
1656 }
1657
1658 @Override
John Spurlock80774932015-05-07 17:38:50 -04001659 public void setInterruptionFilter(String pkg, int filter) throws RemoteException {
1660 enforcePolicyAccess(pkg, "setInterruptionFilter");
1661 final int zen = NotificationManager.zenModeFromInterruptionFilter(filter, -1);
1662 if (zen == -1) throw new IllegalArgumentException("Invalid filter: " + filter);
1663 final long identity = Binder.clearCallingIdentity();
1664 try {
1665 mZenModeHelper.setManualZenMode(zen, null, "setInterruptionFilter");
1666 } finally {
1667 Binder.restoreCallingIdentity(identity);
1668 }
1669 }
1670
1671 @Override
John Spurlocka7d92b12015-05-13 14:48:02 -04001672 public void notifyConditions(final String pkg, IConditionProvider provider,
1673 final Condition[] conditions) {
John Spurlocke77bb362014-04-26 10:24:59 -04001674 final ManagedServiceInfo info = mConditionProviders.checkServiceToken(provider);
1675 checkCallerIsSystemOrSameApp(pkg);
John Spurlocka7d92b12015-05-13 14:48:02 -04001676 mHandler.post(new Runnable() {
1677 @Override
1678 public void run() {
1679 mConditionProviders.notifyConditions(pkg, info, conditions);
1680 }
1681 });
John Spurlocke77bb362014-04-26 10:24:59 -04001682 }
1683
John Spurlockcdb57ae2015-02-11 19:04:11 -05001684 private void enforceSystemOrSystemUIOrVolume(String message) {
1685 if (mAudioManagerInternal != null) {
1686 final int vcuid = mAudioManagerInternal.getVolumeControllerUid();
1687 if (vcuid > 0 && Binder.getCallingUid() == vcuid) {
1688 return;
1689 }
1690 }
1691 enforceSystemOrSystemUI(message);
1692 }
1693
John Spurlocke77bb362014-04-26 10:24:59 -04001694 private void enforceSystemOrSystemUI(String message) {
1695 if (isCallerSystem()) return;
1696 getContext().enforceCallingPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
1697 message);
John Spurlock7340fc82014-04-24 18:50:12 -04001698 }
1699
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04001700 private void enforcePolicyAccess(int uid, String method) {
1701 if (PackageManager.PERMISSION_GRANTED == getContext().checkCallingPermission(
1702 android.Manifest.permission.MANAGE_NOTIFICATIONS)) {
1703 return;
1704 }
1705 boolean accessAllowed = false;
1706 String[] packages = getContext().getPackageManager().getPackagesForUid(uid);
1707 final int packageCount = packages.length;
1708 for (int i = 0; i < packageCount; i++) {
1709 if (checkPolicyAccess(packages[i])) {
1710 accessAllowed = true;
1711 }
1712 }
1713 if (!accessAllowed) {
1714 Slog.w(TAG, "Notification policy access denied calling " + method);
1715 throw new SecurityException("Notification policy access denied");
1716 }
1717 }
1718
John Spurlock80774932015-05-07 17:38:50 -04001719 private void enforcePolicyAccess(String pkg, String method) {
Julia Reynolds6ee26172015-09-28 11:34:48 -04001720 if (PackageManager.PERMISSION_GRANTED == getContext().checkCallingPermission(
1721 android.Manifest.permission.MANAGE_NOTIFICATIONS)) {
1722 return;
1723 }
John Spurlock80774932015-05-07 17:38:50 -04001724 if (!checkPolicyAccess(pkg)) {
1725 Slog.w(TAG, "Notification policy access denied calling " + method);
1726 throw new SecurityException("Notification policy access denied");
John Spurlock1fc476d2015-04-14 16:05:20 -04001727 }
1728 }
1729
John Spurlock80774932015-05-07 17:38:50 -04001730 private boolean checkPackagePolicyAccess(String pkg) {
John Spurlock7c74f782015-06-04 13:01:42 -04001731 return mPolicyAccess.isPackageGranted(pkg);
John Spurlock80774932015-05-07 17:38:50 -04001732 }
1733
1734 private boolean checkPolicyAccess(String pkg) {
1735 return checkPackagePolicyAccess(pkg) || mListeners.isComponentEnabledForPackage(pkg);
John Spurlock1fc476d2015-04-14 16:05:20 -04001736 }
1737
John Spurlock7340fc82014-04-24 18:50:12 -04001738 @Override
Adam Lesinski182f73f2013-12-05 16:48:06 -08001739 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1740 if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
1741 != PackageManager.PERMISSION_GRANTED) {
John Spurlock2b122f42014-08-27 16:29:47 -04001742 pw.println("Permission Denial: can't dump NotificationManager from pid="
Adam Lesinski182f73f2013-12-05 16:48:06 -08001743 + Binder.getCallingPid()
1744 + ", uid=" + Binder.getCallingUid());
1745 return;
1746 }
1747
Chris Wrene4b38802015-07-07 15:54:19 -04001748 final DumpFilter filter = DumpFilter.parseFromArguments(args);
1749 if (filter != null && filter.stats) {
1750 dumpJson(pw, filter);
1751 } else {
1752 dumpImpl(pw, filter);
1753 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001754 }
John Spurlockb4782522014-08-22 14:54:46 -04001755
1756 @Override
1757 public ComponentName getEffectsSuppressor() {
John Spurlockcdb57ae2015-02-11 19:04:11 -05001758 enforceSystemOrSystemUIOrVolume("INotificationManager.getEffectsSuppressor");
John Spurlockb4782522014-08-22 14:54:46 -04001759 return mEffectsSuppressor;
1760 }
John Spurlock2b122f42014-08-27 16:29:47 -04001761
1762 @Override
1763 public boolean matchesCallFilter(Bundle extras) {
1764 enforceSystemOrSystemUI("INotificationManager.matchesCallFilter");
Christoph Studer12aeda82014-09-23 19:08:56 +02001765 return mZenModeHelper.matchesCallFilter(
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07001766 Binder.getCallingUserHandle(),
Christoph Studer12aeda82014-09-23 19:08:56 +02001767 extras,
1768 mRankingHelper.findExtractor(ValidateNotificationPeople.class),
1769 MATCHES_CALL_FILTER_CONTACTS_TIMEOUT_MS,
1770 MATCHES_CALL_FILTER_TIMEOUT_AFFINITY);
John Spurlock2b122f42014-08-27 16:29:47 -04001771 }
John Spurlock530052a2014-11-30 16:26:19 -05001772
1773 @Override
1774 public boolean isSystemConditionProviderEnabled(String path) {
John Spurlockcdb57ae2015-02-11 19:04:11 -05001775 enforceSystemOrSystemUIOrVolume("INotificationManager.isSystemConditionProviderEnabled");
John Spurlockb2278d62015-04-07 12:47:12 -04001776 return mConditionProviders.isSystemProviderEnabled(path);
John Spurlock530052a2014-11-30 16:26:19 -05001777 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001778
Christopher Tatef9767d62015-04-08 14:35:43 -07001779 // Backup/restore interface
1780 @Override
1781 public byte[] getBackupPayload(int user) {
John Spurlock35ef0a62015-05-28 11:24:10 -04001782 if (DBG) Slog.d(TAG, "getBackupPayload u=" + user);
Xiaohui Chenddbe4ca2015-08-13 16:20:56 -07001783 //TODO: http://b/22388012
1784 if (user != UserHandle.USER_SYSTEM) {
John Spurlock35ef0a62015-05-28 11:24:10 -04001785 Slog.w(TAG, "getBackupPayload: cannot backup policy for user " + user);
1786 return null;
1787 }
1788 final ByteArrayOutputStream baos = new ByteArrayOutputStream();
1789 try {
1790 writePolicyXml(baos, true /*forBackup*/);
1791 return baos.toByteArray();
1792 } catch (IOException e) {
1793 Slog.w(TAG, "getBackupPayload: error writing payload for user " + user, e);
1794 }
Christopher Tatef9767d62015-04-08 14:35:43 -07001795 return null;
1796 }
1797
1798 @Override
1799 public void applyRestore(byte[] payload, int user) {
John Spurlock35ef0a62015-05-28 11:24:10 -04001800 if (DBG) Slog.d(TAG, "applyRestore u=" + user + " payload="
1801 + (payload != null ? new String(payload, StandardCharsets.UTF_8) : null));
1802 if (payload == null) {
1803 Slog.w(TAG, "applyRestore: no payload to restore for user " + user);
1804 return;
1805 }
Xiaohui Chenddbe4ca2015-08-13 16:20:56 -07001806 //TODO: http://b/22388012
1807 if (user != UserHandle.USER_SYSTEM) {
John Spurlock35ef0a62015-05-28 11:24:10 -04001808 Slog.w(TAG, "applyRestore: cannot restore policy for user " + user);
1809 return;
1810 }
1811 final ByteArrayInputStream bais = new ByteArrayInputStream(payload);
1812 try {
1813 readPolicyXml(bais, true /*forRestore*/);
1814 savePolicyFile();
1815 } catch (NumberFormatException | XmlPullParserException | IOException e) {
1816 Slog.w(TAG, "applyRestore: error reading payload", e);
1817 }
Christopher Tatef9767d62015-04-08 14:35:43 -07001818 }
1819
John Spurlock1fc476d2015-04-14 16:05:20 -04001820 @Override
John Spurlock80774932015-05-07 17:38:50 -04001821 public boolean isNotificationPolicyAccessGranted(String pkg) {
1822 return checkPolicyAccess(pkg);
John Spurlock1fc476d2015-04-14 16:05:20 -04001823 }
1824
1825 @Override
John Spurlock80774932015-05-07 17:38:50 -04001826 public boolean isNotificationPolicyAccessGrantedForPackage(String pkg) {
1827 enforceSystemOrSystemUI("request policy access status for another package");
1828 return checkPackagePolicyAccess(pkg);
1829 }
1830
1831 @Override
1832 public String[] getPackagesRequestingNotificationPolicyAccess()
1833 throws RemoteException {
1834 enforceSystemOrSystemUI("request policy access packages");
1835 final long identity = Binder.clearCallingIdentity();
1836 try {
John Spurlock7c74f782015-06-04 13:01:42 -04001837 return mPolicyAccess.getRequestingPackages();
John Spurlock80774932015-05-07 17:38:50 -04001838 } finally {
1839 Binder.restoreCallingIdentity(identity);
1840 }
1841 }
1842
1843 @Override
1844 public void setNotificationPolicyAccessGranted(String pkg, boolean granted)
1845 throws RemoteException {
1846 enforceSystemOrSystemUI("grant notification policy access");
1847 final long identity = Binder.clearCallingIdentity();
1848 try {
1849 synchronized (mNotificationList) {
1850 mPolicyAccess.put(pkg, granted);
1851 }
1852 } finally {
1853 Binder.restoreCallingIdentity(identity);
1854 }
1855 }
1856
1857 @Override
1858 public Policy getNotificationPolicy(String pkg) {
1859 enforcePolicyAccess(pkg, "getNotificationPolicy");
John Spurlock1fc476d2015-04-14 16:05:20 -04001860 final long identity = Binder.clearCallingIdentity();
1861 try {
1862 return mZenModeHelper.getNotificationPolicy();
1863 } finally {
1864 Binder.restoreCallingIdentity(identity);
1865 }
1866 }
1867
1868 @Override
John Spurlock80774932015-05-07 17:38:50 -04001869 public void setNotificationPolicy(String pkg, Policy policy) {
1870 enforcePolicyAccess(pkg, "setNotificationPolicy");
John Spurlock1fc476d2015-04-14 16:05:20 -04001871 final long identity = Binder.clearCallingIdentity();
1872 try {
1873 mZenModeHelper.setNotificationPolicy(policy);
1874 } finally {
1875 Binder.restoreCallingIdentity(identity);
1876 }
1877 }
1878 };
John Spurlocka4294292014-03-24 18:02:32 -04001879
John Spurlock32fe4c62014-10-02 12:16:02 -04001880 private String disableNotificationEffects(NotificationRecord record) {
1881 if (mDisableNotificationEffects) {
1882 return "booleanState";
1883 }
1884 if ((mListenerHints & HINT_HOST_DISABLE_EFFECTS) != 0) {
1885 return "listenerHints";
1886 }
1887 if (mCallState != TelephonyManager.CALL_STATE_IDLE && !mZenModeHelper.isCall(record)) {
1888 return "callState";
1889 }
1890 return null;
Chris Wrene4b38802015-07-07 15:54:19 -04001891 };
1892
1893 private void dumpJson(PrintWriter pw, DumpFilter filter) {
1894 JSONObject dump = new JSONObject();
1895 try {
1896 dump.put("service", "Notification Manager");
1897 JSONArray bans = new JSONArray();
1898 try {
1899 ArrayMap<Integer, ArrayList<String>> packageBans = getPackageBans(filter);
1900 for (Integer userId : packageBans.keySet()) {
1901 for (String packageName : packageBans.get(userId)) {
1902 JSONObject ban = new JSONObject();
1903 ban.put("userId", userId);
1904 ban.put("packageName", packageName);
1905 bans.put(ban);
1906 }
1907 }
1908 } catch (NameNotFoundException e) {
1909 // pass
1910 }
1911 dump.put("bans", bans);
1912 dump.put("stats", mUsageStats.dumpJson(filter));
1913 } catch (JSONException e) {
1914 e.printStackTrace();
1915 }
1916 pw.println(dump);
John Spurlock1fa865f2014-07-21 14:56:39 -04001917 }
1918
John Spurlock25e2d242014-06-27 13:58:23 -04001919 void dumpImpl(PrintWriter pw, DumpFilter filter) {
1920 pw.print("Current Notification Manager state");
Dan Sandlera1770312015-07-10 13:59:29 -04001921 if (filter.filtered) {
John Spurlock50806fc2014-07-15 10:22:02 -04001922 pw.print(" (filtered to "); pw.print(filter); pw.print(")");
John Spurlock25e2d242014-06-27 13:58:23 -04001923 }
1924 pw.println(':');
Adam Lesinski182f73f2013-12-05 16:48:06 -08001925 int N;
Julia Reynoldse6b53e62015-07-31 09:25:10 -04001926 final boolean zenOnly = filter.filtered && filter.zen;
Adam Lesinski182f73f2013-12-05 16:48:06 -08001927
John Spurlock50806fc2014-07-15 10:22:02 -04001928 if (!zenOnly) {
1929 synchronized (mToastQueue) {
1930 N = mToastQueue.size();
1931 if (N > 0) {
1932 pw.println(" Toast Queue:");
1933 for (int i=0; i<N; i++) {
1934 mToastQueue.get(i).dump(pw, " ", filter);
1935 }
1936 pw.println(" ");
Adam Lesinski182f73f2013-12-05 16:48:06 -08001937 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001938 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001939 }
1940
1941 synchronized (mNotificationList) {
John Spurlock50806fc2014-07-15 10:22:02 -04001942 if (!zenOnly) {
1943 N = mNotificationList.size();
John Spurlock25e2d242014-06-27 13:58:23 -04001944 if (N > 0) {
John Spurlock50806fc2014-07-15 10:22:02 -04001945 pw.println(" Notification List:");
John Spurlock25e2d242014-06-27 13:58:23 -04001946 for (int i=0; i<N; i++) {
John Spurlock50806fc2014-07-15 10:22:02 -04001947 final NotificationRecord nr = mNotificationList.get(i);
Julia Reynoldse6b53e62015-07-31 09:25:10 -04001948 if (filter.filtered && !filter.matches(nr.sbn)) continue;
Dan Sandlera1770312015-07-10 13:59:29 -04001949 nr.dump(pw, " ", getContext(), filter.redact);
John Spurlock25e2d242014-06-27 13:58:23 -04001950 }
1951 pw.println(" ");
Adam Lesinski182f73f2013-12-05 16:48:06 -08001952 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001953
Julia Reynoldse6b53e62015-07-31 09:25:10 -04001954 if (!filter.filtered) {
John Spurlock50806fc2014-07-15 10:22:02 -04001955 N = mLights.size();
1956 if (N > 0) {
1957 pw.println(" Lights List:");
1958 for (int i=0; i<N; i++) {
Chris Wren6054e612014-11-25 17:16:46 -05001959 if (i == N - 1) {
1960 pw.print(" > ");
1961 } else {
1962 pw.print(" ");
1963 }
1964 pw.println(mLights.get(i));
John Spurlock50806fc2014-07-15 10:22:02 -04001965 }
1966 pw.println(" ");
1967 }
John Spurlockcb566aa2014-08-03 22:58:28 -04001968 pw.println(" mUseAttentionLight=" + mUseAttentionLight);
1969 pw.println(" mNotificationPulseEnabled=" + mNotificationPulseEnabled);
Chris Wren6054e612014-11-25 17:16:46 -05001970 pw.println(" mSoundNotificationKey=" + mSoundNotificationKey);
1971 pw.println(" mVibrateNotificationKey=" + mVibrateNotificationKey);
John Spurlockd8afe3c2014-08-01 14:04:07 -04001972 pw.println(" mDisableNotificationEffects=" + mDisableNotificationEffects);
John Spurlock32fe4c62014-10-02 12:16:02 -04001973 pw.println(" mCallState=" + callStateToString(mCallState));
John Spurlock50806fc2014-07-15 10:22:02 -04001974 pw.println(" mSystemReady=" + mSystemReady);
1975 }
1976 pw.println(" mArchive=" + mArchive.toString());
1977 Iterator<StatusBarNotification> iter = mArchive.descendingIterator();
1978 int i=0;
1979 while (iter.hasNext()) {
1980 final StatusBarNotification sbn = iter.next();
1981 if (filter != null && !filter.matches(sbn)) continue;
1982 pw.println(" " + sbn);
1983 if (++i >= 5) {
1984 if (iter.hasNext()) pw.println(" ...");
1985 break;
1986 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001987 }
1988 }
1989
John Spurlock50806fc2014-07-15 10:22:02 -04001990 if (!zenOnly) {
1991 pw.println("\n Usage Stats:");
1992 mUsageStats.dump(pw, " ", filter);
1993 }
Christoph Studer546bec82014-03-14 12:17:12 +01001994
Julia Reynoldse6b53e62015-07-31 09:25:10 -04001995 if (!filter.filtered || zenOnly) {
John Spurlock25e2d242014-06-27 13:58:23 -04001996 pw.println("\n Zen Mode:");
John Spurlockf3701772015-02-12 13:29:37 -05001997 pw.print(" mInterruptionFilter="); pw.println(mInterruptionFilter);
John Spurlock25e2d242014-06-27 13:58:23 -04001998 mZenModeHelper.dump(pw, " ");
John Spurlock6ae82a72014-07-16 16:23:01 -04001999
2000 pw.println("\n Zen Log:");
2001 ZenLog.dump(pw, " ");
John Spurlock25e2d242014-06-27 13:58:23 -04002002 }
John Spurlocke77bb362014-04-26 10:24:59 -04002003
John Spurlock50806fc2014-07-15 10:22:02 -04002004 if (!zenOnly) {
2005 pw.println("\n Ranking Config:");
2006 mRankingHelper.dump(pw, " ", filter);
Chris Wren54bbef42014-07-09 18:37:56 -04002007
John Spurlock50806fc2014-07-15 10:22:02 -04002008 pw.println("\n Notification listeners:");
2009 mListeners.dump(pw, filter);
John Spurlockd8afe3c2014-08-01 14:04:07 -04002010 pw.print(" mListenerHints: "); pw.println(mListenerHints);
2011 pw.print(" mListenersDisablingEffects: (");
2012 N = mListenersDisablingEffects.size();
John Spurlock1fa865f2014-07-21 14:56:39 -04002013 for (int i = 0; i < N; i++) {
John Spurlockd8afe3c2014-08-01 14:04:07 -04002014 final ManagedServiceInfo listener = mListenersDisablingEffects.valueAt(i);
John Spurlock1fa865f2014-07-21 14:56:39 -04002015 if (i > 0) pw.print(',');
2016 pw.print(listener.component);
2017 }
2018 pw.println(')');
John Spurlock50806fc2014-07-15 10:22:02 -04002019 }
John Spurlock80774932015-05-07 17:38:50 -04002020 pw.println("\n Policy access:");
2021 pw.print(" mPolicyAccess: "); pw.println(mPolicyAccess);
John Spurlocke77bb362014-04-26 10:24:59 -04002022
2023 pw.println("\n Condition providers:");
John Spurlock25e2d242014-06-27 13:58:23 -04002024 mConditionProviders.dump(pw, filter);
Christoph Studer265c1052014-07-23 17:14:33 +02002025
2026 pw.println("\n Group summaries:");
2027 for (Entry<String, NotificationRecord> entry : mSummaryByGroupKey.entrySet()) {
2028 NotificationRecord r = entry.getValue();
2029 pw.println(" " + entry.getKey() + " -> " + r.getKey());
2030 if (mNotificationsByKey.get(r.getKey()) != r) {
2031 pw.println("!!!!!!LEAK: Record not found in mNotificationsByKey.");
Dan Sandlera1770312015-07-10 13:59:29 -04002032 r.dump(pw, " ", getContext(), filter.redact);
Christoph Studer265c1052014-07-23 17:14:33 +02002033 }
2034 }
Chris Wren66189fc2015-06-25 14:04:33 -04002035
2036 try {
2037 pw.println("\n Banned Packages:");
Chris Wrene4b38802015-07-07 15:54:19 -04002038 ArrayMap<Integer, ArrayList<String>> packageBans = getPackageBans(filter);
2039 for (Integer userId : packageBans.keySet()) {
2040 for (String packageName : packageBans.get(userId)) {
2041 pw.println(" " + userId + ": " + packageName);
Chris Wren66189fc2015-06-25 14:04:33 -04002042 }
2043 }
2044 } catch (NameNotFoundException e) {
2045 // pass
2046 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002047 }
2048 }
2049
Chris Wrene4b38802015-07-07 15:54:19 -04002050 private ArrayMap<Integer, ArrayList<String>> getPackageBans(DumpFilter filter)
2051 throws NameNotFoundException {
2052 ArrayMap<Integer, ArrayList<String>> packageBans = new ArrayMap<>();
2053 ArrayList<String> packageNames = new ArrayList<>();
2054 for (UserInfo user : UserManager.get(getContext()).getUsers()) {
2055 final int userId = user.getUserHandle().getIdentifier();
2056 final PackageManager packageManager = getContext().getPackageManager();
2057 List<PackageInfo> packages = packageManager.getInstalledPackages(0, userId);
2058 final int packageCount = packages.size();
2059 for (int p = 0; p < packageCount; p++) {
2060 final String packageName = packages.get(p).packageName;
2061 if (filter == null || filter.matches(packageName)) {
2062 final int uid = packageManager.getPackageUid(packageName, userId);
2063 if (!checkNotificationOp(packageName, uid)) {
2064 packageNames.add(packageName);
2065 }
2066 }
2067 }
2068 if (!packageNames.isEmpty()) {
2069 packageBans.put(userId, packageNames);
2070 packageNames = new ArrayList<>();
2071 }
2072 }
2073 return packageBans;
2074 }
2075
Adam Lesinski182f73f2013-12-05 16:48:06 -08002076 /**
2077 * The private API only accessible to the system process.
2078 */
2079 private final NotificationManagerInternal mInternalService = new NotificationManagerInternal() {
2080 @Override
Christoph Studer8fd7f1e2014-04-11 17:35:05 -04002081 public void enqueueNotification(String pkg, String opPkg, int callingUid, int callingPid,
Adam Lesinski182f73f2013-12-05 16:48:06 -08002082 String tag, int id, Notification notification, int[] idReceived, int userId) {
Christoph Studer8fd7f1e2014-04-11 17:35:05 -04002083 enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, notification,
Adam Lesinski182f73f2013-12-05 16:48:06 -08002084 idReceived, userId);
2085 }
Christoph Studer365e4c32014-09-18 20:35:36 +02002086
2087 @Override
2088 public void removeForegroundServiceFlagFromNotification(String pkg, int notificationId,
2089 int userId) {
2090 checkCallerIsSystem();
2091 synchronized (mNotificationList) {
2092 int i = indexOfNotificationLocked(pkg, null, notificationId, userId);
2093 if (i < 0) {
2094 Log.d(TAG, "stripForegroundServiceFlag: Could not find notification with "
2095 + "pkg=" + pkg + " / id=" + notificationId + " / userId=" + userId);
2096 return;
2097 }
2098 NotificationRecord r = mNotificationList.get(i);
2099 StatusBarNotification sbn = r.sbn;
2100 // NoMan adds flags FLAG_NO_CLEAR and FLAG_ONGOING_EVENT when it sees
2101 // FLAG_FOREGROUND_SERVICE. Hence it's not enough to remove FLAG_FOREGROUND_SERVICE,
2102 // we have to revert to the flags we received initially *and* force remove
2103 // FLAG_FOREGROUND_SERVICE.
2104 sbn.getNotification().flags =
2105 (r.mOriginalFlags & ~Notification.FLAG_FOREGROUND_SERVICE);
2106 mRankingHelper.sort(mNotificationList);
2107 mListeners.notifyPostedLocked(sbn, sbn /* oldSbn */);
2108 }
2109 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08002110 };
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002111
Christoph Studer8fd7f1e2014-04-11 17:35:05 -04002112 void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,
Scott Greenwald9b05c612013-06-25 23:44:05 -04002113 final int callingPid, final String tag, final int id, final Notification notification,
Adam Lesinski182f73f2013-12-05 16:48:06 -08002114 int[] idOut, int incomingUserId) {
Daniel Sandler0da673f2012-04-11 12:33:16 -04002115 if (DBG) {
Adam Lesinski182f73f2013-12-05 16:48:06 -08002116 Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id
2117 + " notification=" + notification);
Daniel Sandler0da673f2012-04-11 12:33:16 -04002118 }
John Spurlock7340fc82014-04-24 18:50:12 -04002119 checkCallerIsSystemOrSameApp(pkg);
2120 final boolean isSystemNotification = isUidSystem(callingUid) || ("android".equals(pkg));
Justin Koh38156c52014-06-04 13:57:49 -07002121 final boolean isNotificationFromListener = mListeners.isListenerPackage(pkg);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08002122
Scott Greenwald9b05c612013-06-25 23:44:05 -04002123 final int userId = ActivityManager.handleIncomingUser(callingPid,
2124 callingUid, incomingUserId, true, false, "enqueueNotification", pkg);
Jeff Sharkey65c4a2b2012-09-25 17:22:27 -07002125 final UserHandle user = new UserHandle(userId);
Dianne Hackborn41203752012-08-31 14:05:51 -07002126
Joe Onoratobd73d012010-06-04 11:44:54 -07002127 // Limit the number of notifications that any given package except the android
Justin Koh38156c52014-06-04 13:57:49 -07002128 // package or a registered listener can enqueue. Prevents DOS attacks and deals with leaks.
2129 if (!isSystemNotification && !isNotificationFromListener) {
Joe Onoratobd73d012010-06-04 11:44:54 -07002130 synchronized (mNotificationList) {
2131 int count = 0;
2132 final int N = mNotificationList.size();
2133 for (int i=0; i<N; i++) {
2134 final NotificationRecord r = mNotificationList.get(i);
Daniel Sandler4f91efd2013-04-25 16:38:41 -04002135 if (r.sbn.getPackageName().equals(pkg) && r.sbn.getUserId() == userId) {
Vladimir Marko2526f332013-09-11 11:13:55 +01002136 if (r.sbn.getId() == id && TextUtils.equals(r.sbn.getTag(), tag)) {
2137 break; // Allow updating existing notification
2138 }
Joe Onoratobd73d012010-06-04 11:44:54 -07002139 count++;
2140 if (count >= MAX_PACKAGE_NOTIFICATIONS) {
2141 Slog.e(TAG, "Package has already posted " + count
2142 + " notifications. Not showing more. package=" + pkg);
2143 return;
2144 }
2145 }
2146 }
2147 }
2148 }
2149
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002150 if (pkg == null || notification == null) {
2151 throw new IllegalArgumentException("null not allowed: pkg=" + pkg
2152 + " id=" + id + " notification=" + notification);
2153 }
Dan Sandlerd63f9322015-05-06 15:18:49 -04002154
Scott Greenwald9b05c612013-06-25 23:44:05 -04002155 mHandler.post(new Runnable() {
2156 @Override
2157 public void run() {
Daniel Sandler0da673f2012-04-11 12:33:16 -04002158
Scott Greenwald9b05c612013-06-25 23:44:05 -04002159 synchronized (mNotificationList) {
Christoph Studercd4adf82014-08-19 17:50:49 +02002160
2161 // === Scoring ===
2162
2163 // 0. Sanitize inputs
2164 notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN,
2165 Notification.PRIORITY_MAX);
2166 // Migrate notification flags to scores
2167 if (0 != (notification.flags & Notification.FLAG_HIGH_PRIORITY)) {
2168 if (notification.priority < Notification.PRIORITY_MAX) {
2169 notification.priority = Notification.PRIORITY_MAX;
2170 }
2171 } else if (SCORE_ONGOING_HIGHER &&
2172 0 != (notification.flags & Notification.FLAG_ONGOING_EVENT)) {
2173 if (notification.priority < Notification.PRIORITY_HIGH) {
2174 notification.priority = Notification.PRIORITY_HIGH;
2175 }
2176 }
2177
2178 // 1. initial score: buckets of 10, around the app [-20..20]
2179 final int score = notification.priority * NOTIFICATION_PRIORITY_MULTIPLIER;
2180
2181 // 2. extract ranking signals from the notification data
2182 final StatusBarNotification n = new StatusBarNotification(
2183 pkg, opPkg, id, tag, callingUid, callingPid, score, notification,
2184 user);
2185 NotificationRecord r = new NotificationRecord(n, score);
2186 NotificationRecord old = mNotificationsByKey.get(n.getKey());
2187 if (old != null) {
2188 // Retain ranking information from previous record
2189 r.copyRankingInformation(old);
2190 }
Christoph Studer265c1052014-07-23 17:14:33 +02002191
2192 // Handle grouped notifications and bail out early if we
2193 // can to avoid extracting signals.
2194 handleGroupedNotificationLocked(r, old, callingUid, callingPid);
2195 boolean ignoreNotification =
Christoph Studer4a9849b2015-01-06 18:55:08 +01002196 removeUnusedGroupedNotificationLocked(r, old, callingUid, callingPid);
Christoph Studercd4adf82014-08-19 17:50:49 +02002197
Christoph Studer1382e672014-10-24 16:30:54 +02002198 // This conditional is a dirty hack to limit the logging done on
2199 // behalf of the download manager without affecting other apps.
2200 if (!pkg.equals("com.android.providers.downloads")
2201 || Log.isLoggable("DownloadManager", Log.VERBOSE)) {
Christoph Studer265c1052014-07-23 17:14:33 +02002202 int enqueueStatus = EVENTLOG_ENQUEUE_STATUS_NEW;
2203 if (ignoreNotification) {
2204 enqueueStatus = EVENTLOG_ENQUEUE_STATUS_IGNORED;
2205 } else if (old != null) {
2206 enqueueStatus = EVENTLOG_ENQUEUE_STATUS_UPDATE;
2207 }
Christoph Studer1382e672014-10-24 16:30:54 +02002208 EventLogTags.writeNotificationEnqueue(callingUid, callingPid,
2209 pkg, id, tag, userId, notification.toString(),
Christoph Studer265c1052014-07-23 17:14:33 +02002210 enqueueStatus);
Christoph Studer1382e672014-10-24 16:30:54 +02002211 }
Christoph Studer265c1052014-07-23 17:14:33 +02002212
2213 if (ignoreNotification) {
2214 return;
2215 }
2216
2217 mRankingHelper.extractSignals(r);
Julia Reynolds5d25ee72015-11-20 15:38:20 -05002218 savePolicyFile();
Christoph Studer265c1052014-07-23 17:14:33 +02002219
Christoph Studercd4adf82014-08-19 17:50:49 +02002220 // 3. Apply local rules
2221
2222 // blocked apps
2223 if (ENABLE_BLOCKED_NOTIFICATIONS && !noteNotificationOp(pkg, callingUid)) {
2224 if (!isSystemNotification) {
2225 r.score = JUNK_SCORE;
2226 Slog.e(TAG, "Suppressing notification from package " + pkg
2227 + " by user request.");
Chris Wren5eab2b72015-06-16 13:56:22 -04002228 mUsageStats.registerBlocked(r);
Christoph Studercd4adf82014-08-19 17:50:49 +02002229 }
2230 }
2231
2232 if (r.score < SCORE_DISPLAY_THRESHOLD) {
2233 // Notification will be blocked because the score is too low.
2234 return;
2235 }
2236
Christoph Studer71f18fd2014-05-20 17:02:04 +02002237 int index = indexOfNotificationLocked(n.getKey());
Scott Greenwald9b05c612013-06-25 23:44:05 -04002238 if (index < 0) {
2239 mNotificationList.add(r);
Christoph Studer546bec82014-03-14 12:17:12 +01002240 mUsageStats.registerPostedByApp(r);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002241 } else {
Christoph Studerd89188d2014-04-03 00:02:39 +02002242 old = mNotificationList.get(index);
2243 mNotificationList.set(index, r);
Christoph Studer061dee22014-05-09 12:28:55 +02002244 mUsageStats.registerUpdatedByApp(r, old);
Scott Greenwald9b05c612013-06-25 23:44:05 -04002245 // Make sure we don't lose the foreground service state.
Christoph Studer71f18fd2014-05-20 17:02:04 +02002246 notification.flags |=
Chris Wrena3446562014-06-03 18:11:47 -04002247 old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;
Chris Wrena3446562014-06-03 18:11:47 -04002248 r.isUpdate = true;
John Spurlocka4294292014-03-24 18:02:32 -04002249 }
Christoph Studer1cd5add2014-07-03 00:23:05 +02002250
John Spurlocka4294292014-03-24 18:02:32 -04002251 mNotificationsByKey.put(n.getKey(), r);
Scott Greenwald9b05c612013-06-25 23:44:05 -04002252
Christoph Studer1cd5add2014-07-03 00:23:05 +02002253 // Ensure if this is a foreground service that the proper additional
2254 // flags are set.
2255 if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
2256 notification.flags |= Notification.FLAG_ONGOING_EVENT
2257 | Notification.FLAG_NO_CLEAR;
2258 }
2259
Chris Wren52eba542014-06-02 15:40:32 -04002260 applyZenModeLocked(r);
Christoph Studercd4adf82014-08-19 17:50:49 +02002261 mRankingHelper.sort(mNotificationList);
Chris Wrenf9536642014-04-17 10:01:54 -04002262
Dan Sandlerd63f9322015-05-06 15:18:49 -04002263 if (notification.getSmallIcon() != null) {
Christoph Studercef37cf2014-07-25 14:18:17 +02002264 StatusBarNotification oldSbn = (old != null) ? old.sbn : null;
2265 mListeners.notifyPostedLocked(n, oldSbn);
Scott Greenwald9b05c612013-06-25 23:44:05 -04002266 } else {
Dan Sandlerd63f9322015-05-06 15:18:49 -04002267 Slog.e(TAG, "Not posting notification without small icon: " + notification);
Christoph Studer71f18fd2014-05-20 17:02:04 +02002268 if (old != null && !old.isCanceled) {
Chris Wrena3446562014-06-03 18:11:47 -04002269 mListeners.notifyRemovedLocked(n);
Scott Greenwald9b05c612013-06-25 23:44:05 -04002270 }
2271 // ATTENTION: in a future release we will bail out here
Adam Lesinski182f73f2013-12-05 16:48:06 -08002272 // so that we do not play sounds, show lights, etc. for invalid
2273 // notifications
Scott Greenwald9b05c612013-06-25 23:44:05 -04002274 Slog.e(TAG, "WARNING: In a future release this will crash the app: "
2275 + n.getPackageName());
2276 }
2277
Chris Wrena3446562014-06-03 18:11:47 -04002278 buzzBeepBlinkLocked(r);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002279 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002280 }
Scott Greenwald9b05c612013-06-25 23:44:05 -04002281 });
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002282
2283 idOut[0] = id;
2284 }
2285
Christoph Studer265c1052014-07-23 17:14:33 +02002286 /**
2287 * Ensures that grouped notification receive their special treatment.
2288 *
2289 * <p>Cancels group children if the new notification causes a group to lose
2290 * its summary.</p>
2291 *
2292 * <p>Updates mSummaryByGroupKey.</p>
2293 */
2294 private void handleGroupedNotificationLocked(NotificationRecord r, NotificationRecord old,
2295 int callingUid, int callingPid) {
2296 StatusBarNotification sbn = r.sbn;
2297 Notification n = sbn.getNotification();
2298 String group = sbn.getGroupKey();
2299 boolean isSummary = n.isGroupSummary();
2300
2301 Notification oldN = old != null ? old.sbn.getNotification() : null;
2302 String oldGroup = old != null ? old.sbn.getGroupKey() : null;
2303 boolean oldIsSummary = old != null && oldN.isGroupSummary();
2304
2305 if (oldIsSummary) {
2306 NotificationRecord removedSummary = mSummaryByGroupKey.remove(oldGroup);
2307 if (removedSummary != old) {
2308 String removedKey =
2309 removedSummary != null ? removedSummary.getKey() : "<null>";
2310 Slog.w(TAG, "Removed summary didn't match old notification: old=" + old.getKey() +
2311 ", removed=" + removedKey);
2312 }
2313 }
2314 if (isSummary) {
2315 mSummaryByGroupKey.put(group, r);
2316 }
2317
2318 // Clear out group children of the old notification if the update
2319 // causes the group summary to go away. This happens when the old
2320 // notification was a summary and the new one isn't, or when the old
2321 // notification was a summary and its group key changed.
2322 if (oldIsSummary && (!isSummary || !oldGroup.equals(group))) {
2323 cancelGroupChildrenLocked(old, callingUid, callingPid, null,
2324 REASON_GROUP_SUMMARY_CANCELED);
2325 }
2326 }
2327
2328 /**
2329 * Performs group notification optimizations if SysUI is the only active
2330 * notification listener and returns whether the given notification should
2331 * be ignored.
2332 *
2333 * <p>Returns true if the given notification is a child of a group with a
2334 * summary, which means that SysUI will never show it, and hence the new
Christoph Studer4a9849b2015-01-06 18:55:08 +01002335 * notification can be safely ignored. Also cancels any previous instance
2336 * of the ignored notification.</p>
Christoph Studer265c1052014-07-23 17:14:33 +02002337 *
2338 * <p>For summaries, cancels all children of that group, as SysUI will
2339 * never show them anymore.</p>
2340 *
2341 * @return true if the given notification can be ignored as an optimization
2342 */
2343 private boolean removeUnusedGroupedNotificationLocked(NotificationRecord r,
Christoph Studer4a9849b2015-01-06 18:55:08 +01002344 NotificationRecord old, int callingUid, int callingPid) {
Selim Cinekb5605e52015-02-20 18:21:41 +01002345 if (!ENABLE_CHILD_NOTIFICATIONS) {
2346 // No optimizations are possible if listeners want groups.
2347 if (mListeners.notificationGroupsDesired()) {
2348 return false;
Christoph Studer265c1052014-07-23 17:14:33 +02002349 }
Selim Cinekb5605e52015-02-20 18:21:41 +01002350
2351 StatusBarNotification sbn = r.sbn;
2352 String group = sbn.getGroupKey();
2353 boolean isSummary = sbn.getNotification().isGroupSummary();
2354 boolean isChild = sbn.getNotification().isGroupChild();
2355
2356 NotificationRecord summary = mSummaryByGroupKey.get(group);
2357 if (isChild && summary != null) {
2358 // Child with an active summary -> ignore
Christoph Studer4a9849b2015-01-06 18:55:08 +01002359 if (DBG) {
Selim Cinekb5605e52015-02-20 18:21:41 +01002360 Slog.d(TAG, "Ignoring group child " + sbn.getKey() + " due to existing summary "
2361 + summary.getKey());
Christoph Studer4a9849b2015-01-06 18:55:08 +01002362 }
Selim Cinekb5605e52015-02-20 18:21:41 +01002363 // Make sure we don't leave an old version of the notification around.
2364 if (old != null) {
2365 if (DBG) {
2366 Slog.d(TAG, "Canceling old version of ignored group child " + sbn.getKey());
2367 }
2368 cancelNotificationLocked(old, false, REASON_GROUP_OPTIMIZATION);
2369 }
2370 return true;
2371 } else if (isSummary) {
2372 // Summary -> cancel children
2373 cancelGroupChildrenLocked(r, callingUid, callingPid, null,
2374 REASON_GROUP_OPTIMIZATION);
Christoph Studer4a9849b2015-01-06 18:55:08 +01002375 }
Christoph Studer265c1052014-07-23 17:14:33 +02002376 }
2377 return false;
2378 }
2379
Chris Wrena3446562014-06-03 18:11:47 -04002380 private void buzzBeepBlinkLocked(NotificationRecord record) {
Chris Wren82ba59d2015-06-05 11:23:44 -04002381 boolean buzz = false;
2382 boolean beep = false;
2383 boolean blink = false;
2384
Chris Wrena3446562014-06-03 18:11:47 -04002385 final Notification notification = record.sbn.getNotification();
2386
2387 // Should this notification make noise, vibe, or use the LED?
Chris Wrence00a232014-11-21 16:25:19 -05002388 final boolean aboveThreshold = record.score >= SCORE_INTERRUPTION_THRESHOLD;
2389 final boolean canInterrupt = aboveThreshold && !record.isIntercepted();
Chris Wrena3446562014-06-03 18:11:47 -04002390 if (DBG || record.isIntercepted())
2391 Slog.v(TAG,
2392 "pkg=" + record.sbn.getPackageName() + " canInterrupt=" + canInterrupt +
2393 " intercept=" + record.isIntercepted()
2394 );
2395
2396 final int currentUser;
2397 final long token = Binder.clearCallingIdentity();
2398 try {
2399 currentUser = ActivityManager.getCurrentUser();
2400 } finally {
2401 Binder.restoreCallingIdentity(token);
2402 }
2403
2404 // If we're not supposed to beep, vibrate, etc. then don't.
John Spurlock32fe4c62014-10-02 12:16:02 -04002405 final String disableEffects = disableNotificationEffects(record);
2406 if (disableEffects != null) {
2407 ZenLog.traceDisableEffects(record, disableEffects);
2408 }
2409 if (disableEffects == null
Chris Wrena3446562014-06-03 18:11:47 -04002410 && (!(record.isUpdate
2411 && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 ))
2412 && (record.getUserId() == UserHandle.USER_ALL ||
2413 record.getUserId() == currentUser ||
2414 mUserProfiles.isCurrentProfile(record.getUserId()))
2415 && canInterrupt
2416 && mSystemReady
2417 && mAudioManager != null) {
2418 if (DBG) Slog.v(TAG, "Interrupting!");
2419
2420 sendAccessibilityEvent(notification, record.sbn.getPackageName());
2421
2422 // sound
2423
2424 // should we use the default notification sound? (indicated either by
2425 // DEFAULT_SOUND or because notification.sound is pointing at
2426 // Settings.System.NOTIFICATION_SOUND)
2427 final boolean useDefaultSound =
2428 (notification.defaults & Notification.DEFAULT_SOUND) != 0 ||
2429 Settings.System.DEFAULT_NOTIFICATION_URI
2430 .equals(notification.sound);
2431
2432 Uri soundUri = null;
2433 boolean hasValidSound = false;
2434
2435 if (useDefaultSound) {
2436 soundUri = Settings.System.DEFAULT_NOTIFICATION_URI;
2437
2438 // check to see if the default notification sound is silent
2439 ContentResolver resolver = getContext().getContentResolver();
2440 hasValidSound = Settings.System.getString(resolver,
2441 Settings.System.NOTIFICATION_SOUND) != null;
2442 } else if (notification.sound != null) {
2443 soundUri = notification.sound;
2444 hasValidSound = (soundUri != null);
2445 }
2446
2447 if (hasValidSound) {
2448 boolean looping =
2449 (notification.flags & Notification.FLAG_INSISTENT) != 0;
Marco Nelissen1c066302014-11-18 10:48:12 -08002450 AudioAttributes audioAttributes = audioAttributesForNotification(notification);
Chris Wren6054e612014-11-25 17:16:46 -05002451 mSoundNotificationKey = record.getKey();
Chris Wrena3446562014-06-03 18:11:47 -04002452 // do not play notifications if stream volume is 0 (typically because
2453 // ringer mode is silent) or if there is a user of exclusive audio focus
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -07002454 if ((mAudioManager.getStreamVolume(
2455 AudioAttributes.toLegacyStreamType(audioAttributes)) != 0)
2456 && !mAudioManager.isAudioFocusExclusive()) {
Chris Wrena3446562014-06-03 18:11:47 -04002457 final long identity = Binder.clearCallingIdentity();
2458 try {
2459 final IRingtonePlayer player =
2460 mAudioManager.getRingtonePlayer();
2461 if (player != null) {
2462 if (DBG) Slog.v(TAG, "Playing sound " + soundUri
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -07002463 + " with attributes " + audioAttributes);
Chris Wrena3446562014-06-03 18:11:47 -04002464 player.playAsync(soundUri, record.sbn.getUser(), looping,
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -07002465 audioAttributes);
Chris Wren82ba59d2015-06-05 11:23:44 -04002466 beep = true;
Chris Wrena3446562014-06-03 18:11:47 -04002467 }
2468 } catch (RemoteException e) {
2469 } finally {
2470 Binder.restoreCallingIdentity(identity);
2471 }
2472 }
2473 }
2474
2475 // vibrate
2476 // Does the notification want to specify its own vibration?
2477 final boolean hasCustomVibrate = notification.vibrate != null;
2478
2479 // new in 4.2: if there was supposed to be a sound and we're in vibrate
2480 // mode, and no other vibration is specified, we fall back to vibration
2481 final boolean convertSoundToVibration =
2482 !hasCustomVibrate
2483 && hasValidSound
John Spurlock661f2cf2014-11-17 10:29:10 -05002484 && (mAudioManager.getRingerModeInternal()
Chris Wrena3446562014-06-03 18:11:47 -04002485 == AudioManager.RINGER_MODE_VIBRATE);
2486
2487 // The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback.
2488 final boolean useDefaultVibrate =
2489 (notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
2490
2491 if ((useDefaultVibrate || convertSoundToVibration || hasCustomVibrate)
John Spurlock661f2cf2014-11-17 10:29:10 -05002492 && !(mAudioManager.getRingerModeInternal()
Chris Wrena3446562014-06-03 18:11:47 -04002493 == AudioManager.RINGER_MODE_SILENT)) {
Chris Wren6054e612014-11-25 17:16:46 -05002494 mVibrateNotificationKey = record.getKey();
Chris Wrena3446562014-06-03 18:11:47 -04002495
2496 if (useDefaultVibrate || convertSoundToVibration) {
2497 // Escalate privileges so we can use the vibrator even if the
2498 // notifying app does not have the VIBRATE permission.
2499 long identity = Binder.clearCallingIdentity();
2500 try {
2501 mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
2502 useDefaultVibrate ? mDefaultVibrationPattern
2503 : mFallbackVibrationPattern,
2504 ((notification.flags & Notification.FLAG_INSISTENT) != 0)
John Spurlock7b414672014-07-18 13:02:39 -04002505 ? 0: -1, audioAttributesForNotification(notification));
Chris Wren82ba59d2015-06-05 11:23:44 -04002506 buzz = true;
Chris Wrena3446562014-06-03 18:11:47 -04002507 } finally {
2508 Binder.restoreCallingIdentity(identity);
2509 }
2510 } else if (notification.vibrate.length > 1) {
2511 // If you want your own vibration pattern, you need the VIBRATE
2512 // permission
2513 mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
2514 notification.vibrate,
2515 ((notification.flags & Notification.FLAG_INSISTENT) != 0)
John Spurlock7b414672014-07-18 13:02:39 -04002516 ? 0: -1, audioAttributesForNotification(notification));
Chris Wren82ba59d2015-06-05 11:23:44 -04002517 buzz = true;
Chris Wrena3446562014-06-03 18:11:47 -04002518 }
2519 }
2520 }
2521
2522 // light
2523 // release the light
2524 boolean wasShowLights = mLights.remove(record.getKey());
Julia Reynoldsf612869ae2015-11-05 16:48:55 -05002525 if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0 && aboveThreshold
2526 && ((record.getSuppressedVisualEffects()
2527 & NotificationListenerService.SUPPRESSED_EFFECT_LIGHTS) == 0)) {
Chris Wrena3446562014-06-03 18:11:47 -04002528 mLights.add(record.getKey());
2529 updateLightsLocked();
Chris Wren5116a822014-06-04 15:59:50 -04002530 if (mUseAttentionLight) {
2531 mAttentionLight.pulse();
2532 }
Chris Wren82ba59d2015-06-05 11:23:44 -04002533 blink = true;
Chris Wrena3446562014-06-03 18:11:47 -04002534 } else if (wasShowLights) {
2535 updateLightsLocked();
2536 }
Chris Wren82ba59d2015-06-05 11:23:44 -04002537 if (buzz || beep || blink) {
2538 EventLogTags.writeNotificationAlert(record.getKey(),
2539 buzz ? 1 : 0, beep ? 1 : 0, blink ? 1 : 0);
John Spurlockcad57682014-07-26 17:09:56 -04002540 mHandler.post(mBuzzBeepBlinked);
2541 }
Chris Wrena3446562014-06-03 18:11:47 -04002542 }
2543
John Spurlock7b414672014-07-18 13:02:39 -04002544 private static AudioAttributes audioAttributesForNotification(Notification n) {
Marco Nelissen1c066302014-11-18 10:48:12 -08002545 if (n.audioAttributes != null
2546 && !Notification.AUDIO_ATTRIBUTES_DEFAULT.equals(n.audioAttributes)) {
2547 // the audio attributes are set and different from the default, use them
John Spurlockbfa5dc42014-07-28 23:30:45 -04002548 return n.audioAttributes;
Jean-Michel Triviceb79bc2014-09-05 11:09:14 -07002549 } else if (n.audioStreamType >= 0 && n.audioStreamType < AudioSystem.getNumStreamTypes()) {
2550 // the stream type is valid, use it
2551 return new AudioAttributes.Builder()
2552 .setInternalLegacyStreamType(n.audioStreamType)
2553 .build();
2554 } else if (n.audioStreamType == AudioSystem.STREAM_DEFAULT) {
2555 return Notification.AUDIO_ATTRIBUTES_DEFAULT;
2556 } else {
2557 Log.w(TAG, String.format("Invalid stream type: %d", n.audioStreamType));
2558 return Notification.AUDIO_ATTRIBUTES_DEFAULT;
John Spurlockbfa5dc42014-07-28 23:30:45 -04002559 }
John Spurlock7b414672014-07-18 13:02:39 -04002560 }
2561
Adam Lesinski182f73f2013-12-05 16:48:06 -08002562 void showNextToastLocked() {
2563 ToastRecord record = mToastQueue.get(0);
2564 while (record != null) {
2565 if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback);
2566 try {
2567 record.callback.show();
2568 scheduleTimeoutLocked(record);
2569 return;
2570 } catch (RemoteException e) {
2571 Slog.w(TAG, "Object died trying to show notification " + record.callback
2572 + " in package " + record.pkg);
2573 // remove it from the list and let the process die
2574 int index = mToastQueue.indexOf(record);
2575 if (index >= 0) {
2576 mToastQueue.remove(index);
2577 }
2578 keepProcessAliveLocked(record.pid);
2579 if (mToastQueue.size() > 0) {
2580 record = mToastQueue.get(0);
2581 } else {
2582 record = null;
2583 }
2584 }
2585 }
2586 }
2587
2588 void cancelToastLocked(int index) {
2589 ToastRecord record = mToastQueue.get(index);
2590 try {
2591 record.callback.hide();
2592 } catch (RemoteException e) {
2593 Slog.w(TAG, "Object died trying to hide notification " + record.callback
2594 + " in package " + record.pkg);
2595 // don't worry about this, we're about to remove it from
2596 // the list anyway
2597 }
2598 mToastQueue.remove(index);
2599 keepProcessAliveLocked(record.pid);
2600 if (mToastQueue.size() > 0) {
2601 // Show the next one. If the callback fails, this will remove
2602 // it from the list, so don't assume that the list hasn't changed
2603 // after this point.
2604 showNextToastLocked();
2605 }
2606 }
2607
2608 private void scheduleTimeoutLocked(ToastRecord r)
2609 {
2610 mHandler.removeCallbacksAndMessages(r);
2611 Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r);
2612 long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY;
2613 mHandler.sendMessageDelayed(m, delay);
2614 }
2615
2616 private void handleTimeout(ToastRecord record)
2617 {
2618 if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback);
2619 synchronized (mToastQueue) {
2620 int index = indexOfToastLocked(record.pkg, record.callback);
2621 if (index >= 0) {
2622 cancelToastLocked(index);
2623 }
2624 }
2625 }
2626
2627 // lock on mToastQueue
2628 int indexOfToastLocked(String pkg, ITransientNotification callback)
2629 {
2630 IBinder cbak = callback.asBinder();
2631 ArrayList<ToastRecord> list = mToastQueue;
2632 int len = list.size();
2633 for (int i=0; i<len; i++) {
2634 ToastRecord r = list.get(i);
2635 if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) {
2636 return i;
2637 }
2638 }
2639 return -1;
2640 }
2641
2642 // lock on mToastQueue
2643 void keepProcessAliveLocked(int pid)
2644 {
2645 int toastCount = 0; // toasts from this pid
2646 ArrayList<ToastRecord> list = mToastQueue;
2647 int N = list.size();
2648 for (int i=0; i<N; i++) {
2649 ToastRecord r = list.get(i);
2650 if (r.pid == pid) {
2651 toastCount++;
2652 }
2653 }
2654 try {
2655 mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0);
2656 } catch (RemoteException e) {
2657 // Shouldn't happen.
2658 }
2659 }
2660
Chris Wrenf9536642014-04-17 10:01:54 -04002661 private void handleRankingReconsideration(Message message) {
Chris Wren470c1ac2014-05-21 15:28:10 -04002662 if (!(message.obj instanceof RankingReconsideration)) return;
2663 RankingReconsideration recon = (RankingReconsideration) message.obj;
2664 recon.run();
Chris Wren333a61c2014-05-28 16:40:57 -04002665 boolean changed;
Chris Wren470c1ac2014-05-21 15:28:10 -04002666 synchronized (mNotificationList) {
2667 final NotificationRecord record = mNotificationsByKey.get(recon.getKey());
2668 if (record == null) {
2669 return;
Chris Wrenf9536642014-04-17 10:01:54 -04002670 }
Chris Wren333a61c2014-05-28 16:40:57 -04002671 int indexBefore = findNotificationRecordIndexLocked(record);
2672 boolean interceptBefore = record.isIntercepted();
Chris Wren3ad4e3a2014-09-02 17:23:51 -04002673 int visibilityBefore = record.getPackageVisibilityOverride();
Chris Wren470c1ac2014-05-21 15:28:10 -04002674 recon.applyChangesLocked(record);
Chris Wren333a61c2014-05-28 16:40:57 -04002675 applyZenModeLocked(record);
Chris Wren54bbef42014-07-09 18:37:56 -04002676 mRankingHelper.sort(mNotificationList);
Chris Wren333a61c2014-05-28 16:40:57 -04002677 int indexAfter = findNotificationRecordIndexLocked(record);
2678 boolean interceptAfter = record.isIntercepted();
Chris Wren3ad4e3a2014-09-02 17:23:51 -04002679 int visibilityAfter = record.getPackageVisibilityOverride();
2680 changed = indexBefore != indexAfter || interceptBefore != interceptAfter
2681 || visibilityBefore != visibilityAfter;
Chris Wrena3446562014-06-03 18:11:47 -04002682 if (interceptBefore && !interceptAfter) {
2683 buzzBeepBlinkLocked(record);
2684 }
Chris Wrenf9536642014-04-17 10:01:54 -04002685 }
Chris Wren333a61c2014-05-28 16:40:57 -04002686 if (changed) {
Chris Wren470c1ac2014-05-21 15:28:10 -04002687 scheduleSendRankingUpdate();
2688 }
2689 }
2690
Chris Wren54bbef42014-07-09 18:37:56 -04002691 private void handleRankingConfigChange() {
2692 synchronized (mNotificationList) {
2693 final int N = mNotificationList.size();
2694 ArrayList<String> orderBefore = new ArrayList<String>(N);
Chris Wren3ad4e3a2014-09-02 17:23:51 -04002695 int[] visibilities = new int[N];
Chris Wren54bbef42014-07-09 18:37:56 -04002696 for (int i = 0; i < N; i++) {
2697 final NotificationRecord r = mNotificationList.get(i);
2698 orderBefore.add(r.getKey());
Chris Wren3ad4e3a2014-09-02 17:23:51 -04002699 visibilities[i] = r.getPackageVisibilityOverride();
Chris Wren54bbef42014-07-09 18:37:56 -04002700 mRankingHelper.extractSignals(r);
2701 }
Chris Wren54bbef42014-07-09 18:37:56 -04002702 for (int i = 0; i < N; i++) {
Chris Wren3ad4e3a2014-09-02 17:23:51 -04002703 mRankingHelper.sort(mNotificationList);
2704 final NotificationRecord r = mNotificationList.get(i);
2705 if (!orderBefore.get(i).equals(r.getKey())
2706 || visibilities[i] != r.getPackageVisibilityOverride()) {
Chris Wren54bbef42014-07-09 18:37:56 -04002707 scheduleSendRankingUpdate();
2708 return;
2709 }
2710 }
2711 }
2712 }
2713
Christoph Studerd5092bc2014-07-03 17:47:58 +02002714 // let zen mode evaluate this record
Chris Wren333a61c2014-05-28 16:40:57 -04002715 private void applyZenModeLocked(NotificationRecord record) {
Christoph Studerd5092bc2014-07-03 17:47:58 +02002716 record.setIntercepted(mZenModeHelper.shouldIntercept(record));
Julia Reynoldsf612869ae2015-11-05 16:48:55 -05002717 if (record.isIntercepted()) {
2718 int suppressed = (mZenModeHelper.shouldSuppressLight() ? SUPPRESSED_EFFECT_LIGHTS : 0)
2719 | (mZenModeHelper.shouldSuppressPeek() ? SUPPRESSED_EFFECT_PEEK : 0);
2720 record.setSuppressedVisualEffects(suppressed);
2721 }
Chris Wren333a61c2014-05-28 16:40:57 -04002722 }
2723
Chris Wren470c1ac2014-05-21 15:28:10 -04002724 // lock on mNotificationList
2725 private int findNotificationRecordIndexLocked(NotificationRecord target) {
Chris Wren54bbef42014-07-09 18:37:56 -04002726 return mRankingHelper.indexOf(mNotificationList, target);
Chris Wrenf9536642014-04-17 10:01:54 -04002727 }
2728
2729 private void scheduleSendRankingUpdate() {
2730 mHandler.removeMessages(MESSAGE_SEND_RANKING_UPDATE);
2731 Message m = Message.obtain(mHandler, MESSAGE_SEND_RANKING_UPDATE);
2732 mHandler.sendMessage(m);
2733 }
2734
2735 private void handleSendRankingUpdate() {
2736 synchronized (mNotificationList) {
Chris Wren333a61c2014-05-28 16:40:57 -04002737 mListeners.notifyRankingUpdateLocked();
Chris Wrenf9536642014-04-17 10:01:54 -04002738 }
2739 }
2740
John Spurlockd8afe3c2014-08-01 14:04:07 -04002741 private void scheduleListenerHintsChanged(int state) {
2742 mHandler.removeMessages(MESSAGE_LISTENER_HINTS_CHANGED);
2743 mHandler.obtainMessage(MESSAGE_LISTENER_HINTS_CHANGED, state, 0).sendToTarget();
John Spurlock1fa865f2014-07-21 14:56:39 -04002744 }
2745
Christoph Studer85a384b2014-08-27 20:16:15 +02002746 private void scheduleInterruptionFilterChanged(int listenerInterruptionFilter) {
2747 mHandler.removeMessages(MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED);
2748 mHandler.obtainMessage(
2749 MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED,
2750 listenerInterruptionFilter,
2751 0).sendToTarget();
2752 }
2753
John Spurlockd8afe3c2014-08-01 14:04:07 -04002754 private void handleListenerHintsChanged(int hints) {
John Spurlock1fa865f2014-07-21 14:56:39 -04002755 synchronized (mNotificationList) {
John Spurlockd8afe3c2014-08-01 14:04:07 -04002756 mListeners.notifyListenerHintsChangedLocked(hints);
John Spurlock1fa865f2014-07-21 14:56:39 -04002757 }
2758 }
2759
Christoph Studer85a384b2014-08-27 20:16:15 +02002760 private void handleListenerInterruptionFilterChanged(int interruptionFilter) {
2761 synchronized (mNotificationList) {
2762 mListeners.notifyInterruptionFilterChanged(interruptionFilter);
2763 }
2764 }
2765
Adam Lesinski182f73f2013-12-05 16:48:06 -08002766 private final class WorkerHandler extends Handler
2767 {
2768 @Override
2769 public void handleMessage(Message msg)
2770 {
2771 switch (msg.what)
2772 {
2773 case MESSAGE_TIMEOUT:
2774 handleTimeout((ToastRecord)msg.obj);
2775 break;
John Spurlock056c5192014-04-20 21:52:01 -04002776 case MESSAGE_SAVE_POLICY_FILE:
2777 handleSavePolicyFile();
2778 break;
Chris Wrenf9536642014-04-17 10:01:54 -04002779 case MESSAGE_SEND_RANKING_UPDATE:
2780 handleSendRankingUpdate();
2781 break;
John Spurlockd8afe3c2014-08-01 14:04:07 -04002782 case MESSAGE_LISTENER_HINTS_CHANGED:
2783 handleListenerHintsChanged(msg.arg1);
John Spurlock1fa865f2014-07-21 14:56:39 -04002784 break;
Christoph Studer85a384b2014-08-27 20:16:15 +02002785 case MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED:
2786 handleListenerInterruptionFilterChanged(msg.arg1);
2787 break;
Chris Wrenf9536642014-04-17 10:01:54 -04002788 }
2789 }
2790
2791 }
2792
2793 private final class RankingWorkerHandler extends Handler
2794 {
2795 public RankingWorkerHandler(Looper looper) {
2796 super(looper);
2797 }
2798
2799 @Override
2800 public void handleMessage(Message msg) {
2801 switch (msg.what) {
2802 case MESSAGE_RECONSIDER_RANKING:
2803 handleRankingReconsideration(msg);
2804 break;
Chris Wren54bbef42014-07-09 18:37:56 -04002805 case MESSAGE_RANKING_CONFIG_CHANGE:
2806 handleRankingConfigChange();
2807 break;
Adam Lesinski182f73f2013-12-05 16:48:06 -08002808 }
2809 }
2810 }
2811
Adam Lesinski182f73f2013-12-05 16:48:06 -08002812 // Notifications
2813 // ============================================================================
2814 static int clamp(int x, int low, int high) {
2815 return (x < low) ? low : ((x > high) ? high : x);
2816 }
2817
2818 void sendAccessibilityEvent(Notification notification, CharSequence packageName) {
2819 AccessibilityManager manager = AccessibilityManager.getInstance(getContext());
svetoslavganov75986cf2009-05-14 22:28:01 -07002820 if (!manager.isEnabled()) {
2821 return;
2822 }
2823
2824 AccessibilityEvent event =
2825 AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
2826 event.setPackageName(packageName);
2827 event.setClassName(Notification.class.getName());
2828 event.setParcelableData(notification);
2829 CharSequence tickerText = notification.tickerText;
2830 if (!TextUtils.isEmpty(tickerText)) {
2831 event.getText().add(tickerText);
2832 }
2833
2834 manager.sendAccessibilityEvent(event);
2835 }
2836
Christoph Studer546bec82014-03-14 12:17:12 +01002837 private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason) {
Joe Onorato46439ce2010-11-19 13:56:21 -08002838 // tell the app
2839 if (sendDelete) {
Daniel Sandlerfde19b12013-01-17 00:21:05 -05002840 if (r.getNotification().deleteIntent != null) {
Joe Onorato46439ce2010-11-19 13:56:21 -08002841 try {
Daniel Sandlerfde19b12013-01-17 00:21:05 -05002842 r.getNotification().deleteIntent.send();
Joe Onorato46439ce2010-11-19 13:56:21 -08002843 } catch (PendingIntent.CanceledException ex) {
2844 // do nothing - there's no relevant way to recover, and
2845 // no reason to let this propagate
Daniel Sandler4f91efd2013-04-25 16:38:41 -04002846 Slog.w(TAG, "canceled PendingIntent for " + r.sbn.getPackageName(), ex);
Joe Onorato46439ce2010-11-19 13:56:21 -08002847 }
2848 }
2849 }
2850
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002851 // status bar
Dan Sandlerd63f9322015-05-06 15:18:49 -04002852 if (r.getNotification().getSmallIcon() != null) {
Christoph Studer71f18fd2014-05-20 17:02:04 +02002853 r.isCanceled = true;
Chris Wren333a61c2014-05-28 16:40:57 -04002854 mListeners.notifyRemovedLocked(r.sbn);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002855 }
2856
Chris Wren6054e612014-11-25 17:16:46 -05002857 final String canceledKey = r.getKey();
2858
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002859 // sound
Chris Wren6054e612014-11-25 17:16:46 -05002860 if (canceledKey.equals(mSoundNotificationKey)) {
2861 mSoundNotificationKey = null;
Jeff Sharkey098d5802012-04-26 17:30:34 -07002862 final long identity = Binder.clearCallingIdentity();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002863 try {
Adam Lesinski182f73f2013-12-05 16:48:06 -08002864 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
Jeff Sharkey098d5802012-04-26 17:30:34 -07002865 if (player != null) {
2866 player.stopAsync();
2867 }
2868 } catch (RemoteException e) {
2869 } finally {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002870 Binder.restoreCallingIdentity(identity);
2871 }
2872 }
2873
2874 // vibrate
Chris Wren6054e612014-11-25 17:16:46 -05002875 if (canceledKey.equals(mVibrateNotificationKey)) {
2876 mVibrateNotificationKey = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002877 long identity = Binder.clearCallingIdentity();
2878 try {
2879 mVibrator.cancel();
2880 }
2881 finally {
2882 Binder.restoreCallingIdentity(identity);
2883 }
2884 }
2885
2886 // light
Chris Wren6054e612014-11-25 17:16:46 -05002887 mLights.remove(canceledKey);
Daniel Sandler23d7c702013-03-07 16:32:06 -05002888
Christoph Studer546bec82014-03-14 12:17:12 +01002889 // Record usage stats
2890 switch (reason) {
2891 case REASON_DELEGATE_CANCEL:
2892 case REASON_DELEGATE_CANCEL_ALL:
2893 case REASON_LISTENER_CANCEL:
2894 case REASON_LISTENER_CANCEL_ALL:
2895 mUsageStats.registerDismissedByUser(r);
2896 break;
Chris Wren9fa689f2015-11-20 16:44:53 -05002897 case REASON_APP_CANCEL:
2898 case REASON_APP_CANCEL_ALL:
Christoph Studer546bec82014-03-14 12:17:12 +01002899 mUsageStats.registerRemovedByApp(r);
2900 break;
Christoph Studer546bec82014-03-14 12:17:12 +01002901 }
2902
Christoph Studercef37cf2014-07-25 14:18:17 +02002903 mNotificationsByKey.remove(r.sbn.getKey());
Christoph Studer265c1052014-07-23 17:14:33 +02002904 String groupKey = r.getGroupKey();
2905 NotificationRecord groupSummary = mSummaryByGroupKey.get(groupKey);
2906 if (groupSummary != null && groupSummary.getKey().equals(r.getKey())) {
2907 mSummaryByGroupKey.remove(groupKey);
2908 }
Christoph Studercef37cf2014-07-25 14:18:17 +02002909
Daniel Sandler23d7c702013-03-07 16:32:06 -05002910 // Save it for users of getHistoricalNotifications()
2911 mArchive.record(r.sbn);
Christoph Studer81e5b5f2014-10-22 17:19:56 +02002912
Chris Wren6650e572015-05-15 17:19:25 -04002913 final long now = System.currentTimeMillis();
Chris Wrene6ddb8a2015-05-27 15:21:00 -04002914 EventLogTags.writeNotificationCanceled(canceledKey, reason,
2915 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002916 }
2917
2918 /**
Dianne Hackbornd8a43f62009-08-17 23:33:56 -07002919 * Cancels a notification ONLY if it has all of the {@code mustHaveFlags}
Doug Zongkerab5c49c2009-12-04 10:31:43 -08002920 * and none of the {@code mustNotHaveFlags}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002921 */
John Spurlocke6a7d932014-03-13 12:29:00 -04002922 void cancelNotification(final int callingUid, final int callingPid,
2923 final String pkg, final String tag, final int id,
Svetoslav Ganov835835e2013-08-04 20:17:52 -07002924 final int mustHaveFlags, final int mustNotHaveFlags, final boolean sendDelete,
John Spurlock7340fc82014-04-24 18:50:12 -04002925 final int userId, final int reason, final ManagedServiceInfo listener) {
Svetoslav Ganov835835e2013-08-04 20:17:52 -07002926 // In enqueueNotificationInternal notifications are added by scheduling the
2927 // work on the worker handler. Hence, we also schedule the cancel on this
2928 // handler to avoid a scenario where an add notification call followed by a
2929 // remove notification call ends up in not removing the notification.
2930 mHandler.post(new Runnable() {
2931 @Override
2932 public void run() {
Christoph Studere4ef156b2014-07-04 18:41:57 +02002933 String listenerName = listener == null ? null : listener.component.toShortString();
Chris Wrenbddb5bc2015-03-04 08:47:46 -08002934 if (DBG) EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, id, tag,
2935 userId, mustHaveFlags, mustNotHaveFlags, reason, listenerName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002936
Svetoslav Ganov835835e2013-08-04 20:17:52 -07002937 synchronized (mNotificationList) {
2938 int index = indexOfNotificationLocked(pkg, tag, id, userId);
2939 if (index >= 0) {
2940 NotificationRecord r = mNotificationList.get(index);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08002941
Christoph Studer546bec82014-03-14 12:17:12 +01002942 // Ideally we'd do this in the caller of this method. However, that would
2943 // require the caller to also find the notification.
2944 if (reason == REASON_DELEGATE_CLICK) {
2945 mUsageStats.registerClickedByUser(r);
2946 }
2947
Svetoslav Ganov835835e2013-08-04 20:17:52 -07002948 if ((r.getNotification().flags & mustHaveFlags) != mustHaveFlags) {
2949 return;
2950 }
2951 if ((r.getNotification().flags & mustNotHaveFlags) != 0) {
2952 return;
2953 }
2954
2955 mNotificationList.remove(index);
2956
Christoph Studer546bec82014-03-14 12:17:12 +01002957 cancelNotificationLocked(r, sendDelete, reason);
Christoph Studer265c1052014-07-23 17:14:33 +02002958 cancelGroupChildrenLocked(r, callingUid, callingPid, listenerName,
2959 REASON_GROUP_SUMMARY_CANCELED);
Svetoslav Ganov835835e2013-08-04 20:17:52 -07002960 updateLightsLocked();
2961 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002962 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002963 }
Svetoslav Ganov835835e2013-08-04 20:17:52 -07002964 });
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002965 }
2966
2967 /**
Daniel Sandler321e9c52012-10-12 10:59:26 -07002968 * Determine whether the userId applies to the notification in question, either because
2969 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard).
2970 */
2971 private boolean notificationMatchesUserId(NotificationRecord r, int userId) {
2972 return
2973 // looking for USER_ALL notifications? match everything
2974 userId == UserHandle.USER_ALL
2975 // a notification sent to USER_ALL matches any query
Daniel Sandlerfde19b12013-01-17 00:21:05 -05002976 || r.getUserId() == UserHandle.USER_ALL
Daniel Sandler321e9c52012-10-12 10:59:26 -07002977 // an exact user match
Daniel Sandlerfde19b12013-01-17 00:21:05 -05002978 || r.getUserId() == userId;
Daniel Sandler321e9c52012-10-12 10:59:26 -07002979 }
2980
2981 /**
Kenny Guy3a7c4a52014-03-03 18:24:03 +00002982 * Determine whether the userId applies to the notification in question, either because
2983 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard) or
Kenny Guy2a764942014-04-02 13:29:20 +01002984 * because it matches one of the users profiles.
Kenny Guy3a7c4a52014-03-03 18:24:03 +00002985 */
Kenny Guy2a764942014-04-02 13:29:20 +01002986 private boolean notificationMatchesCurrentProfiles(NotificationRecord r, int userId) {
Kenny Guya263e4e2014-03-03 18:24:03 +00002987 return notificationMatchesUserId(r, userId)
John Spurlockb408e8e2014-04-23 21:12:45 -04002988 || mUserProfiles.isCurrentProfile(r.getUserId());
Kenny Guy3a7c4a52014-03-03 18:24:03 +00002989 }
2990
2991 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002992 * Cancels all notifications from a given package that have all of the
2993 * {@code mustHaveFlags}.
2994 */
John Spurlocke6a7d932014-03-13 12:29:00 -04002995 boolean cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, int mustHaveFlags,
2996 int mustNotHaveFlags, boolean doit, int userId, int reason,
John Spurlock7340fc82014-04-24 18:50:12 -04002997 ManagedServiceInfo listener) {
Christoph Studere4ef156b2014-07-04 18:41:57 +02002998 String listenerName = listener == null ? null : listener.component.toShortString();
John Spurlocke6a7d932014-03-13 12:29:00 -04002999 EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
3000 pkg, userId, mustHaveFlags, mustNotHaveFlags, reason,
Christoph Studere4ef156b2014-07-04 18:41:57 +02003001 listenerName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003002
3003 synchronized (mNotificationList) {
3004 final int N = mNotificationList.size();
Christoph Studere4ef156b2014-07-04 18:41:57 +02003005 ArrayList<NotificationRecord> canceledNotifications = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003006 for (int i = N-1; i >= 0; --i) {
3007 NotificationRecord r = mNotificationList.get(i);
Daniel Sandler321e9c52012-10-12 10:59:26 -07003008 if (!notificationMatchesUserId(r, userId)) {
Dianne Hackborn41203752012-08-31 14:05:51 -07003009 continue;
3010 }
Amith Yamasani5ec00e92012-11-07 16:58:30 -08003011 // Don't remove notifications to all, if there's no package name specified
Daniel Sandlerfde19b12013-01-17 00:21:05 -05003012 if (r.getUserId() == UserHandle.USER_ALL && pkg == null) {
Amith Yamasani5ec00e92012-11-07 16:58:30 -08003013 continue;
3014 }
Daniel Sandlerfde19b12013-01-17 00:21:05 -05003015 if ((r.getFlags() & mustHaveFlags) != mustHaveFlags) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003016 continue;
3017 }
Daniel Sandlerfde19b12013-01-17 00:21:05 -05003018 if ((r.getFlags() & mustNotHaveFlags) != 0) {
Dianne Hackbornd8a43f62009-08-17 23:33:56 -07003019 continue;
3020 }
Daniel Sandler4f91efd2013-04-25 16:38:41 -04003021 if (pkg != null && !r.sbn.getPackageName().equals(pkg)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003022 continue;
3023 }
Christoph Studere4ef156b2014-07-04 18:41:57 +02003024 if (canceledNotifications == null) {
3025 canceledNotifications = new ArrayList<>();
3026 }
3027 canceledNotifications.add(r);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08003028 if (!doit) {
3029 return true;
3030 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003031 mNotificationList.remove(i);
Christoph Studer546bec82014-03-14 12:17:12 +01003032 cancelNotificationLocked(r, false, reason);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003033 }
Christoph Studere4ef156b2014-07-04 18:41:57 +02003034 if (doit && canceledNotifications != null) {
3035 final int M = canceledNotifications.size();
3036 for (int i = 0; i < M; i++) {
3037 cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid,
Christoph Studer265c1052014-07-23 17:14:33 +02003038 listenerName, REASON_GROUP_SUMMARY_CANCELED);
Christoph Studere4ef156b2014-07-04 18:41:57 +02003039 }
3040 }
3041 if (canceledNotifications != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003042 updateLightsLocked();
3043 }
Christoph Studere4ef156b2014-07-04 18:41:57 +02003044 return canceledNotifications != null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003045 }
3046 }
3047
Adam Lesinski350159c2014-03-27 11:15:11 -07003048 void cancelAllLocked(int callingUid, int callingPid, int userId, int reason,
John Spurlock7340fc82014-04-24 18:50:12 -04003049 ManagedServiceInfo listener, boolean includeCurrentProfiles) {
Christoph Studere4ef156b2014-07-04 18:41:57 +02003050 String listenerName = listener == null ? null : listener.component.toShortString();
John Spurlocke6a7d932014-03-13 12:29:00 -04003051 EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
Christoph Studere4ef156b2014-07-04 18:41:57 +02003052 null, userId, 0, 0, reason, listenerName);
Christoph Studer546bec82014-03-14 12:17:12 +01003053
Christoph Studere4ef156b2014-07-04 18:41:57 +02003054 ArrayList<NotificationRecord> canceledNotifications = null;
Adam Lesinskie8240262014-03-26 16:01:00 -07003055 final int N = mNotificationList.size();
3056 for (int i=N-1; i>=0; i--) {
3057 NotificationRecord r = mNotificationList.get(i);
Kenny Guya263e4e2014-03-03 18:24:03 +00003058 if (includeCurrentProfiles) {
3059 if (!notificationMatchesCurrentProfiles(r, userId)) {
3060 continue;
3061 }
3062 } else {
3063 if (!notificationMatchesUserId(r, userId)) {
3064 continue;
3065 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003066 }
3067
Adam Lesinskie8240262014-03-26 16:01:00 -07003068 if ((r.getFlags() & (Notification.FLAG_ONGOING_EVENT
3069 | Notification.FLAG_NO_CLEAR)) == 0) {
3070 mNotificationList.remove(i);
Christoph Studer546bec82014-03-14 12:17:12 +01003071 cancelNotificationLocked(r, true, reason);
Christoph Studere4ef156b2014-07-04 18:41:57 +02003072 // Make a note so we can cancel children later.
3073 if (canceledNotifications == null) {
3074 canceledNotifications = new ArrayList<>();
3075 }
3076 canceledNotifications.add(r);
Adam Lesinskie8240262014-03-26 16:01:00 -07003077 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003078 }
Christoph Studere4ef156b2014-07-04 18:41:57 +02003079 int M = canceledNotifications != null ? canceledNotifications.size() : 0;
3080 for (int i = 0; i < M; i++) {
3081 cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid,
Christoph Studer265c1052014-07-23 17:14:33 +02003082 listenerName, REASON_GROUP_SUMMARY_CANCELED);
Christoph Studere4ef156b2014-07-04 18:41:57 +02003083 }
Adam Lesinskie8240262014-03-26 16:01:00 -07003084 updateLightsLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003085 }
3086
Christoph Studere4ef156b2014-07-04 18:41:57 +02003087 // Warning: The caller is responsible for invoking updateLightsLocked().
3088 private void cancelGroupChildrenLocked(NotificationRecord r, int callingUid, int callingPid,
Christoph Studer265c1052014-07-23 17:14:33 +02003089 String listenerName, int reason) {
Christoph Studere4ef156b2014-07-04 18:41:57 +02003090 Notification n = r.getNotification();
Christoph Studer3f31f5d2014-07-31 16:55:32 +02003091 if (!n.isGroupSummary()) {
Christoph Studere4ef156b2014-07-04 18:41:57 +02003092 return;
3093 }
3094
3095 String pkg = r.sbn.getPackageName();
3096 int userId = r.getUserId();
3097
3098 if (pkg == null) {
3099 if (DBG) Log.e(TAG, "No package for group summary: " + r.getKey());
3100 return;
3101 }
3102
3103 final int N = mNotificationList.size();
3104 for (int i = N - 1; i >= 0; i--) {
3105 NotificationRecord childR = mNotificationList.get(i);
Christoph Studere4ef156b2014-07-04 18:41:57 +02003106 StatusBarNotification childSbn = childR.sbn;
Christoph Studerc44caa92014-08-22 19:16:00 +02003107 if (childR.getNotification().isGroupChild() &&
3108 childR.getGroupKey().equals(r.getGroupKey())) {
Christoph Studer265c1052014-07-23 17:14:33 +02003109 EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, childSbn.getId(),
3110 childSbn.getTag(), userId, 0, 0, reason, listenerName);
Christoph Studere4ef156b2014-07-04 18:41:57 +02003111 mNotificationList.remove(i);
Christoph Studer265c1052014-07-23 17:14:33 +02003112 cancelNotificationLocked(childR, false, reason);
Christoph Studere4ef156b2014-07-04 18:41:57 +02003113 }
3114 }
3115 }
3116
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003117 // lock on mNotificationList
Adam Lesinski182f73f2013-12-05 16:48:06 -08003118 void updateLightsLocked()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003119 {
The Android Open Source Project10592532009-03-18 17:39:46 -07003120 // handle notification lights
Chris Wren6054e612014-11-25 17:16:46 -05003121 NotificationRecord ledNotification = null;
3122 while (ledNotification == null && !mLights.isEmpty()) {
3123 final String owner = mLights.get(mLights.size() - 1);
3124 ledNotification = mNotificationsByKey.get(owner);
3125 if (ledNotification == null) {
3126 Slog.wtfStack(TAG, "LED Notification does not exist: " + owner);
3127 mLights.remove(owner);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003128 }
3129 }
Mike Lockwoodc22404a2009-12-02 11:15:02 -05003130
Mike Lockwood63b5ad92011-08-30 09:55:30 -04003131 // Don't flash while we are in a call or screen is on
Chris Wren6054e612014-11-25 17:16:46 -05003132 if (ledNotification == null || mInCall || mScreenOn) {
Mike Lockwood3cb67a32009-11-27 14:25:58 -05003133 mNotificationLight.turnOff();
John Spurlockcb566aa2014-08-03 22:58:28 -04003134 mStatusBar.notificationLightOff();
The Android Open Source Project10592532009-03-18 17:39:46 -07003135 } else {
Chris Wren6054e612014-11-25 17:16:46 -05003136 final Notification ledno = ledNotification.sbn.getNotification();
Daniel Sandlerfde19b12013-01-17 00:21:05 -05003137 int ledARGB = ledno.ledARGB;
3138 int ledOnMS = ledno.ledOnMS;
3139 int ledOffMS = ledno.ledOffMS;
3140 if ((ledno.defaults & Notification.DEFAULT_LIGHTS) != 0) {
Mike Lockwood670f9322010-01-20 12:13:36 -05003141 ledARGB = mDefaultNotificationColor;
3142 ledOnMS = mDefaultNotificationLedOn;
3143 ledOffMS = mDefaultNotificationLedOff;
3144 }
3145 if (mNotificationPulseEnabled) {
3146 // pulse repeatedly
Adam Lesinski182f73f2013-12-05 16:48:06 -08003147 mNotificationLight.setFlashing(ledARGB, Light.LIGHT_FLASH_TIMED,
Mike Lockwood670f9322010-01-20 12:13:36 -05003148 ledOnMS, ledOffMS);
Mike Lockwood670f9322010-01-20 12:13:36 -05003149 }
John Spurlock39292322014-08-13 11:00:59 -04003150 // let SystemUI make an independent decision
3151 mStatusBar.notificationLightPulse(ledARGB, ledOnMS, ledOffMS);
The Android Open Source Project10592532009-03-18 17:39:46 -07003152 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003153 }
3154
3155 // lock on mNotificationList
Adam Lesinski182f73f2013-12-05 16:48:06 -08003156 int indexOfNotificationLocked(String pkg, String tag, int id, int userId)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003157 {
3158 ArrayList<NotificationRecord> list = mNotificationList;
3159 final int len = list.size();
3160 for (int i=0; i<len; i++) {
3161 NotificationRecord r = list.get(i);
Vladimir Marko2526f332013-09-11 11:13:55 +01003162 if (notificationMatchesUserId(r, userId) && r.sbn.getId() == id &&
3163 TextUtils.equals(r.sbn.getTag(), tag) && r.sbn.getPackageName().equals(pkg)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003164 return i;
3165 }
3166 }
3167 return -1;
3168 }
3169
Christoph Studer71f18fd2014-05-20 17:02:04 +02003170 // lock on mNotificationList
3171 int indexOfNotificationLocked(String key) {
Christoph Studerc5115552014-06-12 20:22:31 +02003172 final int N = mNotificationList.size();
3173 for (int i = 0; i < N; i++) {
3174 if (key.equals(mNotificationList.get(i).getKey())) {
3175 return i;
3176 }
Christoph Studer71f18fd2014-05-20 17:02:04 +02003177 }
Christoph Studerc5115552014-06-12 20:22:31 +02003178 return -1;
Christoph Studer71f18fd2014-05-20 17:02:04 +02003179 }
3180
Mike Lockwoodc22404a2009-12-02 11:15:02 -05003181 private void updateNotificationPulse() {
3182 synchronized (mNotificationList) {
3183 updateLightsLocked();
3184 }
3185 }
John Spurlocke677d712014-02-13 12:52:19 -05003186
John Spurlock7340fc82014-04-24 18:50:12 -04003187 private static boolean isUidSystem(int uid) {
3188 final int appid = UserHandle.getAppId(uid);
3189 return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0);
3190 }
John Spurlockb408e8e2014-04-23 21:12:45 -04003191
John Spurlock7340fc82014-04-24 18:50:12 -04003192 private static boolean isCallerSystem() {
3193 return isUidSystem(Binder.getCallingUid());
3194 }
3195
3196 private static void checkCallerIsSystem() {
3197 if (isCallerSystem()) {
3198 return;
3199 }
3200 throw new SecurityException("Disallowed call for uid " + Binder.getCallingUid());
3201 }
3202
3203 private static void checkCallerIsSystemOrSameApp(String pkg) {
3204 if (isCallerSystem()) {
3205 return;
3206 }
3207 final int uid = Binder.getCallingUid();
3208 try {
3209 ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo(
3210 pkg, 0, UserHandle.getCallingUserId());
Dan Sandler09afc2e2014-07-18 14:29:20 -04003211 if (ai == null) {
3212 throw new SecurityException("Unknown package " + pkg);
3213 }
John Spurlock7340fc82014-04-24 18:50:12 -04003214 if (!UserHandle.isSameApp(ai.uid, uid)) {
3215 throw new SecurityException("Calling uid " + uid + " gave package"
3216 + pkg + " which is owned by uid " + ai.uid);
3217 }
3218 } catch (RemoteException re) {
3219 throw new SecurityException("Unknown package " + pkg + "\n" + re);
3220 }
3221 }
3222
John Spurlock32fe4c62014-10-02 12:16:02 -04003223 private static String callStateToString(int state) {
3224 switch (state) {
3225 case TelephonyManager.CALL_STATE_IDLE: return "CALL_STATE_IDLE";
3226 case TelephonyManager.CALL_STATE_RINGING: return "CALL_STATE_RINGING";
3227 case TelephonyManager.CALL_STATE_OFFHOOK: return "CALL_STATE_OFFHOOK";
3228 default: return "CALL_STATE_UNKNOWN_" + state;
3229 }
3230 }
3231
3232 private void listenForCallState() {
3233 TelephonyManager.from(getContext()).listen(new PhoneStateListener() {
3234 @Override
3235 public void onCallStateChanged(int state, String incomingNumber) {
3236 if (mCallState == state) return;
3237 if (DBG) Slog.d(TAG, "Call state changed: " + callStateToString(state));
3238 mCallState = state;
3239 }
3240 }, PhoneStateListener.LISTEN_CALL_STATE);
3241 }
3242
Christoph Studer05ad4822014-05-16 14:16:03 +02003243 /**
3244 * Generates a NotificationRankingUpdate from 'sbns', considering only
3245 * notifications visible to the given listener.
Chris Wren333a61c2014-05-28 16:40:57 -04003246 *
3247 * <p>Caller must hold a lock on mNotificationList.</p>
Christoph Studer05ad4822014-05-16 14:16:03 +02003248 */
Chris Wren333a61c2014-05-28 16:40:57 -04003249 private NotificationRankingUpdate makeRankingUpdateLocked(ManagedServiceInfo info) {
Christoph Studer05ad4822014-05-16 14:16:03 +02003250 int speedBumpIndex = -1;
Chris Wren333a61c2014-05-28 16:40:57 -04003251 final int N = mNotificationList.size();
3252 ArrayList<String> keys = new ArrayList<String>(N);
Christoph Studer1d599da2014-06-12 15:25:59 +02003253 ArrayList<String> interceptedKeys = new ArrayList<String>(N);
Chris Wren3ad4e3a2014-09-02 17:23:51 -04003254 Bundle visibilityOverrides = new Bundle();
Julia Reynoldsf612869ae2015-11-05 16:48:55 -05003255 Bundle suppressedVisualEffects = new Bundle();
Chris Wren333a61c2014-05-28 16:40:57 -04003256 for (int i = 0; i < N; i++) {
3257 NotificationRecord record = mNotificationList.get(i);
Christoph Studercef37cf2014-07-25 14:18:17 +02003258 if (!isVisibleToListener(record.sbn, info)) {
Christoph Studer05ad4822014-05-16 14:16:03 +02003259 continue;
3260 }
Chris Wren333a61c2014-05-28 16:40:57 -04003261 keys.add(record.sbn.getKey());
3262 if (record.isIntercepted()) {
Christoph Studer1d599da2014-06-12 15:25:59 +02003263 interceptedKeys.add(record.sbn.getKey());
Julia Reynoldsf612869ae2015-11-05 16:48:55 -05003264
Christoph Studer05ad4822014-05-16 14:16:03 +02003265 }
Julia Reynoldsf612869ae2015-11-05 16:48:55 -05003266 suppressedVisualEffects.putInt(
3267 record.sbn.getKey(), record.getSuppressedVisualEffects());
Chris Wren3ad4e3a2014-09-02 17:23:51 -04003268 if (record.getPackageVisibilityOverride()
3269 != NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE) {
3270 visibilityOverrides.putInt(record.sbn.getKey(),
3271 record.getPackageVisibilityOverride());
3272 }
Christoph Studer327bf782014-09-05 11:40:01 +02003273 // Find first min-prio notification for speedbump placement.
Christoph Studer05ad4822014-05-16 14:16:03 +02003274 if (speedBumpIndex == -1 &&
Christoph Studer327bf782014-09-05 11:40:01 +02003275 // Intrusiveness trumps priority, hence ignore intrusives.
3276 !record.isRecentlyIntrusive() &&
Christoph Studerfbac2142014-09-05 17:40:43 +02003277 // Currently, package priority is either PRIORITY_DEFAULT or PRIORITY_MAX, so
3278 // scanning for PRIORITY_MIN within the package bucket PRIORITY_DEFAULT
3279 // (or lower as a safeguard) is sufficient to find the speedbump index.
3280 // We'll have to revisit this when more package priority buckets are introduced.
3281 record.getPackagePriority() <= Notification.PRIORITY_DEFAULT &&
Chris Wren333a61c2014-05-28 16:40:57 -04003282 record.sbn.getNotification().priority == Notification.PRIORITY_MIN) {
Christoph Studer05ad4822014-05-16 14:16:03 +02003283 speedBumpIndex = keys.size() - 1;
3284 }
3285 }
3286 String[] keysAr = keys.toArray(new String[keys.size()]);
Christoph Studer1d599da2014-06-12 15:25:59 +02003287 String[] interceptedKeysAr = interceptedKeys.toArray(new String[interceptedKeys.size()]);
Chris Wren3ad4e3a2014-09-02 17:23:51 -04003288 return new NotificationRankingUpdate(keysAr, interceptedKeysAr, visibilityOverrides,
Julia Reynoldsf612869ae2015-11-05 16:48:55 -05003289 speedBumpIndex, suppressedVisualEffects);
Christoph Studer05ad4822014-05-16 14:16:03 +02003290 }
3291
Christoph Studercef37cf2014-07-25 14:18:17 +02003292 private boolean isVisibleToListener(StatusBarNotification sbn, ManagedServiceInfo listener) {
3293 if (!listener.enabledAndUserMatches(sbn.getUserId())) {
3294 return false;
3295 }
Justin Koh8d11a5a2014-08-04 18:29:49 -07003296 // TODO: remove this for older listeners.
Christoph Studercef37cf2014-07-25 14:18:17 +02003297 return true;
3298 }
3299
John Spurlock7340fc82014-04-24 18:50:12 -04003300 public class NotificationListeners extends ManagedServices {
3301
Christoph Studerb82bc782014-08-20 14:29:43 +02003302 private final ArraySet<ManagedServiceInfo> mLightTrimListeners = new ArraySet<>();
Christoph Studer265c1052014-07-23 17:14:33 +02003303 private boolean mNotificationGroupsDesired;
Christoph Studerb82bc782014-08-20 14:29:43 +02003304
John Spurlock7340fc82014-04-24 18:50:12 -04003305 public NotificationListeners() {
3306 super(getContext(), mHandler, mNotificationList, mUserProfiles);
3307 }
3308
3309 @Override
3310 protected Config getConfig() {
3311 Config c = new Config();
3312 c.caption = "notification listener";
3313 c.serviceInterface = NotificationListenerService.SERVICE_INTERFACE;
3314 c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_LISTENERS;
3315 c.bindPermission = android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE;
3316 c.settingsAction = Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS;
3317 c.clientLabel = R.string.notification_listener_binding_label;
3318 return c;
3319 }
3320
3321 @Override
3322 protected IInterface asInterface(IBinder binder) {
3323 return INotificationListener.Stub.asInterface(binder);
3324 }
3325
3326 @Override
John Spurlock3b98b3f2014-05-01 09:08:48 -04003327 public void onServiceAdded(ManagedServiceInfo info) {
3328 final INotificationListener listener = (INotificationListener) info.service;
Chris Wren333a61c2014-05-28 16:40:57 -04003329 final NotificationRankingUpdate update;
Christoph Studer05ad4822014-05-16 14:16:03 +02003330 synchronized (mNotificationList) {
Christoph Studer265c1052014-07-23 17:14:33 +02003331 updateNotificationGroupsDesiredLocked();
Chris Wren333a61c2014-05-28 16:40:57 -04003332 update = makeRankingUpdateLocked(info);
Christoph Studer05ad4822014-05-16 14:16:03 +02003333 }
John Spurlock7340fc82014-04-24 18:50:12 -04003334 try {
Chris Wren333a61c2014-05-28 16:40:57 -04003335 listener.onListenerConnected(update);
John Spurlock7340fc82014-04-24 18:50:12 -04003336 } catch (RemoteException e) {
3337 // we tried
3338 }
3339 }
3340
John Spurlock1fa865f2014-07-21 14:56:39 -04003341 @Override
3342 protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
John Spurlockd8afe3c2014-08-01 14:04:07 -04003343 if (mListenersDisablingEffects.remove(removed)) {
3344 updateListenerHintsLocked();
Christoph Studer0d6ef4b2014-12-02 15:00:48 +01003345 updateEffectsSuppressorLocked();
John Spurlock1fa865f2014-07-21 14:56:39 -04003346 }
Christoph Studerb82bc782014-08-20 14:29:43 +02003347 mLightTrimListeners.remove(removed);
Christoph Studer265c1052014-07-23 17:14:33 +02003348 updateNotificationGroupsDesiredLocked();
Christoph Studerb82bc782014-08-20 14:29:43 +02003349 }
3350
3351 public void setOnNotificationPostedTrimLocked(ManagedServiceInfo info, int trim) {
3352 if (trim == TRIM_LIGHT) {
3353 mLightTrimListeners.add(info);
3354 } else {
3355 mLightTrimListeners.remove(info);
3356 }
3357 }
3358
3359 public int getOnNotificationPostedTrim(ManagedServiceInfo info) {
3360 return mLightTrimListeners.contains(info) ? TRIM_LIGHT : TRIM_FULL;
3361
John Spurlock1fa865f2014-07-21 14:56:39 -04003362 }
3363
John Spurlock7340fc82014-04-24 18:50:12 -04003364 /**
3365 * asynchronously notify all listeners about a new notification
Christoph Studercef37cf2014-07-25 14:18:17 +02003366 *
3367 * <p>
3368 * Also takes care of removing a notification that has been visible to a listener before,
3369 * but isn't anymore.
John Spurlock7340fc82014-04-24 18:50:12 -04003370 */
Christoph Studercef37cf2014-07-25 14:18:17 +02003371 public void notifyPostedLocked(StatusBarNotification sbn, StatusBarNotification oldSbn) {
Christoph Studerb82bc782014-08-20 14:29:43 +02003372 // Lazily initialized snapshots of the notification.
3373 StatusBarNotification sbnClone = null;
3374 StatusBarNotification sbnCloneLight = null;
3375
John Spurlock7340fc82014-04-24 18:50:12 -04003376 for (final ManagedServiceInfo info : mServices) {
Christoph Studercef37cf2014-07-25 14:18:17 +02003377 boolean sbnVisible = isVisibleToListener(sbn, info);
3378 boolean oldSbnVisible = oldSbn != null ? isVisibleToListener(oldSbn, info) : false;
3379 // This notification hasn't been and still isn't visible -> ignore.
3380 if (!oldSbnVisible && !sbnVisible) {
Christoph Studer05ad4822014-05-16 14:16:03 +02003381 continue;
Chris Wrenf9536642014-04-17 10:01:54 -04003382 }
Chris Wren333a61c2014-05-28 16:40:57 -04003383 final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
Christoph Studercef37cf2014-07-25 14:18:17 +02003384
3385 // This notification became invisible -> remove the old one.
3386 if (oldSbnVisible && !sbnVisible) {
3387 final StatusBarNotification oldSbnLightClone = oldSbn.cloneLight();
3388 mHandler.post(new Runnable() {
3389 @Override
3390 public void run() {
3391 notifyRemoved(info, oldSbnLightClone, update);
3392 }
3393 });
Christoph Studer05ad4822014-05-16 14:16:03 +02003394 continue;
3395 }
Christoph Studercef37cf2014-07-25 14:18:17 +02003396
Christoph Studerb82bc782014-08-20 14:29:43 +02003397 final int trim = mListeners.getOnNotificationPostedTrim(info);
3398
3399 if (trim == TRIM_LIGHT && sbnCloneLight == null) {
3400 sbnCloneLight = sbn.cloneLight();
3401 } else if (trim == TRIM_FULL && sbnClone == null) {
3402 sbnClone = sbn.clone();
3403 }
3404 final StatusBarNotification sbnToPost =
3405 (trim == TRIM_FULL) ? sbnClone : sbnCloneLight;
3406
Christoph Studer05ad4822014-05-16 14:16:03 +02003407 mHandler.post(new Runnable() {
3408 @Override
3409 public void run() {
Christoph Studerb82bc782014-08-20 14:29:43 +02003410 notifyPosted(info, sbnToPost, update);
Christoph Studer05ad4822014-05-16 14:16:03 +02003411 }
3412 });
Kenny Guy3a7c4a52014-03-03 18:24:03 +00003413 }
3414 }
Kenny Guy3a7c4a52014-03-03 18:24:03 +00003415
John Spurlock7340fc82014-04-24 18:50:12 -04003416 /**
3417 * asynchronously notify all listeners about a removed notification
3418 */
Chris Wren333a61c2014-05-28 16:40:57 -04003419 public void notifyRemovedLocked(StatusBarNotification sbn) {
John Spurlock7340fc82014-04-24 18:50:12 -04003420 // make a copy in case changes are made to the underlying Notification object
3421 // NOTE: this copy is lightweight: it doesn't include heavyweight parts of the
3422 // notification
3423 final StatusBarNotification sbnLight = sbn.cloneLight();
Chris Wrenf9536642014-04-17 10:01:54 -04003424 for (final ManagedServiceInfo info : mServices) {
Christoph Studercef37cf2014-07-25 14:18:17 +02003425 if (!isVisibleToListener(sbn, info)) {
Christoph Studer05ad4822014-05-16 14:16:03 +02003426 continue;
Chris Wrenf9536642014-04-17 10:01:54 -04003427 }
Chris Wren333a61c2014-05-28 16:40:57 -04003428 final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
Christoph Studer05ad4822014-05-16 14:16:03 +02003429 mHandler.post(new Runnable() {
3430 @Override
3431 public void run() {
Christoph Studercef37cf2014-07-25 14:18:17 +02003432 notifyRemoved(info, sbnLight, update);
Christoph Studer05ad4822014-05-16 14:16:03 +02003433 }
3434 });
Chris Wrenf9536642014-04-17 10:01:54 -04003435 }
3436 }
3437
3438 /**
3439 * asynchronously notify all listeners about a reordering of notifications
Chris Wrenf9536642014-04-17 10:01:54 -04003440 */
Chris Wren333a61c2014-05-28 16:40:57 -04003441 public void notifyRankingUpdateLocked() {
Chris Wrenf9536642014-04-17 10:01:54 -04003442 for (final ManagedServiceInfo serviceInfo : mServices) {
Christoph Studer05ad4822014-05-16 14:16:03 +02003443 if (!serviceInfo.isEnabledForCurrentProfiles()) {
3444 continue;
3445 }
Christoph Studercef37cf2014-07-25 14:18:17 +02003446 final NotificationRankingUpdate update = makeRankingUpdateLocked(serviceInfo);
John Spurlock7340fc82014-04-24 18:50:12 -04003447 mHandler.post(new Runnable() {
3448 @Override
3449 public void run() {
Chris Wren333a61c2014-05-28 16:40:57 -04003450 notifyRankingUpdate(serviceInfo, update);
John Spurlock7340fc82014-04-24 18:50:12 -04003451 }
3452 });
Kenny Guya263e4e2014-03-03 18:24:03 +00003453 }
Kenny Guya263e4e2014-03-03 18:24:03 +00003454 }
Kenny Guya263e4e2014-03-03 18:24:03 +00003455
John Spurlockd8afe3c2014-08-01 14:04:07 -04003456 public void notifyListenerHintsChangedLocked(final int hints) {
John Spurlock1fa865f2014-07-21 14:56:39 -04003457 for (final ManagedServiceInfo serviceInfo : mServices) {
3458 if (!serviceInfo.isEnabledForCurrentProfiles()) {
3459 continue;
3460 }
3461 mHandler.post(new Runnable() {
3462 @Override
3463 public void run() {
John Spurlockd8afe3c2014-08-01 14:04:07 -04003464 notifyListenerHintsChanged(serviceInfo, hints);
John Spurlock1fa865f2014-07-21 14:56:39 -04003465 }
3466 });
3467 }
3468 }
3469
Christoph Studer85a384b2014-08-27 20:16:15 +02003470 public void notifyInterruptionFilterChanged(final int interruptionFilter) {
3471 for (final ManagedServiceInfo serviceInfo : mServices) {
3472 if (!serviceInfo.isEnabledForCurrentProfiles()) {
3473 continue;
3474 }
3475 mHandler.post(new Runnable() {
3476 @Override
3477 public void run() {
3478 notifyInterruptionFilterChanged(serviceInfo, interruptionFilter);
3479 }
3480 });
3481 }
3482 }
3483
Christoph Studercef37cf2014-07-25 14:18:17 +02003484 private void notifyPosted(final ManagedServiceInfo info,
Christoph Studer05ad4822014-05-16 14:16:03 +02003485 final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) {
John Spurlock7340fc82014-04-24 18:50:12 -04003486 final INotificationListener listener = (INotificationListener)info.service;
Griff Hazen84a00ea2014-09-02 17:10:47 -07003487 StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
John Spurlock7340fc82014-04-24 18:50:12 -04003488 try {
Griff Hazen84a00ea2014-09-02 17:10:47 -07003489 listener.onNotificationPosted(sbnHolder, rankingUpdate);
John Spurlock7340fc82014-04-24 18:50:12 -04003490 } catch (RemoteException ex) {
3491 Log.e(TAG, "unable to notify listener (posted): " + listener, ex);
3492 }
3493 }
3494
Christoph Studercef37cf2014-07-25 14:18:17 +02003495 private void notifyRemoved(ManagedServiceInfo info, StatusBarNotification sbn,
Christoph Studer05ad4822014-05-16 14:16:03 +02003496 NotificationRankingUpdate rankingUpdate) {
John Spurlock7340fc82014-04-24 18:50:12 -04003497 if (!info.enabledAndUserMatches(sbn.getUserId())) {
3498 return;
3499 }
Christoph Studer05ad4822014-05-16 14:16:03 +02003500 final INotificationListener listener = (INotificationListener) info.service;
Griff Hazen84a00ea2014-09-02 17:10:47 -07003501 StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
John Spurlock7340fc82014-04-24 18:50:12 -04003502 try {
Griff Hazen84a00ea2014-09-02 17:10:47 -07003503 listener.onNotificationRemoved(sbnHolder, rankingUpdate);
John Spurlock7340fc82014-04-24 18:50:12 -04003504 } catch (RemoteException ex) {
3505 Log.e(TAG, "unable to notify listener (removed): " + listener, ex);
John Spurlockb408e8e2014-04-23 21:12:45 -04003506 }
Kenny Guya263e4e2014-03-03 18:24:03 +00003507 }
Chris Wrenf9536642014-04-17 10:01:54 -04003508
Christoph Studer05ad4822014-05-16 14:16:03 +02003509 private void notifyRankingUpdate(ManagedServiceInfo info,
3510 NotificationRankingUpdate rankingUpdate) {
3511 final INotificationListener listener = (INotificationListener) info.service;
Chris Wrenf9536642014-04-17 10:01:54 -04003512 try {
Christoph Studer05ad4822014-05-16 14:16:03 +02003513 listener.onNotificationRankingUpdate(rankingUpdate);
Chris Wrenf9536642014-04-17 10:01:54 -04003514 } catch (RemoteException ex) {
3515 Log.e(TAG, "unable to notify listener (ranking update): " + listener, ex);
3516 }
3517 }
John Spurlock1fa865f2014-07-21 14:56:39 -04003518
John Spurlockd8afe3c2014-08-01 14:04:07 -04003519 private void notifyListenerHintsChanged(ManagedServiceInfo info, int hints) {
John Spurlock1fa865f2014-07-21 14:56:39 -04003520 final INotificationListener listener = (INotificationListener) info.service;
3521 try {
John Spurlockd8afe3c2014-08-01 14:04:07 -04003522 listener.onListenerHintsChanged(hints);
John Spurlock1fa865f2014-07-21 14:56:39 -04003523 } catch (RemoteException ex) {
John Spurlockd8afe3c2014-08-01 14:04:07 -04003524 Log.e(TAG, "unable to notify listener (listener hints): " + listener, ex);
John Spurlock1fa865f2014-07-21 14:56:39 -04003525 }
3526 }
Justin Koh38156c52014-06-04 13:57:49 -07003527
Christoph Studer85a384b2014-08-27 20:16:15 +02003528 private void notifyInterruptionFilterChanged(ManagedServiceInfo info,
3529 int interruptionFilter) {
3530 final INotificationListener listener = (INotificationListener) info.service;
3531 try {
3532 listener.onInterruptionFilterChanged(interruptionFilter);
3533 } catch (RemoteException ex) {
3534 Log.e(TAG, "unable to notify listener (interruption filter): " + listener, ex);
3535 }
3536 }
3537
Justin Koh38156c52014-06-04 13:57:49 -07003538 private boolean isListenerPackage(String packageName) {
3539 if (packageName == null) {
3540 return false;
3541 }
3542 // TODO: clean up locking object later
3543 synchronized (mNotificationList) {
3544 for (final ManagedServiceInfo serviceInfo : mServices) {
3545 if (packageName.equals(serviceInfo.component.getPackageName())) {
3546 return true;
3547 }
3548 }
3549 }
3550 return false;
3551 }
Christoph Studer265c1052014-07-23 17:14:33 +02003552
3553 /**
3554 * Returns whether any of the currently registered listeners wants to receive notification
3555 * groups.
3556 *
3557 * <p>Currently we assume groups are desired by non-SystemUI listeners.</p>
3558 */
3559 public boolean notificationGroupsDesired() {
3560 return mNotificationGroupsDesired;
3561 }
3562
3563 private void updateNotificationGroupsDesiredLocked() {
3564 mNotificationGroupsDesired = true;
3565 // No listeners, no groups.
3566 if (mServices.isEmpty()) {
3567 mNotificationGroupsDesired = false;
3568 return;
3569 }
3570 // One listener: Check whether it's SysUI.
3571 if (mServices.size() == 1 &&
3572 mServices.get(0).component.getPackageName().equals("com.android.systemui")) {
3573 mNotificationGroupsDesired = false;
3574 return;
3575 }
3576 }
Kenny Guya263e4e2014-03-03 18:24:03 +00003577 }
John Spurlock25e2d242014-06-27 13:58:23 -04003578
3579 public static final class DumpFilter {
Dan Sandlera1770312015-07-10 13:59:29 -04003580 public boolean filtered = false;
John Spurlock25e2d242014-06-27 13:58:23 -04003581 public String pkgFilter;
John Spurlock50806fc2014-07-15 10:22:02 -04003582 public boolean zen;
Chris Wrene4b38802015-07-07 15:54:19 -04003583 public long since;
3584 public boolean stats;
Dan Sandlera1770312015-07-10 13:59:29 -04003585 public boolean redact = true;
John Spurlock25e2d242014-06-27 13:58:23 -04003586
3587 public static DumpFilter parseFromArguments(String[] args) {
Dan Sandlera1770312015-07-10 13:59:29 -04003588 final DumpFilter filter = new DumpFilter();
3589 for (int ai = 0; ai < args.length; ai++) {
3590 final String a = args[ai];
3591 if ("--noredact".equals(a) || "--reveal".equals(a)) {
3592 filter.redact = false;
3593 } else if ("p".equals(a) || "pkg".equals(a) || "--package".equals(a)) {
3594 if (ai < args.length-1) {
3595 ai++;
3596 filter.pkgFilter = args[ai].trim().toLowerCase();
3597 if (filter.pkgFilter.isEmpty()) {
3598 filter.pkgFilter = null;
3599 } else {
3600 filter.filtered = true;
3601 }
3602 }
3603 } else if ("--zen".equals(a) || "zen".equals(a)) {
3604 filter.filtered = true;
3605 filter.zen = true;
3606 } else if ("--stats".equals(a)) {
3607 filter.stats = true;
3608 if (ai < args.length-1) {
3609 ai++;
3610 filter.since = Long.valueOf(args[ai]);
3611 } else {
3612 filter.since = 0;
3613 }
3614 }
John Spurlock25e2d242014-06-27 13:58:23 -04003615 }
Dan Sandlera1770312015-07-10 13:59:29 -04003616 return filter;
John Spurlock25e2d242014-06-27 13:58:23 -04003617 }
3618
3619 public boolean matches(StatusBarNotification sbn) {
Dan Sandlera1770312015-07-10 13:59:29 -04003620 if (!filtered) return true;
3621 return zen ? true : sbn != null
John Spurlock50806fc2014-07-15 10:22:02 -04003622 && (matches(sbn.getPackageName()) || matches(sbn.getOpPkg()));
John Spurlock25e2d242014-06-27 13:58:23 -04003623 }
3624
3625 public boolean matches(ComponentName component) {
Dan Sandlera1770312015-07-10 13:59:29 -04003626 if (!filtered) return true;
3627 return zen ? true : component != null && matches(component.getPackageName());
John Spurlock25e2d242014-06-27 13:58:23 -04003628 }
3629
3630 public boolean matches(String pkg) {
Dan Sandlera1770312015-07-10 13:59:29 -04003631 if (!filtered) return true;
3632 return zen ? true : pkg != null && pkg.toLowerCase().contains(pkgFilter);
John Spurlock50806fc2014-07-15 10:22:02 -04003633 }
3634
3635 @Override
3636 public String toString() {
Chris Wrene4b38802015-07-07 15:54:19 -04003637 return stats ? "stats" : zen ? "zen" : ('\'' + pkgFilter + '\'');
John Spurlock25e2d242014-06-27 13:58:23 -04003638 }
3639 }
Griff Hazen84a00ea2014-09-02 17:10:47 -07003640
3641 /**
3642 * Wrapper for a StatusBarNotification object that allows transfer across a oneway
3643 * binder without sending large amounts of data over a oneway transaction.
3644 */
3645 private static final class StatusBarNotificationHolder
3646 extends IStatusBarNotificationHolder.Stub {
Griff Hazene9aac5f2014-09-05 20:04:09 -07003647 private StatusBarNotification mValue;
Griff Hazen84a00ea2014-09-02 17:10:47 -07003648
3649 public StatusBarNotificationHolder(StatusBarNotification value) {
3650 mValue = value;
3651 }
3652
Griff Hazene9aac5f2014-09-05 20:04:09 -07003653 /** Get the held value and clear it. This function should only be called once per holder */
Griff Hazen84a00ea2014-09-02 17:10:47 -07003654 @Override
3655 public StatusBarNotification get() {
Griff Hazene9aac5f2014-09-05 20:04:09 -07003656 StatusBarNotification value = mValue;
3657 mValue = null;
3658 return value;
Griff Hazen84a00ea2014-09-02 17:10:47 -07003659 }
3660 }
John Spurlock7c74f782015-06-04 13:01:42 -04003661
3662 private final class PolicyAccess {
3663 private static final String SEPARATOR = ":";
3664 private final String[] PERM = {
3665 android.Manifest.permission.ACCESS_NOTIFICATION_POLICY
3666 };
3667
3668 public boolean isPackageGranted(String pkg) {
3669 return pkg != null && getGrantedPackages().contains(pkg);
3670 }
3671
3672 public void put(String pkg, boolean granted) {
3673 if (pkg == null) return;
3674 final ArraySet<String> pkgs = getGrantedPackages();
3675 boolean changed;
3676 if (granted) {
3677 changed = pkgs.add(pkg);
3678 } else {
3679 changed = pkgs.remove(pkg);
3680 }
3681 if (!changed) return;
3682 final String setting = TextUtils.join(SEPARATOR, pkgs);
3683 final int currentUser = ActivityManager.getCurrentUser();
3684 Settings.Secure.putStringForUser(getContext().getContentResolver(),
3685 Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES,
3686 setting,
3687 currentUser);
3688 getContext().sendBroadcastAsUser(new Intent(NotificationManager
3689 .ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED)
3690 .setPackage(pkg)
3691 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), new UserHandle(currentUser), null);
3692 }
3693
3694 public ArraySet<String> getGrantedPackages() {
3695 final ArraySet<String> pkgs = new ArraySet<>();
Julia Reynoldsea6c4482015-08-13 09:01:33 -04003696
3697 long identity = Binder.clearCallingIdentity();
3698 try {
3699 final String setting = Settings.Secure.getStringForUser(
3700 getContext().getContentResolver(),
3701 Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES,
3702 ActivityManager.getCurrentUser());
3703 if (setting != null) {
3704 final String[] tokens = setting.split(SEPARATOR);
3705 for (int i = 0; i < tokens.length; i++) {
3706 String token = tokens[i];
3707 if (token != null) {
3708 token.trim();
3709 }
3710 if (TextUtils.isEmpty(token)) {
3711 continue;
3712 }
3713 pkgs.add(token);
John Spurlock7c74f782015-06-04 13:01:42 -04003714 }
John Spurlock7c74f782015-06-04 13:01:42 -04003715 }
Julia Reynoldsea6c4482015-08-13 09:01:33 -04003716 } finally {
3717 Binder.restoreCallingIdentity(identity);
John Spurlock7c74f782015-06-04 13:01:42 -04003718 }
3719 return pkgs;
3720 }
3721
3722 public String[] getRequestingPackages() throws RemoteException {
3723 final ParceledListSlice list = AppGlobals.getPackageManager()
3724 .getPackagesHoldingPermissions(PERM, 0 /*flags*/,
3725 ActivityManager.getCurrentUser());
3726 final List<PackageInfo> pkgs = list.getList();
3727 if (pkgs == null || pkgs.isEmpty()) return new String[0];
3728 final int N = pkgs.size();
3729 final String[] rt = new String[N];
3730 for (int i = 0; i < N; i++) {
3731 rt[i] = pkgs.get(i).packageName;
3732 }
3733 return rt;
3734 }
3735 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003736}