blob: bb07e9d7e881cd87a4d06f3fcb49942aab8f095f [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Adam Lesinski182f73f2013-12-05 16:48:06 -080017package com.android.server.notification;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080018
Chris Wren9fa689f2015-11-20 16:44:53 -050019import static android.service.notification.NotificationAssistantService.REASON_APP_CANCEL;
20import static android.service.notification.NotificationAssistantService.REASON_APP_CANCEL_ALL;
Jason Monk63506742015-12-16 12:06:51 -050021import 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_CLICK;
24import static android.service.notification.NotificationAssistantService.REASON_DELEGATE_ERROR;
25import static android.service.notification.NotificationAssistantService.REASON_GROUP_OPTIMIZATION;
26import static android.service.notification.NotificationAssistantService.REASON_GROUP_SUMMARY_CANCELED;
Chris Wren9fa689f2015-11-20 16:44:53 -050027import static android.service.notification.NotificationAssistantService.REASON_LISTENER_CANCEL;
28import static android.service.notification.NotificationAssistantService.REASON_LISTENER_CANCEL_ALL;
Jason Monk63506742015-12-16 12:06:51 -050029import static android.service.notification.NotificationAssistantService.REASON_PACKAGE_BANNED;
30import static android.service.notification.NotificationAssistantService.REASON_PACKAGE_CHANGED;
31import static android.service.notification.NotificationAssistantService.REASON_USER_STOPPED;
32import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_EFFECTS;
33import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_HIGH;
Julia Reynoldsf612869ae2015-11-05 16:48:55 -050034import static android.service.notification.NotificationListenerService.SUPPRESSED_EFFECT_LIGHTS;
35import static android.service.notification.NotificationListenerService.SUPPRESSED_EFFECT_PEEK;
Christoph Studerb82bc782014-08-20 14:29:43 +020036import static android.service.notification.NotificationListenerService.TRIM_FULL;
37import static android.service.notification.NotificationListenerService.TRIM_LIGHT;
Jeff Sharkey098d5802012-04-26 17:30:34 -070038import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
39import static org.xmlpull.v1.XmlPullParser.END_TAG;
40import static org.xmlpull.v1.XmlPullParser.START_TAG;
svetoslavganov75986cf2009-05-14 22:28:01 -070041
Dianne Hackborn41203752012-08-31 14:05:51 -070042import android.app.ActivityManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080043import android.app.ActivityManagerNative;
John Spurlock7340fc82014-04-24 18:50:12 -040044import android.app.AppGlobals;
Daniel Sandler4a900ac2013-01-30 14:04:10 -050045import android.app.AppOpsManager;
Julia Reynoldsa47a27f2015-08-24 08:31:47 -040046import android.app.AutomaticZenRule;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080047import android.app.IActivityManager;
48import android.app.INotificationManager;
49import android.app.ITransientNotification;
50import android.app.Notification;
John Spurlockb4782522014-08-22 14:54:46 -040051import android.app.NotificationManager;
John Spurlock1fc476d2015-04-14 16:05:20 -040052import android.app.NotificationManager.Policy;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080053import android.app.PendingIntent;
54import android.app.StatusBarManager;
John Spurlock35ef0a62015-05-28 11:24:10 -040055import android.app.backup.BackupManager;
Amith Yamasanif47e51e2015-04-17 10:02:15 -070056import android.app.usage.UsageEvents;
Amith Yamasanif47e51e2015-04-17 10:02:15 -070057import android.app.usage.UsageStatsManagerInternal;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080058import android.content.BroadcastReceiver;
Daniel Sandler5feceeb2013-03-22 18:29:23 -070059import android.content.ComponentName;
Dianne Hackborn1dac2772009-06-26 18:16:48 -070060import android.content.ContentResolver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080061import android.content.Context;
62import android.content.Intent;
63import android.content.IntentFilter;
John Spurlock7340fc82014-04-24 18:50:12 -040064import android.content.pm.ApplicationInfo;
Kenny Guy70058402014-10-28 20:45:06 +000065import android.content.pm.IPackageManager;
Daniel Sandler4a900ac2013-01-30 14:04:10 -050066import android.content.pm.PackageInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080067import android.content.pm.PackageManager;
68import android.content.pm.PackageManager.NameNotFoundException;
Christoph Studercee44ba2014-05-20 18:36:43 +020069import android.content.pm.ParceledListSlice;
Chris Wren66189fc2015-06-25 14:04:33 -040070import android.content.pm.UserInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080071import android.content.res.Resources;
Dianne Hackborn1dac2772009-06-26 18:16:48 -070072import android.database.ContentObserver;
John Spurlock7b414672014-07-18 13:02:39 -040073import android.media.AudioAttributes;
svetoslavganov75986cf2009-05-14 22:28:01 -070074import android.media.AudioManager;
John Spurlockcdb57ae2015-02-11 19:04:11 -050075import android.media.AudioManagerInternal;
Jean-Michel Triviceb79bc2014-09-05 11:09:14 -070076import android.media.AudioSystem;
Jeff Sharkey098d5802012-04-26 17:30:34 -070077import android.media.IRingtonePlayer;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080078import android.net.Uri;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080079import android.os.Binder;
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;
Scott Greenwald9a05b312013-06-28 00:37:54 -0400115import com.android.internal.R;
Chris Wrend1dbc922015-06-19 17:51:16 -0400116import com.android.internal.statusbar.NotificationVisibility;
John Spurlock056c5192014-04-20 21:52:01 -0400117import com.android.internal.util.FastXmlSerializer;
Julia Reynoldsa47a27f2015-08-24 08:31:47 -0400118import com.android.internal.util.Preconditions;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800119import com.android.server.EventLogTags;
Amith Yamasanif47e51e2015-04-17 10:02:15 -0700120import com.android.server.LocalServices;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800121import com.android.server.SystemService;
122import com.android.server.lights.Light;
123import com.android.server.lights.LightsManager;
John Spurlock7340fc82014-04-24 18:50:12 -0400124import com.android.server.notification.ManagedServices.ManagedServiceInfo;
125import com.android.server.notification.ManagedServices.UserProfiles;
John Spurlockb408e8e2014-04-23 21:12:45 -0400126import com.android.server.statusbar.StatusBarManagerInternal;
John Spurlockb408e8e2014-04-23 21:12:45 -0400127import libcore.io.IoUtils;
Chris Wrene4b38802015-07-07 15:54:19 -0400128import org.json.JSONArray;
129import org.json.JSONException;
130import org.json.JSONObject;
Jeff Sharkey098d5802012-04-26 17:30:34 -0700131import org.xmlpull.v1.XmlPullParser;
132import org.xmlpull.v1.XmlPullParserException;
John Spurlock056c5192014-04-20 21:52:01 -0400133import org.xmlpull.v1.XmlSerializer;
Jeff Sharkey098d5802012-04-26 17:30:34 -0700134
John Spurlock35ef0a62015-05-28 11:24:10 -0400135import java.io.ByteArrayInputStream;
136import java.io.ByteArrayOutputStream;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400137import java.io.File;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800138import java.io.FileDescriptor;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400139import java.io.FileInputStream;
140import java.io.FileNotFoundException;
John Spurlock056c5192014-04-20 21:52:01 -0400141import java.io.FileOutputStream;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400142import java.io.IOException;
John Spurlock35ef0a62015-05-28 11:24:10 -0400143import java.io.InputStream;
144import java.io.OutputStream;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800145import java.io.PrintWriter;
Wojciech Staszkiewicz9e9e2e72015-05-08 14:58:46 +0100146import java.nio.charset.StandardCharsets;
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500147import java.util.ArrayDeque;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800148import java.util.ArrayList;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400149import java.util.HashSet;
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500150import java.util.Iterator;
John Spurlock7c74f782015-06-04 13:01:42 -0400151import java.util.List;
Christoph Studer265c1052014-07-23 17:14:33 +0200152import java.util.Map.Entry;
John Spurlockb4782522014-08-22 14:54:46 -0400153import java.util.Objects;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400154
Daniel Sandlerd0a2f862010-08-03 15:29:31 -0400155/** {@hide} */
Adam Lesinski182f73f2013-12-05 16:48:06 -0800156public class NotificationManagerService extends SystemService {
157 static final String TAG = "NotificationService";
Christoph Studer1f32c652014-11-26 15:32:20 +0100158 static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
Selim Cinek40412492015-12-08 18:03:22 -0800159 public static final boolean ENABLE_CHILD_NOTIFICATIONS
160 = SystemProperties.getBoolean("debug.child_notifs", true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800161
Adam Lesinski182f73f2013-12-05 16:48:06 -0800162 static final int MAX_PACKAGE_NOTIFICATIONS = 50;
Joe Onoratobd73d012010-06-04 11:44:54 -0700163
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800164 // message codes
Adam Lesinski182f73f2013-12-05 16:48:06 -0800165 static final int MESSAGE_TIMEOUT = 2;
John Spurlock056c5192014-04-20 21:52:01 -0400166 static final int MESSAGE_SAVE_POLICY_FILE = 3;
Chris Wrenf9536642014-04-17 10:01:54 -0400167 static final int MESSAGE_RECONSIDER_RANKING = 4;
Chris Wren54bbef42014-07-09 18:37:56 -0400168 static final int MESSAGE_RANKING_CONFIG_CHANGE = 5;
169 static final int MESSAGE_SEND_RANKING_UPDATE = 6;
John Spurlockd8afe3c2014-08-01 14:04:07 -0400170 static final int MESSAGE_LISTENER_HINTS_CHANGED = 7;
Christoph Studer85a384b2014-08-27 20:16:15 +0200171 static final int MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED = 8;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800172
Adam Lesinski182f73f2013-12-05 16:48:06 -0800173 static final int LONG_DELAY = 3500; // 3.5 seconds
174 static final int SHORT_DELAY = 2000; // 2 seconds
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800175
Adam Lesinski182f73f2013-12-05 16:48:06 -0800176 static final long[] DEFAULT_VIBRATE_PATTERN = {0, 250, 250, 250};
Christoph Studer265c1052014-07-23 17:14:33 +0200177
Adam Lesinski182f73f2013-12-05 16:48:06 -0800178 static final int VIBRATE_PATTERN_MAXLEN = 8 * 2 + 1; // up to eight bumps
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800179
Adam Lesinski182f73f2013-12-05 16:48:06 -0800180 static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_NOTIFICATION;
Daniel Sandler526fa0e2012-12-04 14:51:50 -0500181
Adam Lesinski182f73f2013-12-05 16:48:06 -0800182 static final boolean ENABLE_BLOCKED_NOTIFICATIONS = true;
183 static final boolean ENABLE_BLOCKED_TOASTS = true;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400184
Christoph Studer12aeda82014-09-23 19:08:56 +0200185 // When #matchesCallFilter is called from the ringer, wait at most
186 // 3s to resolve the contacts. This timeout is required since
187 // ContactsProvider might take a long time to start up.
188 //
189 // Return STARRED_CONTACT when the timeout is hit in order to avoid
190 // missed calls in ZEN mode "Important".
191 static final int MATCHES_CALL_FILTER_CONTACTS_TIMEOUT_MS = 3000;
192 static final float MATCHES_CALL_FILTER_TIMEOUT_AFFINITY =
193 ValidateNotificationPeople.STARRED_CONTACT;
194
Christoph Studer265c1052014-07-23 17:14:33 +0200195 /** notification_enqueue status value for a newly enqueued notification. */
196 private static final int EVENTLOG_ENQUEUE_STATUS_NEW = 0;
197
198 /** notification_enqueue status value for an existing notification. */
199 private static final int EVENTLOG_ENQUEUE_STATUS_UPDATE = 1;
200
201 /** notification_enqueue status value for an ignored notification. */
202 private static final int EVENTLOG_ENQUEUE_STATUS_IGNORED = 2;
203
Adam Lesinski182f73f2013-12-05 16:48:06 -0800204 private IActivityManager mAm;
205 AudioManager mAudioManager;
John Spurlockcdb57ae2015-02-11 19:04:11 -0500206 AudioManagerInternal mAudioManagerInternal;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800207 StatusBarManagerInternal mStatusBar;
208 Vibrator mVibrator;
209
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800210 final IBinder mForegroundToken = new Binder();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800211 private WorkerHandler mHandler;
Chris Wrenf9536642014-04-17 10:01:54 -0400212 private final HandlerThread mRankingThread = new HandlerThread("ranker",
213 Process.THREAD_PRIORITY_BACKGROUND);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800214
Adam Lesinski182f73f2013-12-05 16:48:06 -0800215 private Light mNotificationLight;
216 Light mAttentionLight;
Mike Lockwood670f9322010-01-20 12:13:36 -0500217 private int mDefaultNotificationColor;
218 private int mDefaultNotificationLedOn;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800219
Mike Lockwood670f9322010-01-20 12:13:36 -0500220 private int mDefaultNotificationLedOff;
Daniel Sandleredbb3802012-11-13 20:49:47 -0800221 private long[] mDefaultVibrationPattern;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800222
Daniel Sandleredbb3802012-11-13 20:49:47 -0800223 private long[] mFallbackVibrationPattern;
Chris Wren5116a822014-06-04 15:59:50 -0400224 private boolean mUseAttentionLight;
Adam Lesinski182f73f2013-12-05 16:48:06 -0800225 boolean mSystemReady;
Daniel Sandleredbb3802012-11-13 20:49:47 -0800226
John Spurlockd8afe3c2014-08-01 14:04:07 -0400227 private boolean mDisableNotificationEffects;
John Spurlock32fe4c62014-10-02 12:16:02 -0400228 private int mCallState;
Chris Wren6054e612014-11-25 17:16:46 -0500229 private String mSoundNotificationKey;
230 private String mVibrateNotificationKey;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800231
John Spurlockd8afe3c2014-08-01 14:04:07 -0400232 private final ArraySet<ManagedServiceInfo> mListenersDisablingEffects = new ArraySet<>();
John Spurlockb4782522014-08-22 14:54:46 -0400233 private ComponentName mEffectsSuppressor;
John Spurlockd8afe3c2014-08-01 14:04:07 -0400234 private int mListenerHints; // right now, all hints are global
John Spurlock83104102015-02-12 23:25:12 -0500235 private int mInterruptionFilter = NotificationListenerService.INTERRUPTION_FILTER_UNKNOWN;
John Spurlock1fa865f2014-07-21 14:56:39 -0400236
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500237 // for enabling and disabling notification pulse behavior
Mike Lockwood63b5ad92011-08-30 09:55:30 -0400238 private boolean mScreenOn = true;
Daniel Sandlere96ffb12010-03-11 13:38:06 -0500239 private boolean mInCall = false;
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500240 private boolean mNotificationPulseEnabled;
241
Daniel Sandler09a247e2013-02-14 10:24:17 -0500242 // used as a mutex for access to all active notifications & listeners
Adam Lesinski182f73f2013-12-05 16:48:06 -0800243 final ArrayList<NotificationRecord> mNotificationList =
Fred Quintana6ecaff12009-09-25 14:23:13 -0700244 new ArrayList<NotificationRecord>();
John Spurlocka4294292014-03-24 18:02:32 -0400245 final ArrayMap<String, NotificationRecord> mNotificationsByKey =
246 new ArrayMap<String, NotificationRecord>();
Adam Lesinski182f73f2013-12-05 16:48:06 -0800247 final ArrayList<ToastRecord> mToastQueue = new ArrayList<ToastRecord>();
Christoph Studer265c1052014-07-23 17:14:33 +0200248 final ArrayMap<String, NotificationRecord> mSummaryByGroupKey = new ArrayMap<>();
John Spurlock7c74f782015-06-04 13:01:42 -0400249 final PolicyAccess mPolicyAccess = new PolicyAccess();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800250
Chris Wren6054e612014-11-25 17:16:46 -0500251 // The last key in this list owns the hardware.
252 ArrayList<String> mLights = new ArrayList<>();
svetoslavganov75986cf2009-05-14 22:28:01 -0700253
Adam Lesinski182f73f2013-12-05 16:48:06 -0800254 private AppOpsManager mAppOps;
Amith Yamasanif47e51e2015-04-17 10:02:15 -0700255 private UsageStatsManagerInternal mAppUsageStats;
Daniel Sandler4a900ac2013-01-30 14:04:10 -0500256
Griff Hazen9f637d12014-06-10 11:13:51 -0700257 private Archive mArchive;
258
John Spurlock21258a32015-05-27 18:22:55 -0400259 // Persistent storage for notification policy
Daniel Sandler0da673f2012-04-11 12:33:16 -0400260 private AtomicFile mPolicyFile;
John Spurlock21258a32015-05-27 18:22:55 -0400261
262 // Temporary holder for <blocked-packages> config coming from old policy files.
Daniel Sandler0da673f2012-04-11 12:33:16 -0400263 private HashSet<String> mBlockedPackages = new HashSet<String>();
264
265 private static final int DB_VERSION = 1;
266
John Spurlock21258a32015-05-27 18:22:55 -0400267 private static final String TAG_NOTIFICATION_POLICY = "notification-policy";
Daniel Sandler0da673f2012-04-11 12:33:16 -0400268 private static final String ATTR_VERSION = "version";
269
John Spurlock21258a32015-05-27 18:22:55 -0400270 // Obsolete: converted if present, but not resaved to disk.
Daniel Sandler0da673f2012-04-11 12:33:16 -0400271 private static final String TAG_BLOCKED_PKGS = "blocked-packages";
272 private static final String TAG_PACKAGE = "package";
273 private static final String ATTR_NAME = "name";
274
Chris Wren54bbef42014-07-09 18:37:56 -0400275 private RankingHelper mRankingHelper;
Scott Greenwald9a05b312013-06-28 00:37:54 -0400276
John Spurlockb408e8e2014-04-23 21:12:45 -0400277 private final UserProfiles mUserProfiles = new UserProfiles();
John Spurlock7340fc82014-04-24 18:50:12 -0400278 private NotificationListeners mListeners;
279 private ConditionProviders mConditionProviders;
Christoph Studer1c3f81f2014-04-16 15:05:56 +0200280 private NotificationUsageStats mUsageStats;
Christoph Studer546bec82014-03-14 12:17:12 +0100281
John Spurlocke6a7d932014-03-13 12:29:00 -0400282 private static final int MY_UID = Process.myUid();
283 private static final int MY_PID = Process.myPid();
John Spurlocke6a7d932014-03-13 12:29:00 -0400284
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500285 private static class Archive {
Griff Hazen9f637d12014-06-10 11:13:51 -0700286 final int mBufferSize;
287 final ArrayDeque<StatusBarNotification> mBuffer;
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500288
Griff Hazen9f637d12014-06-10 11:13:51 -0700289 public Archive(int size) {
290 mBufferSize = size;
291 mBuffer = new ArrayDeque<StatusBarNotification>(mBufferSize);
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500292 }
Jeff Sharkey0c1baf92013-04-03 13:08:52 -0700293
Daniel Sandler5e62e3a2013-04-15 20:57:02 -0400294 public String toString() {
295 final StringBuilder sb = new StringBuilder();
296 final int N = mBuffer.size();
297 sb.append("Archive (");
298 sb.append(N);
299 sb.append(" notification");
300 sb.append((N==1)?")":"s)");
301 return sb.toString();
302 }
303
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500304 public void record(StatusBarNotification nr) {
Griff Hazen9f637d12014-06-10 11:13:51 -0700305 if (mBuffer.size() == mBufferSize) {
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500306 mBuffer.removeFirst();
307 }
Daniel Sandler26b81d52013-05-20 20:56:43 -0400308
309 // We don't want to store the heavy bits of the notification in the archive,
310 // but other clients in the system process might be using the object, so we
311 // store a (lightened) copy.
312 mBuffer.addLast(nr.cloneLight());
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500313 }
314
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500315 public Iterator<StatusBarNotification> descendingIterator() {
316 return mBuffer.descendingIterator();
317 }
Daniel Sandler78d0d252013-02-12 08:14:52 -0500318
319 public StatusBarNotification[] getArray(int count) {
Griff Hazen9f637d12014-06-10 11:13:51 -0700320 if (count == 0) count = mBufferSize;
Daniel Sandler78d0d252013-02-12 08:14:52 -0500321 final StatusBarNotification[] a
322 = new StatusBarNotification[Math.min(count, mBuffer.size())];
323 Iterator<StatusBarNotification> iter = descendingIterator();
324 int i=0;
325 while (iter.hasNext() && i < count) {
326 a[i++] = iter.next();
327 }
328 return a;
329 }
330
Daniel Sandlerfde19b12013-01-17 00:21:05 -0500331 }
332
John Spurlock35ef0a62015-05-28 11:24:10 -0400333 private void readPolicyXml(InputStream stream, boolean forRestore)
334 throws XmlPullParserException, NumberFormatException, IOException {
335 final XmlPullParser parser = Xml.newPullParser();
336 parser.setInput(stream, StandardCharsets.UTF_8.name());
337
338 int type;
339 String tag;
340 int version = DB_VERSION;
341 while ((type = parser.next()) != END_DOCUMENT) {
342 tag = parser.getName();
343 if (type == START_TAG) {
344 if (TAG_NOTIFICATION_POLICY.equals(tag)) {
345 version = Integer.parseInt(
346 parser.getAttributeValue(null, ATTR_VERSION));
347 } else if (TAG_BLOCKED_PKGS.equals(tag)) {
348 while ((type = parser.next()) != END_DOCUMENT) {
349 tag = parser.getName();
350 if (TAG_PACKAGE.equals(tag)) {
351 mBlockedPackages.add(
352 parser.getAttributeValue(null, ATTR_NAME));
353 } else if (TAG_BLOCKED_PKGS.equals(tag) && type == END_TAG) {
354 break;
355 }
356 }
357 }
358 }
359 mZenModeHelper.readXml(parser, forRestore);
360 mRankingHelper.readXml(parser, forRestore);
361 }
362 }
363
John Spurlock056c5192014-04-20 21:52:01 -0400364 private void loadPolicyFile() {
John Spurlock21258a32015-05-27 18:22:55 -0400365 if (DBG) Slog.d(TAG, "loadPolicyFile");
John Spurlock056c5192014-04-20 21:52:01 -0400366 synchronized(mPolicyFile) {
367 mBlockedPackages.clear();
Daniel Sandler0da673f2012-04-11 12:33:16 -0400368
John Spurlock056c5192014-04-20 21:52:01 -0400369 FileInputStream infile = null;
370 try {
371 infile = mPolicyFile.openRead();
John Spurlock35ef0a62015-05-28 11:24:10 -0400372 readPolicyXml(infile, false /*forRestore*/);
John Spurlock056c5192014-04-20 21:52:01 -0400373 } catch (FileNotFoundException e) {
374 // No data yet
375 } catch (IOException e) {
376 Log.wtf(TAG, "Unable to read notification policy", e);
377 } catch (NumberFormatException e) {
378 Log.wtf(TAG, "Unable to parse notification policy", e);
379 } catch (XmlPullParserException e) {
380 Log.wtf(TAG, "Unable to parse notification policy", e);
381 } finally {
382 IoUtils.closeQuietly(infile);
383 }
384 }
385 }
386
387 public void savePolicyFile() {
388 mHandler.removeMessages(MESSAGE_SAVE_POLICY_FILE);
389 mHandler.sendEmptyMessage(MESSAGE_SAVE_POLICY_FILE);
390 }
391
392 private void handleSavePolicyFile() {
John Spurlock21258a32015-05-27 18:22:55 -0400393 if (DBG) Slog.d(TAG, "handleSavePolicyFile");
John Spurlock056c5192014-04-20 21:52:01 -0400394 synchronized (mPolicyFile) {
395 final FileOutputStream stream;
396 try {
397 stream = mPolicyFile.startWrite();
398 } catch (IOException e) {
399 Slog.w(TAG, "Failed to save policy file", e);
400 return;
401 }
402
403 try {
John Spurlock35ef0a62015-05-28 11:24:10 -0400404 writePolicyXml(stream, false /*forBackup*/);
John Spurlock056c5192014-04-20 21:52:01 -0400405 mPolicyFile.finishWrite(stream);
406 } catch (IOException e) {
407 Slog.w(TAG, "Failed to save policy file, restoring backup", e);
408 mPolicyFile.failWrite(stream);
Daniel Sandler0da673f2012-04-11 12:33:16 -0400409 }
410 }
John Spurlock35ef0a62015-05-28 11:24:10 -0400411 BackupManager.dataChanged(getContext().getPackageName());
412 }
413
414 private void writePolicyXml(OutputStream stream, boolean forBackup) throws IOException {
415 final XmlSerializer out = new FastXmlSerializer();
416 out.setOutput(stream, StandardCharsets.UTF_8.name());
417 out.startDocument(null, true);
418 out.startTag(null, TAG_NOTIFICATION_POLICY);
419 out.attribute(null, ATTR_VERSION, Integer.toString(DB_VERSION));
420 mZenModeHelper.writeXml(out, forBackup);
421 mRankingHelper.writeXml(out, forBackup);
422 out.endTag(null, TAG_NOTIFICATION_POLICY);
423 out.endDocument();
Daniel Sandler0da673f2012-04-11 12:33:16 -0400424 }
425
Daniel Sandler4a900ac2013-01-30 14:04:10 -0500426 /** Use this when you actually want to post a notification or toast.
427 *
428 * Unchecked. Not exposed via Binder, but can be called in the course of enqueue*().
429 */
430 private boolean noteNotificationOp(String pkg, int uid) {
431 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
432 != AppOpsManager.MODE_ALLOWED) {
433 Slog.v(TAG, "notifications are disabled by AppOps for " + pkg);
434 return false;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400435 }
Daniel Sandler4a900ac2013-01-30 14:04:10 -0500436 return true;
Daniel Sandler0da673f2012-04-11 12:33:16 -0400437 }
438
Chris Wren66189fc2015-06-25 14:04:33 -0400439 /** Use this to check if a package can post a notification or toast. */
440 private boolean checkNotificationOp(String pkg, int uid) {
441 return mAppOps.checkOp(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
442 == AppOpsManager.MODE_ALLOWED;
443 }
444
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800445 private static final class ToastRecord
446 {
447 final int pid;
448 final String pkg;
449 final ITransientNotification callback;
450 int duration;
451
452 ToastRecord(int pid, String pkg, ITransientNotification callback, int duration)
453 {
454 this.pid = pid;
455 this.pkg = pkg;
456 this.callback = callback;
457 this.duration = duration;
458 }
459
460 void update(int duration) {
461 this.duration = duration;
462 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800463
John Spurlock25e2d242014-06-27 13:58:23 -0400464 void dump(PrintWriter pw, String prefix, DumpFilter filter) {
465 if (filter != null && !filter.matches(pkg)) return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800466 pw.println(prefix + this);
467 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800468
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800469 @Override
470 public final String toString()
471 {
472 return "ToastRecord{"
473 + Integer.toHexString(System.identityHashCode(this))
474 + " pkg=" + pkg
475 + " callback=" + callback
476 + " duration=" + duration;
477 }
478 }
479
Adam Lesinski182f73f2013-12-05 16:48:06 -0800480 private final NotificationDelegate mNotificationDelegate = new NotificationDelegate() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800481
Adam Lesinski182f73f2013-12-05 16:48:06 -0800482 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800483 public void onSetDisabled(int status) {
484 synchronized (mNotificationList) {
John Spurlockd8afe3c2014-08-01 14:04:07 -0400485 mDisableNotificationEffects =
486 (status & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0;
John Spurlock32fe4c62014-10-02 12:16:02 -0400487 if (disableNotificationEffects(null) != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800488 // cancel whatever's going on
489 long identity = Binder.clearCallingIdentity();
490 try {
Adam Lesinski182f73f2013-12-05 16:48:06 -0800491 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
Jeff Sharkey098d5802012-04-26 17:30:34 -0700492 if (player != null) {
493 player.stopAsync();
494 }
495 } catch (RemoteException e) {
496 } finally {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800497 Binder.restoreCallingIdentity(identity);
498 }
499
500 identity = Binder.clearCallingIdentity();
501 try {
502 mVibrator.cancel();
Jeff Sharkey098d5802012-04-26 17:30:34 -0700503 } finally {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800504 Binder.restoreCallingIdentity(identity);
505 }
506 }
507 }
508 }
509
Adam Lesinski182f73f2013-12-05 16:48:06 -0800510 @Override
John Spurlocke6a7d932014-03-13 12:29:00 -0400511 public void onClearAll(int callingUid, int callingPid, int userId) {
Adam Lesinskie8240262014-03-26 16:01:00 -0700512 synchronized (mNotificationList) {
Kenny Guya263e4e2014-03-03 18:24:03 +0000513 cancelAllLocked(callingUid, callingPid, userId, REASON_DELEGATE_CANCEL_ALL, null,
514 /*includeCurrentProfiles*/ true);
Adam Lesinskie8240262014-03-26 16:01:00 -0700515 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800516 }
517
Adam Lesinski182f73f2013-12-05 16:48:06 -0800518 @Override
Christoph Studer03b87a22014-04-30 17:33:27 +0200519 public void onNotificationClick(int callingUid, int callingPid, String key) {
520 synchronized (mNotificationList) {
Christoph Studer03b87a22014-04-30 17:33:27 +0200521 NotificationRecord r = mNotificationsByKey.get(key);
522 if (r == null) {
523 Log.w(TAG, "No notification with key: " + key);
524 return;
525 }
Chris Wrene6ddb8a2015-05-27 15:21:00 -0400526 final long now = System.currentTimeMillis();
527 EventLogTags.writeNotificationClicked(key,
528 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
529
Christoph Studer03b87a22014-04-30 17:33:27 +0200530 StatusBarNotification sbn = r.sbn;
531 cancelNotification(callingUid, callingPid, sbn.getPackageName(), sbn.getTag(),
532 sbn.getId(), Notification.FLAG_AUTO_CANCEL,
533 Notification.FLAG_FOREGROUND_SERVICE, false, r.getUserId(),
534 REASON_DELEGATE_CLICK, null);
535 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800536 }
537
Adam Lesinski182f73f2013-12-05 16:48:06 -0800538 @Override
Christoph Studer4da84cd2014-10-21 17:24:20 +0200539 public void onNotificationActionClick(int callingUid, int callingPid, String key,
540 int actionIndex) {
541 synchronized (mNotificationList) {
Christoph Studer4da84cd2014-10-21 17:24:20 +0200542 NotificationRecord r = mNotificationsByKey.get(key);
543 if (r == null) {
544 Log.w(TAG, "No notification with key: " + key);
545 return;
546 }
Chris Wrene6ddb8a2015-05-27 15:21:00 -0400547 final long now = System.currentTimeMillis();
548 EventLogTags.writeNotificationActionClicked(key, actionIndex,
549 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
Christoph Studer4da84cd2014-10-21 17:24:20 +0200550 // TODO: Log action click via UsageStats.
551 }
552 }
553
554 @Override
John Spurlocke6a7d932014-03-13 12:29:00 -0400555 public void onNotificationClear(int callingUid, int callingPid,
556 String pkg, String tag, int id, int userId) {
557 cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
Kenny Guy3a7c4a52014-03-03 18:24:03 +0000558 Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
John Spurlocke6a7d932014-03-13 12:29:00 -0400559 true, userId, REASON_DELEGATE_CANCEL, null);
Daniel Sandler0f0b11c2010-08-04 15:54:58 -0400560 }
561
Adam Lesinski182f73f2013-12-05 16:48:06 -0800562 @Override
Chris Wrenb659c4f2015-06-25 17:12:27 -0400563 public void onPanelRevealed(boolean clearEffects, int items) {
564 EventLogTags.writeNotificationPanelRevealed(items);
Christoph Studer1f32c652014-11-26 15:32:20 +0100565 if (clearEffects) {
566 clearEffects();
567 }
568 }
569
570 @Override
571 public void onPanelHidden() {
572 EventLogTags.writeNotificationPanelHidden();
573 }
574
575 @Override
576 public void clearEffects() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800577 synchronized (mNotificationList) {
Christoph Studer1f32c652014-11-26 15:32:20 +0100578 if (DBG) Slog.d(TAG, "clearEffects");
579
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800580 // sound
Chris Wren6054e612014-11-25 17:16:46 -0500581 mSoundNotificationKey = null;
Jeff Sharkey098d5802012-04-26 17:30:34 -0700582
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800583 long identity = Binder.clearCallingIdentity();
584 try {
Adam Lesinski182f73f2013-12-05 16:48:06 -0800585 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
Jeff Sharkey098d5802012-04-26 17:30:34 -0700586 if (player != null) {
587 player.stopAsync();
588 }
589 } catch (RemoteException e) {
590 } finally {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800591 Binder.restoreCallingIdentity(identity);
592 }
593
594 // vibrate
Chris Wren6054e612014-11-25 17:16:46 -0500595 mVibrateNotificationKey = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800596 identity = Binder.clearCallingIdentity();
597 try {
598 mVibrator.cancel();
Jeff Sharkey098d5802012-04-26 17:30:34 -0700599 } finally {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800600 Binder.restoreCallingIdentity(identity);
601 }
602
603 // light
604 mLights.clear();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800605 updateLightsLocked();
606 }
607 }
Joe Onorato005847b2010-06-04 16:08:02 -0400608
Adam Lesinski182f73f2013-12-05 16:48:06 -0800609 @Override
John Spurlocke6a7d932014-03-13 12:29:00 -0400610 public void onNotificationError(int callingUid, int callingPid, String pkg, String tag, int id,
Kenny Guy3a7c4a52014-03-03 18:24:03 +0000611 int uid, int initialPid, String message, int userId) {
Daniel Sandlerd0a2f862010-08-03 15:29:31 -0400612 Slog.d(TAG, "onNotification error pkg=" + pkg + " tag=" + tag + " id=" + id
613 + "; will crashApplication(uid=" + uid + ", pid=" + initialPid + ")");
John Spurlocke6a7d932014-03-13 12:29:00 -0400614 cancelNotification(callingUid, callingPid, pkg, tag, id, 0, 0, false, userId,
615 REASON_DELEGATE_ERROR, null);
Dianne Hackborn9d39d0c2010-06-24 15:57:42 -0700616 long ident = Binder.clearCallingIdentity();
617 try {
618 ActivityManagerNative.getDefault().crashApplication(uid, initialPid, pkg,
619 "Bad notification posted from package " + pkg
620 + ": " + message);
621 } catch (RemoteException e) {
622 }
623 Binder.restoreCallingIdentity(ident);
Joe Onorato005847b2010-06-04 16:08:02 -0400624 }
John Spurlocke677d712014-02-13 12:52:19 -0500625
626 @Override
Chris Wrend1dbc922015-06-19 17:51:16 -0400627 public void onNotificationVisibilityChanged(NotificationVisibility[] newlyVisibleKeys,
628 NotificationVisibility[] noLongerVisibleKeys) {
Christoph Studerffeb0c32014-05-07 22:23:56 +0200629 synchronized (mNotificationList) {
Chris Wrend1dbc922015-06-19 17:51:16 -0400630 for (NotificationVisibility nv : newlyVisibleKeys) {
631 NotificationRecord r = mNotificationsByKey.get(nv.key);
Christoph Studerffeb0c32014-05-07 22:23:56 +0200632 if (r == null) continue;
Chris Wrend1dbc922015-06-19 17:51:16 -0400633 r.setVisibility(true, nv.rank);
634 nv.recycle();
Christoph Studerffeb0c32014-05-07 22:23:56 +0200635 }
636 // Note that we might receive this event after notifications
637 // have already left the system, e.g. after dismissing from the
638 // shade. Hence not finding notifications in
639 // mNotificationsByKey is not an exceptional condition.
Chris Wrend1dbc922015-06-19 17:51:16 -0400640 for (NotificationVisibility nv : noLongerVisibleKeys) {
641 NotificationRecord r = mNotificationsByKey.get(nv.key);
Christoph Studerffeb0c32014-05-07 22:23:56 +0200642 if (r == null) continue;
Chris Wrend1dbc922015-06-19 17:51:16 -0400643 r.setVisibility(false, nv.rank);
644 nv.recycle();
Christoph Studerffeb0c32014-05-07 22:23:56 +0200645 }
646 }
Christoph Studer92b389d2014-04-01 18:44:40 +0200647 }
Chris Wren78403d72014-07-28 10:23:24 +0100648
649 @Override
650 public void onNotificationExpansionChanged(String key,
651 boolean userAction, boolean expanded) {
Chris Wren78403d72014-07-28 10:23:24 +0100652 synchronized (mNotificationList) {
653 NotificationRecord r = mNotificationsByKey.get(key);
654 if (r != null) {
655 r.stats.onExpansionChanged(userAction, expanded);
Chris Wrene6ddb8a2015-05-27 15:21:00 -0400656 final long now = System.currentTimeMillis();
657 EventLogTags.writeNotificationExpansion(key,
658 userAction ? 1 : 0, expanded ? 1 : 0,
659 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
Chris Wren78403d72014-07-28 10:23:24 +0100660 }
661 }
662 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800663 };
664
Kenny Guy70058402014-10-28 20:45:06 +0000665 private final BroadcastReceiver mPackageIntentReceiver = new BroadcastReceiver() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800666 @Override
667 public void onReceive(Context context, Intent intent) {
668 String action = intent.getAction();
Dianne Hackborn29cd7f12015-01-08 10:37:05 -0800669 if (action == null) {
670 return;
671 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800672
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800673 boolean queryRestart = false;
Chris Wrenae9bb572013-05-15 14:50:28 -0400674 boolean queryRemove = false;
Daniel Sandler26ece572012-06-01 15:38:46 -0400675 boolean packageChanged = false;
John Spurlock79f78922013-05-16 09:10:05 -0400676 boolean cancelNotifications = true;
Chris Wrenf9536642014-04-17 10:01:54 -0400677
Chris Wren3da73022013-05-10 14:41:21 -0400678 if (action.equals(Intent.ACTION_PACKAGE_ADDED)
Chris Wrenae9bb572013-05-15 14:50:28 -0400679 || (queryRemove=action.equals(Intent.ACTION_PACKAGE_REMOVED))
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800680 || action.equals(Intent.ACTION_PACKAGE_RESTARTED)
Daniel Sandler26ece572012-06-01 15:38:46 -0400681 || (packageChanged=action.equals(Intent.ACTION_PACKAGE_CHANGED))
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800682 || (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART))
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800683 || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
Kenny Guy70058402014-10-28 20:45:06 +0000684 int changeUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
685 UserHandle.USER_ALL);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800686 String pkgList[] = null;
Chris Wrenae9bb572013-05-15 14:50:28 -0400687 boolean queryReplace = queryRemove &&
688 intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
John Spurlocke77bb362014-04-26 10:24:59 -0400689 if (DBG) Slog.i(TAG, "action=" + action + " queryReplace=" + queryReplace);
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800690 if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800691 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800692 } else if (queryRestart) {
693 pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800694 } else {
695 Uri uri = intent.getData();
696 if (uri == null) {
697 return;
698 }
699 String pkgName = uri.getSchemeSpecificPart();
700 if (pkgName == null) {
701 return;
702 }
Daniel Sandler26ece572012-06-01 15:38:46 -0400703 if (packageChanged) {
704 // We cancel notifications for packages which have just been disabled
Christopher Tate06e5fed2013-10-09 14:39:15 -0700705 try {
Kenny Guy70058402014-10-28 20:45:06 +0000706 final IPackageManager pm = AppGlobals.getPackageManager();
707 final int enabled = pm.getApplicationEnabledSetting(pkgName,
708 changeUserId != UserHandle.USER_ALL ? changeUserId :
Xiaohui Chenddbe4ca2015-08-13 16:20:56 -0700709 UserHandle.USER_SYSTEM);
Christopher Tate06e5fed2013-10-09 14:39:15 -0700710 if (enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED
711 || enabled == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
712 cancelNotifications = false;
713 }
714 } catch (IllegalArgumentException e) {
715 // Package doesn't exist; probably racing with uninstall.
716 // cancelNotifications is already true, so nothing to do here.
717 if (DBG) {
718 Slog.i(TAG, "Exception trying to look up app enabled setting", e);
719 }
Kenny Guy70058402014-10-28 20:45:06 +0000720 } catch (RemoteException e) {
721 // Failed to talk to PackageManagerService Should never happen!
Daniel Sandler26ece572012-06-01 15:38:46 -0400722 }
723 }
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800724 pkgList = new String[]{pkgName};
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800725 }
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700726
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800727 if (pkgList != null && (pkgList.length > 0)) {
728 for (String pkgName : pkgList) {
John Spurlock79f78922013-05-16 09:10:05 -0400729 if (cancelNotifications) {
John Spurlocke6a7d932014-03-13 12:29:00 -0400730 cancelAllNotificationsInt(MY_UID, MY_PID, pkgName, 0, 0, !queryRestart,
Kenny Guy70058402014-10-28 20:45:06 +0000731 changeUserId, REASON_PACKAGE_CHANGED, null);
John Spurlock79f78922013-05-16 09:10:05 -0400732 }
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800733 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800734 }
John Spurlockb408e8e2014-04-23 21:12:45 -0400735 mListeners.onPackagesChanged(queryReplace, pkgList);
John Spurlock7340fc82014-04-24 18:50:12 -0400736 mConditionProviders.onPackagesChanged(queryReplace, pkgList);
John Spurlock35ef0a62015-05-28 11:24:10 -0400737 mRankingHelper.onPackagesChanged(queryReplace, pkgList);
Kenny Guy70058402014-10-28 20:45:06 +0000738 }
739 }
740 };
741
742 private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
743 @Override
744 public void onReceive(Context context, Intent intent) {
745 String action = intent.getAction();
746
747 if (action.equals(Intent.ACTION_SCREEN_ON)) {
Mike Lockwood63b5ad92011-08-30 09:55:30 -0400748 // Keep track of screen on/off state, but do not turn off the notification light
749 // until user passes through the lock screen or views the notification.
750 mScreenOn = true;
Christoph Studer1f32c652014-11-26 15:32:20 +0100751 updateNotificationPulse();
Mike Lockwood63b5ad92011-08-30 09:55:30 -0400752 } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
753 mScreenOn = false;
Christoph Studer1f32c652014-11-26 15:32:20 +0100754 updateNotificationPulse();
Daniel Sandlere96ffb12010-03-11 13:38:06 -0500755 } else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) {
John Spurlock5d2eeb12014-01-16 10:46:36 -0500756 mInCall = TelephonyManager.EXTRA_STATE_OFFHOOK
757 .equals(intent.getStringExtra(TelephonyManager.EXTRA_STATE));
Daniel Sandlere96ffb12010-03-11 13:38:06 -0500758 updateNotificationPulse();
Dianne Hackborn80a4af22012-08-27 19:18:31 -0700759 } else if (action.equals(Intent.ACTION_USER_STOPPED)) {
760 int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
761 if (userHandle >= 0) {
John Spurlocke6a7d932014-03-13 12:29:00 -0400762 cancelAllNotificationsInt(MY_UID, MY_PID, null, 0, 0, true, userHandle,
763 REASON_USER_STOPPED, null);
Dianne Hackborn80a4af22012-08-27 19:18:31 -0700764 }
Mike Lockwood63b5ad92011-08-30 09:55:30 -0400765 } else if (action.equals(Intent.ACTION_USER_PRESENT)) {
766 // turn off LED when user passes through lock screen
767 mNotificationLight.turnOff();
John Spurlockcb566aa2014-08-03 22:58:28 -0400768 mStatusBar.notificationLightOff();
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400769 } else if (action.equals(Intent.ACTION_USER_SWITCHED)) {
John Spurlock1b8b22b2015-05-20 09:47:13 -0400770 final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400771 // reload per-user settings
772 mSettingsObserver.update(null);
John Spurlockb408e8e2014-04-23 21:12:45 -0400773 mUserProfiles.updateCache(context);
Christoph Studerb53dfd42014-09-12 14:45:59 +0200774 // Refresh managed services
John Spurlock1b8b22b2015-05-20 09:47:13 -0400775 mConditionProviders.onUserSwitched(user);
776 mListeners.onUserSwitched(user);
John Spurlock21258a32015-05-27 18:22:55 -0400777 mZenModeHelper.onUserSwitched(user);
Kenny Guy3a7c4a52014-03-03 18:24:03 +0000778 } else if (action.equals(Intent.ACTION_USER_ADDED)) {
John Spurlockb408e8e2014-04-23 21:12:45 -0400779 mUserProfiles.updateCache(context);
John Spurlock21258a32015-05-27 18:22:55 -0400780 } else if (action.equals(Intent.ACTION_USER_REMOVED)) {
781 final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
782 mZenModeHelper.onUserRemoved(user);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800783 }
784 }
785 };
786
John Spurlock7c74f782015-06-04 13:01:42 -0400787 private final class SettingsObserver extends ContentObserver {
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400788 private final Uri NOTIFICATION_LIGHT_PULSE_URI
789 = Settings.System.getUriFor(Settings.System.NOTIFICATION_LIGHT_PULSE);
790
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700791 SettingsObserver(Handler handler) {
792 super(handler);
793 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800794
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700795 void observe() {
Adam Lesinski182f73f2013-12-05 16:48:06 -0800796 ContentResolver resolver = getContext().getContentResolver();
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400797 resolver.registerContentObserver(NOTIFICATION_LIGHT_PULSE_URI,
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700798 false, this, UserHandle.USER_ALL);
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400799 update(null);
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700800 }
801
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400802 @Override public void onChange(boolean selfChange, Uri uri) {
803 update(uri);
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700804 }
805
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400806 public void update(Uri uri) {
Adam Lesinski182f73f2013-12-05 16:48:06 -0800807 ContentResolver resolver = getContext().getContentResolver();
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400808 if (uri == null || NOTIFICATION_LIGHT_PULSE_URI.equals(uri)) {
809 boolean pulseEnabled = Settings.System.getInt(resolver,
810 Settings.System.NOTIFICATION_LIGHT_PULSE, 0) != 0;
811 if (mNotificationPulseEnabled != pulseEnabled) {
812 mNotificationPulseEnabled = pulseEnabled;
813 updateNotificationPulse();
814 }
815 }
Dianne Hackborn1dac2772009-06-26 18:16:48 -0700816 }
817 }
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500818
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400819 private SettingsObserver mSettingsObserver;
John Spurlock056c5192014-04-20 21:52:01 -0400820 private ZenModeHelper mZenModeHelper;
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400821
John Spurlockcad57682014-07-26 17:09:56 -0400822 private final Runnable mBuzzBeepBlinked = new Runnable() {
823 @Override
824 public void run() {
825 mStatusBar.buzzBeepBlinked();
826 }
827 };
828
Daniel Sandleredbb3802012-11-13 20:49:47 -0800829 static long[] getLongArray(Resources r, int resid, int maxlen, long[] def) {
830 int[] ar = r.getIntArray(resid);
831 if (ar == null) {
832 return def;
833 }
834 final int len = ar.length > maxlen ? maxlen : ar.length;
835 long[] out = new long[len];
836 for (int i=0; i<len; i++) {
837 out[i] = ar[i];
838 }
839 return out;
840 }
841
Jeff Brownb880d882014-02-10 19:47:07 -0800842 public NotificationManagerService(Context context) {
843 super(context);
844 }
845
Adam Lesinski182f73f2013-12-05 16:48:06 -0800846 @Override
847 public void onStart() {
Chris Wren54bbef42014-07-09 18:37:56 -0400848 Resources resources = getContext().getResources();
849
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800850 mAm = ActivityManagerNative.getDefault();
Adam Lesinski182f73f2013-12-05 16:48:06 -0800851 mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
852 mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
Amith Yamasanif47e51e2015-04-17 10:02:15 -0700853 mAppUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
San Mehat3ee13172010-02-04 20:54:43 -0800854
Adam Lesinski182f73f2013-12-05 16:48:06 -0800855 mHandler = new WorkerHandler();
Chris Wrenf9536642014-04-17 10:01:54 -0400856 mRankingThread.start();
Chris Wren54bbef42014-07-09 18:37:56 -0400857 String[] extractorNames;
858 try {
859 extractorNames = resources.getStringArray(R.array.config_notificationSignalExtractors);
860 } catch (Resources.NotFoundException e) {
861 extractorNames = new String[0];
862 }
Chris Wren5eab2b72015-06-16 13:56:22 -0400863 mUsageStats = new NotificationUsageStats(getContext());
Chris Wren54bbef42014-07-09 18:37:56 -0400864 mRankingHelper = new RankingHelper(getContext(),
865 new RankingWorkerHandler(mRankingThread.getLooper()),
Chris Wren5eab2b72015-06-16 13:56:22 -0400866 mUsageStats,
Chris Wren54bbef42014-07-09 18:37:56 -0400867 extractorNames);
John Spurlockb2278d62015-04-07 12:47:12 -0400868 mConditionProviders = new ConditionProviders(getContext(), mHandler, mUserProfiles);
869 mZenModeHelper = new ZenModeHelper(getContext(), mHandler.getLooper(), mConditionProviders);
John Spurlock1c923a32014-04-27 16:42:29 -0400870 mZenModeHelper.addCallback(new ZenModeHelper.Callback() {
John Spurlock056c5192014-04-20 21:52:01 -0400871 @Override
872 public void onConfigChanged() {
873 savePolicyFile();
874 }
John Spurlockd8afe3c2014-08-01 14:04:07 -0400875
876 @Override
877 void onZenModeChanged() {
John Spurlock80774932015-05-07 17:38:50 -0400878 sendRegisteredOnlyBroadcast(NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED);
Jason Monka9927322015-12-13 16:22:37 -0500879 getContext().sendBroadcastAsUser(
Jason Monk63506742015-12-16 12:06:51 -0500880 new Intent(NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED_INTERNAL)
881 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT),
Jason Monka9927322015-12-13 16:22:37 -0500882 UserHandle.ALL, android.Manifest.permission.MANAGE_NOTIFICATIONS);
John Spurlockd8afe3c2014-08-01 14:04:07 -0400883 synchronized(mNotificationList) {
Christoph Studer85a384b2014-08-27 20:16:15 +0200884 updateInterruptionFilterLocked();
John Spurlockd8afe3c2014-08-01 14:04:07 -0400885 }
886 }
John Spurlock1fc476d2015-04-14 16:05:20 -0400887
888 @Override
889 void onPolicyChanged() {
John Spurlock80774932015-05-07 17:38:50 -0400890 sendRegisteredOnlyBroadcast(NotificationManager.ACTION_NOTIFICATION_POLICY_CHANGED);
891 }
John Spurlock056c5192014-04-20 21:52:01 -0400892 });
893 final File systemDir = new File(Environment.getDataDirectory(), "system");
894 mPolicyFile = new AtomicFile(new File(systemDir, "notification_policy.xml"));
Daniel Sandler4a900ac2013-01-30 14:04:10 -0500895
896 importOldBlockDb();
Daniel Sandler0da673f2012-04-11 12:33:16 -0400897
John Spurlock7340fc82014-04-24 18:50:12 -0400898 mListeners = new NotificationListeners();
Adam Lesinski182f73f2013-12-05 16:48:06 -0800899 mStatusBar = getLocalService(StatusBarManagerInternal.class);
900 mStatusBar.setNotificationDelegate(mNotificationDelegate);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800901
Adam Lesinski182f73f2013-12-05 16:48:06 -0800902 final LightsManager lights = getLocalService(LightsManager.class);
903 mNotificationLight = lights.getLight(LightsManager.LIGHT_ID_NOTIFICATIONS);
904 mAttentionLight = lights.getLight(LightsManager.LIGHT_ID_ATTENTION);
Mike Lockwood3cb67a32009-11-27 14:25:58 -0500905
Mike Lockwood670f9322010-01-20 12:13:36 -0500906 mDefaultNotificationColor = resources.getColor(
Scott Greenwald9a05b312013-06-28 00:37:54 -0400907 R.color.config_defaultNotificationColor);
Mike Lockwood670f9322010-01-20 12:13:36 -0500908 mDefaultNotificationLedOn = resources.getInteger(
Scott Greenwald9a05b312013-06-28 00:37:54 -0400909 R.integer.config_defaultNotificationLedOn);
Mike Lockwood670f9322010-01-20 12:13:36 -0500910 mDefaultNotificationLedOff = resources.getInteger(
Scott Greenwald9a05b312013-06-28 00:37:54 -0400911 R.integer.config_defaultNotificationLedOff);
Mike Lockwood670f9322010-01-20 12:13:36 -0500912
Daniel Sandleredbb3802012-11-13 20:49:47 -0800913 mDefaultVibrationPattern = getLongArray(resources,
Scott Greenwald9a05b312013-06-28 00:37:54 -0400914 R.array.config_defaultNotificationVibePattern,
Daniel Sandleredbb3802012-11-13 20:49:47 -0800915 VIBRATE_PATTERN_MAXLEN,
916 DEFAULT_VIBRATE_PATTERN);
917
918 mFallbackVibrationPattern = getLongArray(resources,
Scott Greenwald9a05b312013-06-28 00:37:54 -0400919 R.array.config_notificationFallbackVibePattern,
Daniel Sandleredbb3802012-11-13 20:49:47 -0800920 VIBRATE_PATTERN_MAXLEN,
921 DEFAULT_VIBRATE_PATTERN);
922
Chris Wren5116a822014-06-04 15:59:50 -0400923 mUseAttentionLight = resources.getBoolean(R.bool.config_useAttentionLight);
924
Joe Onorato39f5b6a2009-07-23 12:29:19 -0400925 // Don't start allowing notifications until the setup wizard has run once.
926 // After that, including subsequent boots, init with notifications turned on.
927 // This works on the first boot because the setup wizard will toggle this
928 // flag at least once and we'll go back to 0 after that.
Adam Lesinski182f73f2013-12-05 16:48:06 -0800929 if (0 == Settings.Global.getInt(getContext().getContentResolver(),
Jeff Brownbf6f6f92012-09-25 15:03:20 -0700930 Settings.Global.DEVICE_PROVISIONED, 0)) {
John Spurlockd8afe3c2014-08-01 14:04:07 -0400931 mDisableNotificationEffects = true;
Joe Onorato39f5b6a2009-07-23 12:29:19 -0400932 }
John Spurlockb2278d62015-04-07 12:47:12 -0400933 mZenModeHelper.initZenMode();
John Spurlockf3701772015-02-12 13:29:37 -0500934 mInterruptionFilter = mZenModeHelper.getZenModeListenerInterruptionFilter();
Joe Onorato39f5b6a2009-07-23 12:29:19 -0400935
John Spurlockb408e8e2014-04-23 21:12:45 -0400936 mUserProfiles.updateCache(getContext());
John Spurlock32fe4c62014-10-02 12:16:02 -0400937 listenForCallState();
Kenny Guya263e4e2014-03-03 18:24:03 +0000938
Mike Lockwood35e16bf2010-11-30 19:53:36 -0500939 // register for various Intents
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800940 IntentFilter filter = new IntentFilter();
Mike Lockwoodc22404a2009-12-02 11:15:02 -0500941 filter.addAction(Intent.ACTION_SCREEN_ON);
942 filter.addAction(Intent.ACTION_SCREEN_OFF);
Daniel Sandlere96ffb12010-03-11 13:38:06 -0500943 filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
Mike Lockwood63b5ad92011-08-30 09:55:30 -0400944 filter.addAction(Intent.ACTION_USER_PRESENT);
Dianne Hackborn80a4af22012-08-27 19:18:31 -0700945 filter.addAction(Intent.ACTION_USER_STOPPED);
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400946 filter.addAction(Intent.ACTION_USER_SWITCHED);
Kenny Guy3a7c4a52014-03-03 18:24:03 +0000947 filter.addAction(Intent.ACTION_USER_ADDED);
John Spurlock21258a32015-05-27 18:22:55 -0400948 filter.addAction(Intent.ACTION_USER_REMOVED);
Adam Lesinski182f73f2013-12-05 16:48:06 -0800949 getContext().registerReceiver(mIntentReceiver, filter);
Kenny Guy70058402014-10-28 20:45:06 +0000950
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800951 IntentFilter pkgFilter = new IntentFilter();
Chris Wren3da73022013-05-10 14:41:21 -0400952 pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800953 pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
Daniel Sandleraac0eb02011-08-06 22:51:56 -0400954 pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800955 pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
956 pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
957 pkgFilter.addDataScheme("package");
Kenny Guy70058402014-10-28 20:45:06 +0000958 getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, pkgFilter, null,
959 null);
960
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800961 IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
Kenny Guy70058402014-10-28 20:45:06 +0000962 getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, sdFilter, null,
963 null);
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800964
Daniel Sandler4b749ef2013-03-18 21:53:04 -0400965 mSettingsObserver = new SettingsObserver(mHandler);
Scott Greenwald9a05b312013-06-28 00:37:54 -0400966
Griff Hazen9f637d12014-06-10 11:13:51 -0700967 mArchive = new Archive(resources.getInteger(
968 R.integer.config_notificationServiceArchiveSize));
969
Adam Lesinski182f73f2013-12-05 16:48:06 -0800970 publishBinderService(Context.NOTIFICATION_SERVICE, mService);
971 publishLocalService(NotificationManagerInternal.class, mInternalService);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800972 }
973
John Spurlocke7a835b2015-05-13 10:47:05 -0400974 private void sendRegisteredOnlyBroadcast(String action) {
975 getContext().sendBroadcastAsUser(new Intent(action)
976 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), UserHandle.ALL, null);
977 }
978
Daniel Sandler4a900ac2013-01-30 14:04:10 -0500979 /**
980 * Read the old XML-based app block database and import those blockages into the AppOps system.
981 */
982 private void importOldBlockDb() {
John Spurlock056c5192014-04-20 21:52:01 -0400983 loadPolicyFile();
Daniel Sandler4a900ac2013-01-30 14:04:10 -0500984
Adam Lesinski182f73f2013-12-05 16:48:06 -0800985 PackageManager pm = getContext().getPackageManager();
Daniel Sandler4a900ac2013-01-30 14:04:10 -0500986 for (String pkg : mBlockedPackages) {
987 PackageInfo info = null;
988 try {
989 info = pm.getPackageInfo(pkg, 0);
Adam Lesinski182f73f2013-12-05 16:48:06 -0800990 setNotificationsEnabledForPackageImpl(pkg, info.applicationInfo.uid, false);
Daniel Sandler4a900ac2013-01-30 14:04:10 -0500991 } catch (NameNotFoundException e) {
992 // forget you
993 }
994 }
995 mBlockedPackages.clear();
Daniel Sandler4a900ac2013-01-30 14:04:10 -0500996 }
997
Adam Lesinski182f73f2013-12-05 16:48:06 -0800998 @Override
999 public void onBootPhase(int phase) {
1000 if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
1001 // no beeping until we're basically done booting
1002 mSystemReady = true;
Jeff Sharkey098d5802012-04-26 17:30:34 -07001003
Adam Lesinski182f73f2013-12-05 16:48:06 -08001004 // Grab our optional AudioService
1005 mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
John Spurlockcdb57ae2015-02-11 19:04:11 -05001006 mAudioManagerInternal = getLocalService(AudioManagerInternal.class);
John Spurlock661f2cf2014-11-17 10:29:10 -05001007 mZenModeHelper.onSystemReady();
Adam Lesinskia6db4ab2014-03-24 12:31:45 -07001008 } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
1009 // This observer will force an update when observe is called, causing us to
1010 // bind to listener services.
1011 mSettingsObserver.observe();
John Spurlockb408e8e2014-04-23 21:12:45 -04001012 mListeners.onBootPhaseAppsCanStart();
John Spurlock7340fc82014-04-24 18:50:12 -04001013 mConditionProviders.onBootPhaseAppsCanStart();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001014 }
1015 }
1016
Adam Lesinski182f73f2013-12-05 16:48:06 -08001017 void setNotificationsEnabledForPackageImpl(String pkg, int uid, boolean enabled) {
1018 Slog.v(TAG, (enabled?"en":"dis") + "abling notifications for " + pkg);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001019
Adam Lesinski182f73f2013-12-05 16:48:06 -08001020 mAppOps.setMode(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg,
1021 enabled ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001022
Adam Lesinski182f73f2013-12-05 16:48:06 -08001023 // Now, cancel any outstanding notifications that are part of a just-disabled app
1024 if (ENABLE_BLOCKED_NOTIFICATIONS && !enabled) {
John Spurlocke6a7d932014-03-13 12:29:00 -04001025 cancelAllNotificationsInt(MY_UID, MY_PID, pkg, 0, 0, true, UserHandle.getUserId(uid),
1026 REASON_PACKAGE_BANNED, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001027 }
1028 }
1029
John Spurlockd8afe3c2014-08-01 14:04:07 -04001030 private void updateListenerHintsLocked() {
Christoph Studer85a384b2014-08-27 20:16:15 +02001031 final int hints = mListenersDisablingEffects.isEmpty() ? 0 : HINT_HOST_DISABLE_EFFECTS;
John Spurlockd8afe3c2014-08-01 14:04:07 -04001032 if (hints == mListenerHints) return;
John Spurlocka7082992015-03-09 12:19:23 -04001033 ZenLog.traceListenerHintsChanged(mListenerHints, hints, mListenersDisablingEffects.size());
John Spurlockd8afe3c2014-08-01 14:04:07 -04001034 mListenerHints = hints;
1035 scheduleListenerHintsChanged(hints);
John Spurlock1fa865f2014-07-21 14:56:39 -04001036 }
1037
John Spurlockb4782522014-08-22 14:54:46 -04001038 private void updateEffectsSuppressorLocked() {
1039 final ComponentName suppressor = !mListenersDisablingEffects.isEmpty()
1040 ? mListenersDisablingEffects.valueAt(0).component : null;
1041 if (Objects.equals(suppressor, mEffectsSuppressor)) return;
John Spurlocka7082992015-03-09 12:19:23 -04001042 ZenLog.traceEffectsSuppressorChanged(mEffectsSuppressor, suppressor);
John Spurlockb4782522014-08-22 14:54:46 -04001043 mEffectsSuppressor = suppressor;
John Spurlock8403b752014-12-10 12:47:01 -05001044 mZenModeHelper.setEffectsSuppressed(suppressor != null);
John Spurlocke7a835b2015-05-13 10:47:05 -04001045 sendRegisteredOnlyBroadcast(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED);
John Spurlockb4782522014-08-22 14:54:46 -04001046 }
1047
Christoph Studer85a384b2014-08-27 20:16:15 +02001048 private void updateInterruptionFilterLocked() {
1049 int interruptionFilter = mZenModeHelper.getZenModeListenerInterruptionFilter();
1050 if (interruptionFilter == mInterruptionFilter) return;
1051 mInterruptionFilter = interruptionFilter;
1052 scheduleInterruptionFilterChanged(interruptionFilter);
1053 }
1054
Adam Lesinski182f73f2013-12-05 16:48:06 -08001055 private final IBinder mService = new INotificationManager.Stub() {
1056 // Toasts
1057 // ============================================================================
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001058
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001059 @Override
Adam Lesinski182f73f2013-12-05 16:48:06 -08001060 public void enqueueToast(String pkg, ITransientNotification callback, int duration)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001061 {
Adam Lesinski182f73f2013-12-05 16:48:06 -08001062 if (DBG) {
1063 Slog.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback
1064 + " duration=" + duration);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001065 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001066
1067 if (pkg == null || callback == null) {
1068 Slog.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback);
1069 return ;
1070 }
1071
John Spurlock7340fc82014-04-24 18:50:12 -04001072 final boolean isSystemToast = isCallerSystem() || ("android".equals(pkg));
Adam Lesinski182f73f2013-12-05 16:48:06 -08001073
1074 if (ENABLE_BLOCKED_TOASTS && !noteNotificationOp(pkg, Binder.getCallingUid())) {
1075 if (!isSystemToast) {
1076 Slog.e(TAG, "Suppressing toast from package " + pkg + " by user request.");
1077 return;
1078 }
1079 }
1080
1081 synchronized (mToastQueue) {
1082 int callingPid = Binder.getCallingPid();
1083 long callingId = Binder.clearCallingIdentity();
1084 try {
1085 ToastRecord record;
1086 int index = indexOfToastLocked(pkg, callback);
1087 // If it's already in the queue, we update it in place, we don't
1088 // move it to the end of the queue.
1089 if (index >= 0) {
1090 record = mToastQueue.get(index);
1091 record.update(duration);
1092 } else {
1093 // Limit the number of toasts that any given package except the android
1094 // package can enqueue. Prevents DOS attacks and deals with leaks.
1095 if (!isSystemToast) {
1096 int count = 0;
1097 final int N = mToastQueue.size();
1098 for (int i=0; i<N; i++) {
1099 final ToastRecord r = mToastQueue.get(i);
1100 if (r.pkg.equals(pkg)) {
1101 count++;
1102 if (count >= MAX_PACKAGE_NOTIFICATIONS) {
1103 Slog.e(TAG, "Package has already posted " + count
1104 + " toasts. Not showing more. Package=" + pkg);
1105 return;
1106 }
1107 }
1108 }
1109 }
1110
1111 record = new ToastRecord(callingPid, pkg, callback, duration);
1112 mToastQueue.add(record);
1113 index = mToastQueue.size() - 1;
1114 keepProcessAliveLocked(callingPid);
1115 }
1116 // If it's at index 0, it's the current toast. It doesn't matter if it's
1117 // new or just been updated. Call back and tell it to show itself.
1118 // If the callback fails, this will remove it from the list, so don't
1119 // assume that it's valid after this.
1120 if (index == 0) {
1121 showNextToastLocked();
1122 }
1123 } finally {
1124 Binder.restoreCallingIdentity(callingId);
1125 }
1126 }
1127 }
1128
1129 @Override
1130 public void cancelToast(String pkg, ITransientNotification callback) {
1131 Slog.i(TAG, "cancelToast pkg=" + pkg + " callback=" + callback);
1132
1133 if (pkg == null || callback == null) {
1134 Slog.e(TAG, "Not cancelling notification. pkg=" + pkg + " callback=" + callback);
1135 return ;
1136 }
1137
1138 synchronized (mToastQueue) {
1139 long callingId = Binder.clearCallingIdentity();
1140 try {
1141 int index = indexOfToastLocked(pkg, callback);
1142 if (index >= 0) {
1143 cancelToastLocked(index);
1144 } else {
1145 Slog.w(TAG, "Toast already cancelled. pkg=" + pkg
1146 + " callback=" + callback);
1147 }
1148 } finally {
1149 Binder.restoreCallingIdentity(callingId);
1150 }
1151 }
1152 }
1153
1154 @Override
Christoph Studer8fd7f1e2014-04-11 17:35:05 -04001155 public void enqueueNotificationWithTag(String pkg, String opPkg, String tag, int id,
Adam Lesinski182f73f2013-12-05 16:48:06 -08001156 Notification notification, int[] idOut, int userId) throws RemoteException {
Christoph Studer8fd7f1e2014-04-11 17:35:05 -04001157 enqueueNotificationInternal(pkg, opPkg, Binder.getCallingUid(),
Adam Lesinski182f73f2013-12-05 16:48:06 -08001158 Binder.getCallingPid(), tag, id, notification, idOut, userId);
1159 }
1160
1161 @Override
1162 public void cancelNotificationWithTag(String pkg, String tag, int id, int userId) {
John Spurlock7340fc82014-04-24 18:50:12 -04001163 checkCallerIsSystemOrSameApp(pkg);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001164 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1165 Binder.getCallingUid(), userId, true, false, "cancelNotificationWithTag", pkg);
1166 // Don't allow client applications to cancel foreground service notis.
John Spurlocke6a7d932014-03-13 12:29:00 -04001167 cancelNotification(Binder.getCallingUid(), Binder.getCallingPid(), pkg, tag, id, 0,
Adam Lesinski182f73f2013-12-05 16:48:06 -08001168 Binder.getCallingUid() == Process.SYSTEM_UID
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04001169 ? 0 : Notification.FLAG_FOREGROUND_SERVICE, false, userId,
Chris Wren9fa689f2015-11-20 16:44:53 -05001170 REASON_APP_CANCEL, null);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001171 }
1172
1173 @Override
1174 public void cancelAllNotifications(String pkg, int userId) {
John Spurlock7340fc82014-04-24 18:50:12 -04001175 checkCallerIsSystemOrSameApp(pkg);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001176
1177 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1178 Binder.getCallingUid(), userId, true, false, "cancelAllNotifications", pkg);
1179
1180 // Calling from user space, don't allow the canceling of actively
1181 // running foreground services.
John Spurlocke6a7d932014-03-13 12:29:00 -04001182 cancelAllNotificationsInt(Binder.getCallingUid(), Binder.getCallingPid(),
1183 pkg, 0, Notification.FLAG_FOREGROUND_SERVICE, true, userId,
Chris Wren9fa689f2015-11-20 16:44:53 -05001184 REASON_APP_CANCEL_ALL, null);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001185 }
1186
1187 @Override
1188 public void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) {
John Spurlock7340fc82014-04-24 18:50:12 -04001189 checkCallerIsSystem();
Adam Lesinski182f73f2013-12-05 16:48:06 -08001190
1191 setNotificationsEnabledForPackageImpl(pkg, uid, enabled);
1192 }
1193
1194 /**
1195 * Use this when you just want to know if notifications are OK for this package.
1196 */
1197 @Override
1198 public boolean areNotificationsEnabledForPackage(String pkg, int uid) {
John Spurlock7340fc82014-04-24 18:50:12 -04001199 checkCallerIsSystem();
Adam Lesinski182f73f2013-12-05 16:48:06 -08001200 return (mAppOps.checkOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
1201 == AppOpsManager.MODE_ALLOWED);
1202 }
1203
Chris Wren54bbef42014-07-09 18:37:56 -04001204 @Override
Julia Reynolds233a5f92015-10-19 13:51:23 -04001205 public ParceledListSlice<Notification.Topic> getTopics(String pkg, int uid) {
Chris Wren54bbef42014-07-09 18:37:56 -04001206 checkCallerIsSystem();
Julia Reynolds233a5f92015-10-19 13:51:23 -04001207 return new ParceledListSlice<Notification.Topic>(mRankingHelper.getTopics(pkg, uid));
1208 }
1209
1210 @Override
1211 public void setTopicPriority(String pkg, int uid, Notification.Topic topic, int priority) {
1212 checkCallerIsSystem();
1213 mRankingHelper.setTopicPriority(pkg, uid, topic, priority);
Chris Wren54bbef42014-07-09 18:37:56 -04001214 savePolicyFile();
1215 }
1216
1217 @Override
Julia Reynolds233a5f92015-10-19 13:51:23 -04001218 public int getTopicPriority(String pkg, int uid, Notification.Topic topic) {
Chris Wren54bbef42014-07-09 18:37:56 -04001219 checkCallerIsSystem();
Julia Reynolds233a5f92015-10-19 13:51:23 -04001220 return mRankingHelper.getTopicPriority(pkg, uid, topic);
Chris Wren54bbef42014-07-09 18:37:56 -04001221 }
1222
Chris Wren3ad4e3a2014-09-02 17:23:51 -04001223 @Override
Julia Reynolds233a5f92015-10-19 13:51:23 -04001224 public void setTopicVisibilityOverride(String pkg, int uid, Notification.Topic topic,
1225 int visibility) {
Chris Wren3ad4e3a2014-09-02 17:23:51 -04001226 checkCallerIsSystem();
Julia Reynolds233a5f92015-10-19 13:51:23 -04001227 mRankingHelper.setTopicVisibilityOverride(pkg, uid, topic, visibility);
Chris Wren3ad4e3a2014-09-02 17:23:51 -04001228 savePolicyFile();
1229 }
1230
1231 @Override
Julia Reynolds233a5f92015-10-19 13:51:23 -04001232 public int getTopicVisibilityOverride(String pkg, int uid, Notification.Topic topic) {
Chris Wren3ad4e3a2014-09-02 17:23:51 -04001233 checkCallerIsSystem();
Julia Reynolds233a5f92015-10-19 13:51:23 -04001234 return mRankingHelper.getTopicVisibilityOverride(pkg, uid, topic);
Chris Wren3ad4e3a2014-09-02 17:23:51 -04001235 }
1236
Julia Reynolds5d25ee72015-11-20 15:38:20 -05001237 @Override
1238 public void setTopicImportance(String pkg, int uid, Notification.Topic topic,
1239 int importance) {
Julia Reynoldsead00aa2015-12-07 08:23:48 -05001240 enforceSystemOrSystemUI("Caller not system or systemui");
Julia Reynolds5d25ee72015-11-20 15:38:20 -05001241 mRankingHelper.setTopicImportance(pkg, uid, topic, importance);
1242 savePolicyFile();
1243 }
1244
1245 @Override
1246 public int getTopicImportance(String pkg, int uid, Notification.Topic topic) {
1247 checkCallerIsSystem();
1248 return mRankingHelper.getTopicImportance(pkg, uid, topic);
1249 }
1250
Adam Lesinski182f73f2013-12-05 16:48:06 -08001251 /**
1252 * System-only API for getting a list of current (i.e. not cleared) notifications.
1253 *
1254 * Requires ACCESS_NOTIFICATIONS which is signature|system.
Chris Wrenf9536642014-04-17 10:01:54 -04001255 * @returns A list of all the notifications, in natural order.
Adam Lesinski182f73f2013-12-05 16:48:06 -08001256 */
1257 @Override
1258 public StatusBarNotification[] getActiveNotifications(String callingPkg) {
1259 // enforce() will ensure the calling uid has the correct permission
1260 getContext().enforceCallingOrSelfPermission(
1261 android.Manifest.permission.ACCESS_NOTIFICATIONS,
1262 "NotificationManagerService.getActiveNotifications");
1263
1264 StatusBarNotification[] tmp = null;
1265 int uid = Binder.getCallingUid();
1266
1267 // noteOp will check to make sure the callingPkg matches the uid
1268 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
1269 == AppOpsManager.MODE_ALLOWED) {
1270 synchronized (mNotificationList) {
1271 tmp = new StatusBarNotification[mNotificationList.size()];
1272 final int N = mNotificationList.size();
1273 for (int i=0; i<N; i++) {
1274 tmp[i] = mNotificationList.get(i).sbn;
1275 }
1276 }
1277 }
1278 return tmp;
1279 }
1280
1281 /**
Dan Sandler994349c2015-04-15 11:02:54 -04001282 * Public API for getting a list of current notifications for the calling package/uid.
1283 *
1284 * @returns A list of all the package's notifications, in natural order.
1285 */
1286 @Override
1287 public ParceledListSlice<StatusBarNotification> getAppActiveNotifications(String pkg,
1288 int incomingUserId) {
1289 checkCallerIsSystemOrSameApp(pkg);
1290 int userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1291 Binder.getCallingUid(), incomingUserId, true, false,
1292 "getAppActiveNotifications", pkg);
1293
Erik Wolsheimer2242b4d2015-11-24 13:22:04 -08001294 final ArrayList<StatusBarNotification> list
1295 = new ArrayList<StatusBarNotification>(mNotificationList.size());
Dan Sandler994349c2015-04-15 11:02:54 -04001296
1297 synchronized (mNotificationList) {
Erik Wolsheimer2242b4d2015-11-24 13:22:04 -08001298 final int N = mNotificationList.size();
Dan Sandler994349c2015-04-15 11:02:54 -04001299 for (int i = 0; i < N; i++) {
1300 final StatusBarNotification sbn = mNotificationList.get(i).sbn;
1301 if (sbn.getPackageName().equals(pkg) && sbn.getUserId() == userId) {
1302 // We could pass back a cloneLight() but clients might get confused and
1303 // try to send this thing back to notify() again, which would not work
1304 // very well.
1305 final StatusBarNotification sbnOut = new StatusBarNotification(
1306 sbn.getPackageName(),
1307 sbn.getOpPkg(),
1308 sbn.getId(), sbn.getTag(), sbn.getUid(), sbn.getInitialPid(),
1309 0, // hide score from apps
1310 sbn.getNotification().clone(),
1311 sbn.getUser(), sbn.getPostTime());
1312 list.add(sbnOut);
1313 }
1314 }
1315 }
1316
1317 return new ParceledListSlice<StatusBarNotification>(list);
1318 }
1319
1320 /**
Adam Lesinski182f73f2013-12-05 16:48:06 -08001321 * System-only API for getting a list of recent (cleared, no longer shown) notifications.
1322 *
1323 * Requires ACCESS_NOTIFICATIONS which is signature|system.
1324 */
1325 @Override
1326 public StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count) {
1327 // enforce() will ensure the calling uid has the correct permission
1328 getContext().enforceCallingOrSelfPermission(
1329 android.Manifest.permission.ACCESS_NOTIFICATIONS,
1330 "NotificationManagerService.getHistoricalNotifications");
1331
1332 StatusBarNotification[] tmp = null;
1333 int uid = Binder.getCallingUid();
1334
1335 // noteOp will check to make sure the callingPkg matches the uid
1336 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
1337 == AppOpsManager.MODE_ALLOWED) {
1338 synchronized (mArchive) {
1339 tmp = mArchive.getArray(count);
1340 }
1341 }
1342 return tmp;
1343 }
1344
1345 /**
1346 * Register a listener binder directly with the notification manager.
1347 *
1348 * Only works with system callers. Apps should extend
1349 * {@link android.service.notification.NotificationListenerService}.
1350 */
1351 @Override
1352 public void registerListener(final INotificationListener listener,
1353 final ComponentName component, final int userid) {
Christoph Studer3e144d32014-05-22 16:48:40 +02001354 enforceSystemOrSystemUI("INotificationManager.registerListener");
John Spurlock7340fc82014-04-24 18:50:12 -04001355 mListeners.registerService(listener, component, userid);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001356 }
1357
1358 /**
1359 * Remove a listener binder directly
1360 */
1361 @Override
1362 public void unregisterListener(INotificationListener listener, int userid) {
John Spurlock7340fc82014-04-24 18:50:12 -04001363 mListeners.unregisterService(listener, userid);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001364 }
1365
1366 /**
1367 * Allow an INotificationListener to simulate a "clear all" operation.
1368 *
1369 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onClearAllNotifications}
1370 *
1371 * @param token The binder for the listener, to check that the caller is allowed
1372 */
1373 @Override
John Spurlocka4294292014-03-24 18:02:32 -04001374 public void cancelNotificationsFromListener(INotificationListener token, String[] keys) {
John Spurlocke6a7d932014-03-13 12:29:00 -04001375 final int callingUid = Binder.getCallingUid();
1376 final int callingPid = Binder.getCallingPid();
Adam Lesinski182f73f2013-12-05 16:48:06 -08001377 long identity = Binder.clearCallingIdentity();
1378 try {
Adam Lesinskie8240262014-03-26 16:01:00 -07001379 synchronized (mNotificationList) {
John Spurlock7340fc82014-04-24 18:50:12 -04001380 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
John Spurlocka4294292014-03-24 18:02:32 -04001381 if (keys != null) {
1382 final int N = keys.length;
1383 for (int i = 0; i < N; i++) {
1384 NotificationRecord r = mNotificationsByKey.get(keys[i]);
Griff Hazen335e1f02014-09-11 14:49:31 -07001385 if (r == null) continue;
Kenny Guya263e4e2014-03-03 18:24:03 +00001386 final int userId = r.sbn.getUserId();
1387 if (userId != info.userid && userId != UserHandle.USER_ALL &&
John Spurlockb408e8e2014-04-23 21:12:45 -04001388 !mUserProfiles.isCurrentProfile(userId)) {
Kenny Guya263e4e2014-03-03 18:24:03 +00001389 throw new SecurityException("Disallowed call from listener: "
John Spurlock7340fc82014-04-24 18:50:12 -04001390 + info.service);
Kenny Guya263e4e2014-03-03 18:24:03 +00001391 }
Griff Hazen335e1f02014-09-11 14:49:31 -07001392 cancelNotificationFromListenerLocked(info, callingUid, callingPid,
1393 r.sbn.getPackageName(), r.sbn.getTag(), r.sbn.getId(),
1394 userId);
John Spurlocka4294292014-03-24 18:02:32 -04001395 }
1396 } else {
1397 cancelAllLocked(callingUid, callingPid, info.userid,
Kenny Guya263e4e2014-03-03 18:24:03 +00001398 REASON_LISTENER_CANCEL_ALL, info, info.supportsProfiles());
John Spurlocka4294292014-03-24 18:02:32 -04001399 }
Adam Lesinskie8240262014-03-26 16:01:00 -07001400 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001401 } finally {
1402 Binder.restoreCallingIdentity(identity);
1403 }
1404 }
1405
Amith Yamasanif47e51e2015-04-17 10:02:15 -07001406 @Override
1407 public void setNotificationsShownFromListener(INotificationListener token, String[] keys) {
Amith Yamasanif47e51e2015-04-17 10:02:15 -07001408 long identity = Binder.clearCallingIdentity();
1409 try {
1410 synchronized (mNotificationList) {
1411 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1412 if (keys != null) {
1413 final int N = keys.length;
1414 for (int i = 0; i < N; i++) {
1415 NotificationRecord r = mNotificationsByKey.get(keys[i]);
1416 if (r == null) continue;
1417 final int userId = r.sbn.getUserId();
1418 if (userId != info.userid && userId != UserHandle.USER_ALL &&
1419 !mUserProfiles.isCurrentProfile(userId)) {
1420 throw new SecurityException("Disallowed call from listener: "
1421 + info.service);
1422 }
1423 if (!r.isSeen()) {
1424 if (DBG) Slog.d(TAG, "Marking notification as seen " + keys[i]);
1425 mAppUsageStats.reportEvent(r.sbn.getPackageName(),
Xiaohui Chenddbe4ca2015-08-13 16:20:56 -07001426 userId == UserHandle.USER_ALL ? UserHandle.USER_SYSTEM
Amith Yamasanif47e51e2015-04-17 10:02:15 -07001427 : userId,
Adam Lesinskic8e87292015-06-10 15:33:45 -07001428 UsageEvents.Event.USER_INTERACTION);
Amith Yamasanif47e51e2015-04-17 10:02:15 -07001429 r.setSeen();
1430 }
1431 }
1432 }
1433 }
1434 } finally {
1435 Binder.restoreCallingIdentity(identity);
1436 }
1437 }
1438
John Spurlock7340fc82014-04-24 18:50:12 -04001439 private void cancelNotificationFromListenerLocked(ManagedServiceInfo info,
Kenny Guya263e4e2014-03-03 18:24:03 +00001440 int callingUid, int callingPid, String pkg, String tag, int id, int userId) {
John Spurlocka4294292014-03-24 18:02:32 -04001441 cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
1442 Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
1443 true,
Kenny Guya263e4e2014-03-03 18:24:03 +00001444 userId, REASON_LISTENER_CANCEL, info);
John Spurlocka4294292014-03-24 18:02:32 -04001445 }
1446
Adam Lesinski182f73f2013-12-05 16:48:06 -08001447 /**
1448 * Allow an INotificationListener to simulate clearing (dismissing) a single notification.
1449 *
1450 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onNotificationClear}
1451 *
1452 * @param token The binder for the listener, to check that the caller is allowed
1453 */
1454 @Override
1455 public void cancelNotificationFromListener(INotificationListener token, String pkg,
1456 String tag, int id) {
John Spurlocke6a7d932014-03-13 12:29:00 -04001457 final int callingUid = Binder.getCallingUid();
1458 final int callingPid = Binder.getCallingPid();
Adam Lesinski182f73f2013-12-05 16:48:06 -08001459 long identity = Binder.clearCallingIdentity();
1460 try {
Adam Lesinskie8240262014-03-26 16:01:00 -07001461 synchronized (mNotificationList) {
John Spurlock7340fc82014-04-24 18:50:12 -04001462 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
Kenny Guya263e4e2014-03-03 18:24:03 +00001463 if (info.supportsProfiles()) {
1464 Log.e(TAG, "Ignoring deprecated cancelNotification(pkg, tag, id) "
1465 + "from " + info.component
1466 + " use cancelNotification(key) instead.");
1467 } else {
1468 cancelNotificationFromListenerLocked(info, callingUid, callingPid,
1469 pkg, tag, id, info.userid);
1470 }
Adam Lesinskie8240262014-03-26 16:01:00 -07001471 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001472 } finally {
1473 Binder.restoreCallingIdentity(identity);
1474 }
1475 }
1476
1477 /**
1478 * Allow an INotificationListener to request the list of outstanding notifications seen by
1479 * the current user. Useful when starting up, after which point the listener callbacks
1480 * should be used.
1481 *
1482 * @param token The binder for the listener, to check that the caller is allowed
Dan Sandlerea75fdd2014-08-12 12:29:19 -04001483 * @param keys An array of notification keys to fetch, or null to fetch everything
Chris Wrenf9536642014-04-17 10:01:54 -04001484 * @returns The return value will contain the notifications specified in keys, in that
1485 * order, or if keys is null, all the notifications, in natural order.
Adam Lesinski182f73f2013-12-05 16:48:06 -08001486 */
1487 @Override
Christoph Studercee44ba2014-05-20 18:36:43 +02001488 public ParceledListSlice<StatusBarNotification> getActiveNotificationsFromListener(
Christoph Studerb82bc782014-08-20 14:29:43 +02001489 INotificationListener token, String[] keys, int trim) {
Adam Lesinski182f73f2013-12-05 16:48:06 -08001490 synchronized (mNotificationList) {
John Spurlock7340fc82014-04-24 18:50:12 -04001491 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
Dan Sandlerea75fdd2014-08-12 12:29:19 -04001492 final boolean getKeys = keys != null;
1493 final int N = getKeys ? keys.length : mNotificationList.size();
Christoph Studerb82bc782014-08-20 14:29:43 +02001494 final ArrayList<StatusBarNotification> list
1495 = new ArrayList<StatusBarNotification>(N);
Christoph Studercee44ba2014-05-20 18:36:43 +02001496 for (int i=0; i<N; i++) {
Dan Sandlerea75fdd2014-08-12 12:29:19 -04001497 final NotificationRecord r = getKeys
1498 ? mNotificationsByKey.get(keys[i])
1499 : mNotificationList.get(i);
Christoph Studerb82bc782014-08-20 14:29:43 +02001500 if (r == null) continue;
1501 StatusBarNotification sbn = r.sbn;
1502 if (!isVisibleToListener(sbn, info)) continue;
1503 StatusBarNotification sbnToSend =
1504 (trim == TRIM_FULL) ? sbn : sbn.cloneLight();
1505 list.add(sbnToSend);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001506 }
Christoph Studercee44ba2014-05-20 18:36:43 +02001507 return new ParceledListSlice<StatusBarNotification>(list);
Adam Lesinski182f73f2013-12-05 16:48:06 -08001508 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001509 }
1510
1511 @Override
John Spurlockd8afe3c2014-08-01 14:04:07 -04001512 public void requestHintsFromListener(INotificationListener token, int hints) {
1513 final long identity = Binder.clearCallingIdentity();
1514 try {
1515 synchronized (mNotificationList) {
1516 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1517 final boolean disableEffects = (hints & HINT_HOST_DISABLE_EFFECTS) != 0;
1518 if (disableEffects) {
1519 mListenersDisablingEffects.add(info);
1520 } else {
1521 mListenersDisablingEffects.remove(info);
1522 }
John Spurlockd8afe3c2014-08-01 14:04:07 -04001523 updateListenerHintsLocked();
John Spurlockb4782522014-08-22 14:54:46 -04001524 updateEffectsSuppressorLocked();
John Spurlock1fa865f2014-07-21 14:56:39 -04001525 }
John Spurlockd8afe3c2014-08-01 14:04:07 -04001526 } finally {
1527 Binder.restoreCallingIdentity(identity);
John Spurlock1fa865f2014-07-21 14:56:39 -04001528 }
1529 }
1530
1531 @Override
John Spurlockd8afe3c2014-08-01 14:04:07 -04001532 public int getHintsFromListener(INotificationListener token) {
John Spurlock1fa865f2014-07-21 14:56:39 -04001533 synchronized (mNotificationList) {
John Spurlockd8afe3c2014-08-01 14:04:07 -04001534 return mListenerHints;
John Spurlock1fa865f2014-07-21 14:56:39 -04001535 }
1536 }
1537
1538 @Override
Christoph Studer85a384b2014-08-27 20:16:15 +02001539 public void requestInterruptionFilterFromListener(INotificationListener token,
1540 int interruptionFilter) throws RemoteException {
1541 final long identity = Binder.clearCallingIdentity();
1542 try {
1543 synchronized (mNotificationList) {
John Spurlock661f2cf2014-11-17 10:29:10 -05001544 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1545 mZenModeHelper.requestFromListener(info.component, interruptionFilter);
Christoph Studer85a384b2014-08-27 20:16:15 +02001546 updateInterruptionFilterLocked();
1547 }
1548 } finally {
1549 Binder.restoreCallingIdentity(identity);
1550 }
1551 }
1552
1553 @Override
1554 public int getInterruptionFilterFromListener(INotificationListener token)
1555 throws RemoteException {
1556 synchronized (mNotificationLight) {
1557 return mInterruptionFilter;
1558 }
1559 }
1560
1561 @Override
Christoph Studerb82bc782014-08-20 14:29:43 +02001562 public void setOnNotificationPostedTrimFromListener(INotificationListener token, int trim)
1563 throws RemoteException {
1564 synchronized (mNotificationList) {
1565 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1566 if (info == null) return;
1567 mListeners.setOnNotificationPostedTrimLocked(info, trim);
1568 }
1569 }
1570
1571 @Override
John Spurlockb2278d62015-04-07 12:47:12 -04001572 public int getZenMode() {
1573 return mZenModeHelper.getZenMode();
1574 }
1575
1576 @Override
John Spurlock056c5192014-04-20 21:52:01 -04001577 public ZenModeConfig getZenModeConfig() {
John Spurlockcdb57ae2015-02-11 19:04:11 -05001578 enforceSystemOrSystemUIOrVolume("INotificationManager.getZenModeConfig");
John Spurlock056c5192014-04-20 21:52:01 -04001579 return mZenModeHelper.getConfig();
1580 }
1581
1582 @Override
John Spurlockb2278d62015-04-07 12:47:12 -04001583 public void setZenMode(int mode, Uri conditionId, String reason) throws RemoteException {
John Spurlockcdb57ae2015-02-11 19:04:11 -05001584 enforceSystemOrSystemUIOrVolume("INotificationManager.setZenMode");
1585 final long identity = Binder.clearCallingIdentity();
1586 try {
John Spurlockb2278d62015-04-07 12:47:12 -04001587 mZenModeHelper.setManualZenMode(mode, conditionId, reason);
John Spurlockcdb57ae2015-02-11 19:04:11 -05001588 } finally {
1589 Binder.restoreCallingIdentity(identity);
1590 }
1591 }
1592
1593 @Override
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04001594 public List<AutomaticZenRule> getAutomaticZenRules() throws RemoteException {
1595 enforcePolicyAccess(Binder.getCallingUid(), "getAutomaticZenRules");
1596 return mZenModeHelper.getAutomaticZenRules();
1597 }
1598
1599 @Override
Julia Reynolds4fe98d62015-10-06 16:23:41 -04001600 public AutomaticZenRule getAutomaticZenRule(String id) throws RemoteException {
1601 Preconditions.checkNotNull(id, "Id is null");
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04001602 enforcePolicyAccess(Binder.getCallingUid(), "getAutomaticZenRule");
Julia Reynolds4fe98d62015-10-06 16:23:41 -04001603 return mZenModeHelper.getAutomaticZenRule(id);
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04001604 }
1605
1606 @Override
Julia Reynolds4fe98d62015-10-06 16:23:41 -04001607 public AutomaticZenRule addAutomaticZenRule(AutomaticZenRule automaticZenRule)
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04001608 throws RemoteException {
1609 Preconditions.checkNotNull(automaticZenRule, "automaticZenRule is null");
1610 Preconditions.checkNotNull(automaticZenRule.getName(), "Name is null");
1611 Preconditions.checkNotNull(automaticZenRule.getOwner(), "Owner is null");
1612 Preconditions.checkNotNull(automaticZenRule.getConditionId(), "ConditionId is null");
Julia Reynolds4fe98d62015-10-06 16:23:41 -04001613 enforcePolicyAccess(Binder.getCallingUid(), "addAutomaticZenRule");
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04001614
Julia Reynolds4fe98d62015-10-06 16:23:41 -04001615 return mZenModeHelper.addAutomaticZenRule(automaticZenRule,
1616 "addAutomaticZenRule");
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04001617 }
1618
1619 @Override
Julia Reynolds4fe98d62015-10-06 16:23:41 -04001620 public boolean updateAutomaticZenRule(AutomaticZenRule automaticZenRule)
1621 throws RemoteException {
1622 Preconditions.checkNotNull(automaticZenRule, "automaticZenRule is null");
1623 Preconditions.checkNotNull(automaticZenRule.getName(), "Name is null");
1624 Preconditions.checkNotNull(automaticZenRule.getOwner(), "Owner is null");
1625 Preconditions.checkNotNull(automaticZenRule.getConditionId(), "ConditionId is null");
1626 enforcePolicyAccess(Binder.getCallingUid(), "updateAutomaticZenRule");
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04001627
Julia Reynolds4fe98d62015-10-06 16:23:41 -04001628 return mZenModeHelper.updateAutomaticZenRule(automaticZenRule,
1629 "updateAutomaticZenRule");
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04001630 }
1631
1632 @Override
Julia Reynolds4fe98d62015-10-06 16:23:41 -04001633 public boolean removeAutomaticZenRule(String id) throws RemoteException {
1634 Preconditions.checkNotNull(id, "Id is null");
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04001635 // Verify that they can modify zen rules.
1636 enforcePolicyAccess(Binder.getCallingUid(), "removeAutomaticZenRule");
1637
Julia Reynolds4fe98d62015-10-06 16:23:41 -04001638 return mZenModeHelper.removeAutomaticZenRule(id, "removeAutomaticZenRule");
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04001639 }
1640
1641 @Override
Julia Reynoldsc8e54e82015-11-30 16:43:05 -05001642 public boolean removeAutomaticZenRules(String packageName) throws RemoteException {
1643 Preconditions.checkNotNull(packageName, "Package name is null");
1644 enforceSystemOrSystemUI("removeAutomaticZenRules");
1645
1646 return mZenModeHelper.removeAutomaticZenRules(packageName, "removeAutomaticZenRules");
1647 }
1648
1649 @Override
John Spurlock80774932015-05-07 17:38:50 -04001650 public void setInterruptionFilter(String pkg, int filter) throws RemoteException {
1651 enforcePolicyAccess(pkg, "setInterruptionFilter");
1652 final int zen = NotificationManager.zenModeFromInterruptionFilter(filter, -1);
1653 if (zen == -1) throw new IllegalArgumentException("Invalid filter: " + filter);
1654 final long identity = Binder.clearCallingIdentity();
1655 try {
1656 mZenModeHelper.setManualZenMode(zen, null, "setInterruptionFilter");
1657 } finally {
1658 Binder.restoreCallingIdentity(identity);
1659 }
1660 }
1661
1662 @Override
John Spurlocka7d92b12015-05-13 14:48:02 -04001663 public void notifyConditions(final String pkg, IConditionProvider provider,
1664 final Condition[] conditions) {
John Spurlocke77bb362014-04-26 10:24:59 -04001665 final ManagedServiceInfo info = mConditionProviders.checkServiceToken(provider);
1666 checkCallerIsSystemOrSameApp(pkg);
John Spurlocka7d92b12015-05-13 14:48:02 -04001667 mHandler.post(new Runnable() {
1668 @Override
1669 public void run() {
1670 mConditionProviders.notifyConditions(pkg, info, conditions);
1671 }
1672 });
John Spurlocke77bb362014-04-26 10:24:59 -04001673 }
1674
John Spurlockcdb57ae2015-02-11 19:04:11 -05001675 private void enforceSystemOrSystemUIOrVolume(String message) {
1676 if (mAudioManagerInternal != null) {
1677 final int vcuid = mAudioManagerInternal.getVolumeControllerUid();
1678 if (vcuid > 0 && Binder.getCallingUid() == vcuid) {
1679 return;
1680 }
1681 }
1682 enforceSystemOrSystemUI(message);
1683 }
1684
John Spurlocke77bb362014-04-26 10:24:59 -04001685 private void enforceSystemOrSystemUI(String message) {
1686 if (isCallerSystem()) return;
1687 getContext().enforceCallingPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
1688 message);
John Spurlock7340fc82014-04-24 18:50:12 -04001689 }
1690
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04001691 private void enforcePolicyAccess(int uid, String method) {
1692 if (PackageManager.PERMISSION_GRANTED == getContext().checkCallingPermission(
1693 android.Manifest.permission.MANAGE_NOTIFICATIONS)) {
1694 return;
1695 }
1696 boolean accessAllowed = false;
1697 String[] packages = getContext().getPackageManager().getPackagesForUid(uid);
1698 final int packageCount = packages.length;
1699 for (int i = 0; i < packageCount; i++) {
1700 if (checkPolicyAccess(packages[i])) {
1701 accessAllowed = true;
1702 }
1703 }
1704 if (!accessAllowed) {
1705 Slog.w(TAG, "Notification policy access denied calling " + method);
1706 throw new SecurityException("Notification policy access denied");
1707 }
1708 }
1709
John Spurlock80774932015-05-07 17:38:50 -04001710 private void enforcePolicyAccess(String pkg, String method) {
Julia Reynolds6ee26172015-09-28 11:34:48 -04001711 if (PackageManager.PERMISSION_GRANTED == getContext().checkCallingPermission(
1712 android.Manifest.permission.MANAGE_NOTIFICATIONS)) {
1713 return;
1714 }
John Spurlock80774932015-05-07 17:38:50 -04001715 if (!checkPolicyAccess(pkg)) {
1716 Slog.w(TAG, "Notification policy access denied calling " + method);
1717 throw new SecurityException("Notification policy access denied");
John Spurlock1fc476d2015-04-14 16:05:20 -04001718 }
1719 }
1720
John Spurlock80774932015-05-07 17:38:50 -04001721 private boolean checkPackagePolicyAccess(String pkg) {
John Spurlock7c74f782015-06-04 13:01:42 -04001722 return mPolicyAccess.isPackageGranted(pkg);
John Spurlock80774932015-05-07 17:38:50 -04001723 }
1724
1725 private boolean checkPolicyAccess(String pkg) {
1726 return checkPackagePolicyAccess(pkg) || mListeners.isComponentEnabledForPackage(pkg);
John Spurlock1fc476d2015-04-14 16:05:20 -04001727 }
1728
John Spurlock7340fc82014-04-24 18:50:12 -04001729 @Override
Adam Lesinski182f73f2013-12-05 16:48:06 -08001730 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1731 if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
1732 != PackageManager.PERMISSION_GRANTED) {
John Spurlock2b122f42014-08-27 16:29:47 -04001733 pw.println("Permission Denial: can't dump NotificationManager from pid="
Adam Lesinski182f73f2013-12-05 16:48:06 -08001734 + Binder.getCallingPid()
1735 + ", uid=" + Binder.getCallingUid());
1736 return;
1737 }
1738
Chris Wrene4b38802015-07-07 15:54:19 -04001739 final DumpFilter filter = DumpFilter.parseFromArguments(args);
1740 if (filter != null && filter.stats) {
1741 dumpJson(pw, filter);
1742 } else {
1743 dumpImpl(pw, filter);
1744 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001745 }
John Spurlockb4782522014-08-22 14:54:46 -04001746
1747 @Override
1748 public ComponentName getEffectsSuppressor() {
John Spurlockcdb57ae2015-02-11 19:04:11 -05001749 enforceSystemOrSystemUIOrVolume("INotificationManager.getEffectsSuppressor");
John Spurlockb4782522014-08-22 14:54:46 -04001750 return mEffectsSuppressor;
1751 }
John Spurlock2b122f42014-08-27 16:29:47 -04001752
1753 @Override
1754 public boolean matchesCallFilter(Bundle extras) {
1755 enforceSystemOrSystemUI("INotificationManager.matchesCallFilter");
Christoph Studer12aeda82014-09-23 19:08:56 +02001756 return mZenModeHelper.matchesCallFilter(
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07001757 Binder.getCallingUserHandle(),
Christoph Studer12aeda82014-09-23 19:08:56 +02001758 extras,
1759 mRankingHelper.findExtractor(ValidateNotificationPeople.class),
1760 MATCHES_CALL_FILTER_CONTACTS_TIMEOUT_MS,
1761 MATCHES_CALL_FILTER_TIMEOUT_AFFINITY);
John Spurlock2b122f42014-08-27 16:29:47 -04001762 }
John Spurlock530052a2014-11-30 16:26:19 -05001763
1764 @Override
1765 public boolean isSystemConditionProviderEnabled(String path) {
John Spurlockcdb57ae2015-02-11 19:04:11 -05001766 enforceSystemOrSystemUIOrVolume("INotificationManager.isSystemConditionProviderEnabled");
John Spurlockb2278d62015-04-07 12:47:12 -04001767 return mConditionProviders.isSystemProviderEnabled(path);
John Spurlock530052a2014-11-30 16:26:19 -05001768 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001769
Christopher Tatef9767d62015-04-08 14:35:43 -07001770 // Backup/restore interface
1771 @Override
1772 public byte[] getBackupPayload(int user) {
John Spurlock35ef0a62015-05-28 11:24:10 -04001773 if (DBG) Slog.d(TAG, "getBackupPayload u=" + user);
Xiaohui Chenddbe4ca2015-08-13 16:20:56 -07001774 //TODO: http://b/22388012
1775 if (user != UserHandle.USER_SYSTEM) {
John Spurlock35ef0a62015-05-28 11:24:10 -04001776 Slog.w(TAG, "getBackupPayload: cannot backup policy for user " + user);
1777 return null;
1778 }
1779 final ByteArrayOutputStream baos = new ByteArrayOutputStream();
1780 try {
1781 writePolicyXml(baos, true /*forBackup*/);
1782 return baos.toByteArray();
1783 } catch (IOException e) {
1784 Slog.w(TAG, "getBackupPayload: error writing payload for user " + user, e);
1785 }
Christopher Tatef9767d62015-04-08 14:35:43 -07001786 return null;
1787 }
1788
1789 @Override
1790 public void applyRestore(byte[] payload, int user) {
John Spurlock35ef0a62015-05-28 11:24:10 -04001791 if (DBG) Slog.d(TAG, "applyRestore u=" + user + " payload="
1792 + (payload != null ? new String(payload, StandardCharsets.UTF_8) : null));
1793 if (payload == null) {
1794 Slog.w(TAG, "applyRestore: no payload to restore for user " + user);
1795 return;
1796 }
Xiaohui Chenddbe4ca2015-08-13 16:20:56 -07001797 //TODO: http://b/22388012
1798 if (user != UserHandle.USER_SYSTEM) {
John Spurlock35ef0a62015-05-28 11:24:10 -04001799 Slog.w(TAG, "applyRestore: cannot restore policy for user " + user);
1800 return;
1801 }
1802 final ByteArrayInputStream bais = new ByteArrayInputStream(payload);
1803 try {
1804 readPolicyXml(bais, true /*forRestore*/);
1805 savePolicyFile();
1806 } catch (NumberFormatException | XmlPullParserException | IOException e) {
1807 Slog.w(TAG, "applyRestore: error reading payload", e);
1808 }
Christopher Tatef9767d62015-04-08 14:35:43 -07001809 }
1810
John Spurlock1fc476d2015-04-14 16:05:20 -04001811 @Override
John Spurlock80774932015-05-07 17:38:50 -04001812 public boolean isNotificationPolicyAccessGranted(String pkg) {
1813 return checkPolicyAccess(pkg);
John Spurlock1fc476d2015-04-14 16:05:20 -04001814 }
1815
1816 @Override
John Spurlock80774932015-05-07 17:38:50 -04001817 public boolean isNotificationPolicyAccessGrantedForPackage(String pkg) {
1818 enforceSystemOrSystemUI("request policy access status for another package");
1819 return checkPackagePolicyAccess(pkg);
1820 }
1821
1822 @Override
1823 public String[] getPackagesRequestingNotificationPolicyAccess()
1824 throws RemoteException {
1825 enforceSystemOrSystemUI("request policy access packages");
1826 final long identity = Binder.clearCallingIdentity();
1827 try {
John Spurlock7c74f782015-06-04 13:01:42 -04001828 return mPolicyAccess.getRequestingPackages();
John Spurlock80774932015-05-07 17:38:50 -04001829 } finally {
1830 Binder.restoreCallingIdentity(identity);
1831 }
1832 }
1833
1834 @Override
1835 public void setNotificationPolicyAccessGranted(String pkg, boolean granted)
1836 throws RemoteException {
1837 enforceSystemOrSystemUI("grant notification policy access");
1838 final long identity = Binder.clearCallingIdentity();
1839 try {
1840 synchronized (mNotificationList) {
1841 mPolicyAccess.put(pkg, granted);
1842 }
1843 } finally {
1844 Binder.restoreCallingIdentity(identity);
1845 }
1846 }
1847
1848 @Override
1849 public Policy getNotificationPolicy(String pkg) {
1850 enforcePolicyAccess(pkg, "getNotificationPolicy");
John Spurlock1fc476d2015-04-14 16:05:20 -04001851 final long identity = Binder.clearCallingIdentity();
1852 try {
1853 return mZenModeHelper.getNotificationPolicy();
1854 } finally {
1855 Binder.restoreCallingIdentity(identity);
1856 }
1857 }
1858
1859 @Override
John Spurlock80774932015-05-07 17:38:50 -04001860 public void setNotificationPolicy(String pkg, Policy policy) {
1861 enforcePolicyAccess(pkg, "setNotificationPolicy");
John Spurlock1fc476d2015-04-14 16:05:20 -04001862 final long identity = Binder.clearCallingIdentity();
1863 try {
1864 mZenModeHelper.setNotificationPolicy(policy);
1865 } finally {
1866 Binder.restoreCallingIdentity(identity);
1867 }
1868 }
1869 };
John Spurlocka4294292014-03-24 18:02:32 -04001870
John Spurlock32fe4c62014-10-02 12:16:02 -04001871 private String disableNotificationEffects(NotificationRecord record) {
1872 if (mDisableNotificationEffects) {
1873 return "booleanState";
1874 }
1875 if ((mListenerHints & HINT_HOST_DISABLE_EFFECTS) != 0) {
1876 return "listenerHints";
1877 }
1878 if (mCallState != TelephonyManager.CALL_STATE_IDLE && !mZenModeHelper.isCall(record)) {
1879 return "callState";
1880 }
1881 return null;
Chris Wrene4b38802015-07-07 15:54:19 -04001882 };
1883
1884 private void dumpJson(PrintWriter pw, DumpFilter filter) {
1885 JSONObject dump = new JSONObject();
1886 try {
1887 dump.put("service", "Notification Manager");
1888 JSONArray bans = new JSONArray();
1889 try {
1890 ArrayMap<Integer, ArrayList<String>> packageBans = getPackageBans(filter);
1891 for (Integer userId : packageBans.keySet()) {
1892 for (String packageName : packageBans.get(userId)) {
1893 JSONObject ban = new JSONObject();
1894 ban.put("userId", userId);
1895 ban.put("packageName", packageName);
1896 bans.put(ban);
1897 }
1898 }
1899 } catch (NameNotFoundException e) {
1900 // pass
1901 }
1902 dump.put("bans", bans);
1903 dump.put("stats", mUsageStats.dumpJson(filter));
1904 } catch (JSONException e) {
1905 e.printStackTrace();
1906 }
1907 pw.println(dump);
John Spurlock1fa865f2014-07-21 14:56:39 -04001908 }
1909
John Spurlock25e2d242014-06-27 13:58:23 -04001910 void dumpImpl(PrintWriter pw, DumpFilter filter) {
1911 pw.print("Current Notification Manager state");
Dan Sandlera1770312015-07-10 13:59:29 -04001912 if (filter.filtered) {
John Spurlock50806fc2014-07-15 10:22:02 -04001913 pw.print(" (filtered to "); pw.print(filter); pw.print(")");
John Spurlock25e2d242014-06-27 13:58:23 -04001914 }
1915 pw.println(':');
Adam Lesinski182f73f2013-12-05 16:48:06 -08001916 int N;
Julia Reynoldse6b53e62015-07-31 09:25:10 -04001917 final boolean zenOnly = filter.filtered && filter.zen;
Adam Lesinski182f73f2013-12-05 16:48:06 -08001918
John Spurlock50806fc2014-07-15 10:22:02 -04001919 if (!zenOnly) {
1920 synchronized (mToastQueue) {
1921 N = mToastQueue.size();
1922 if (N > 0) {
1923 pw.println(" Toast Queue:");
1924 for (int i=0; i<N; i++) {
1925 mToastQueue.get(i).dump(pw, " ", filter);
1926 }
1927 pw.println(" ");
Adam Lesinski182f73f2013-12-05 16:48:06 -08001928 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001929 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001930 }
1931
1932 synchronized (mNotificationList) {
John Spurlock50806fc2014-07-15 10:22:02 -04001933 if (!zenOnly) {
1934 N = mNotificationList.size();
John Spurlock25e2d242014-06-27 13:58:23 -04001935 if (N > 0) {
John Spurlock50806fc2014-07-15 10:22:02 -04001936 pw.println(" Notification List:");
John Spurlock25e2d242014-06-27 13:58:23 -04001937 for (int i=0; i<N; i++) {
John Spurlock50806fc2014-07-15 10:22:02 -04001938 final NotificationRecord nr = mNotificationList.get(i);
Julia Reynoldse6b53e62015-07-31 09:25:10 -04001939 if (filter.filtered && !filter.matches(nr.sbn)) continue;
Dan Sandlera1770312015-07-10 13:59:29 -04001940 nr.dump(pw, " ", getContext(), filter.redact);
John Spurlock25e2d242014-06-27 13:58:23 -04001941 }
1942 pw.println(" ");
Adam Lesinski182f73f2013-12-05 16:48:06 -08001943 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001944
Julia Reynoldse6b53e62015-07-31 09:25:10 -04001945 if (!filter.filtered) {
John Spurlock50806fc2014-07-15 10:22:02 -04001946 N = mLights.size();
1947 if (N > 0) {
1948 pw.println(" Lights List:");
1949 for (int i=0; i<N; i++) {
Chris Wren6054e612014-11-25 17:16:46 -05001950 if (i == N - 1) {
1951 pw.print(" > ");
1952 } else {
1953 pw.print(" ");
1954 }
1955 pw.println(mLights.get(i));
John Spurlock50806fc2014-07-15 10:22:02 -04001956 }
1957 pw.println(" ");
1958 }
John Spurlockcb566aa2014-08-03 22:58:28 -04001959 pw.println(" mUseAttentionLight=" + mUseAttentionLight);
1960 pw.println(" mNotificationPulseEnabled=" + mNotificationPulseEnabled);
Chris Wren6054e612014-11-25 17:16:46 -05001961 pw.println(" mSoundNotificationKey=" + mSoundNotificationKey);
1962 pw.println(" mVibrateNotificationKey=" + mVibrateNotificationKey);
John Spurlockd8afe3c2014-08-01 14:04:07 -04001963 pw.println(" mDisableNotificationEffects=" + mDisableNotificationEffects);
John Spurlock32fe4c62014-10-02 12:16:02 -04001964 pw.println(" mCallState=" + callStateToString(mCallState));
John Spurlock50806fc2014-07-15 10:22:02 -04001965 pw.println(" mSystemReady=" + mSystemReady);
1966 }
1967 pw.println(" mArchive=" + mArchive.toString());
1968 Iterator<StatusBarNotification> iter = mArchive.descendingIterator();
1969 int i=0;
1970 while (iter.hasNext()) {
1971 final StatusBarNotification sbn = iter.next();
1972 if (filter != null && !filter.matches(sbn)) continue;
1973 pw.println(" " + sbn);
1974 if (++i >= 5) {
1975 if (iter.hasNext()) pw.println(" ...");
1976 break;
1977 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08001978 }
1979 }
1980
John Spurlock50806fc2014-07-15 10:22:02 -04001981 if (!zenOnly) {
1982 pw.println("\n Usage Stats:");
1983 mUsageStats.dump(pw, " ", filter);
1984 }
Christoph Studer546bec82014-03-14 12:17:12 +01001985
Julia Reynoldse6b53e62015-07-31 09:25:10 -04001986 if (!filter.filtered || zenOnly) {
John Spurlock25e2d242014-06-27 13:58:23 -04001987 pw.println("\n Zen Mode:");
John Spurlockf3701772015-02-12 13:29:37 -05001988 pw.print(" mInterruptionFilter="); pw.println(mInterruptionFilter);
John Spurlock25e2d242014-06-27 13:58:23 -04001989 mZenModeHelper.dump(pw, " ");
John Spurlock6ae82a72014-07-16 16:23:01 -04001990
1991 pw.println("\n Zen Log:");
1992 ZenLog.dump(pw, " ");
John Spurlock25e2d242014-06-27 13:58:23 -04001993 }
John Spurlocke77bb362014-04-26 10:24:59 -04001994
John Spurlock50806fc2014-07-15 10:22:02 -04001995 if (!zenOnly) {
1996 pw.println("\n Ranking Config:");
1997 mRankingHelper.dump(pw, " ", filter);
Chris Wren54bbef42014-07-09 18:37:56 -04001998
John Spurlock50806fc2014-07-15 10:22:02 -04001999 pw.println("\n Notification listeners:");
2000 mListeners.dump(pw, filter);
John Spurlockd8afe3c2014-08-01 14:04:07 -04002001 pw.print(" mListenerHints: "); pw.println(mListenerHints);
2002 pw.print(" mListenersDisablingEffects: (");
2003 N = mListenersDisablingEffects.size();
John Spurlock1fa865f2014-07-21 14:56:39 -04002004 for (int i = 0; i < N; i++) {
John Spurlockd8afe3c2014-08-01 14:04:07 -04002005 final ManagedServiceInfo listener = mListenersDisablingEffects.valueAt(i);
John Spurlock1fa865f2014-07-21 14:56:39 -04002006 if (i > 0) pw.print(',');
2007 pw.print(listener.component);
2008 }
2009 pw.println(')');
John Spurlock50806fc2014-07-15 10:22:02 -04002010 }
John Spurlock80774932015-05-07 17:38:50 -04002011 pw.println("\n Policy access:");
2012 pw.print(" mPolicyAccess: "); pw.println(mPolicyAccess);
John Spurlocke77bb362014-04-26 10:24:59 -04002013
2014 pw.println("\n Condition providers:");
John Spurlock25e2d242014-06-27 13:58:23 -04002015 mConditionProviders.dump(pw, filter);
Christoph Studer265c1052014-07-23 17:14:33 +02002016
2017 pw.println("\n Group summaries:");
2018 for (Entry<String, NotificationRecord> entry : mSummaryByGroupKey.entrySet()) {
2019 NotificationRecord r = entry.getValue();
2020 pw.println(" " + entry.getKey() + " -> " + r.getKey());
2021 if (mNotificationsByKey.get(r.getKey()) != r) {
2022 pw.println("!!!!!!LEAK: Record not found in mNotificationsByKey.");
Dan Sandlera1770312015-07-10 13:59:29 -04002023 r.dump(pw, " ", getContext(), filter.redact);
Christoph Studer265c1052014-07-23 17:14:33 +02002024 }
2025 }
Chris Wren66189fc2015-06-25 14:04:33 -04002026
2027 try {
2028 pw.println("\n Banned Packages:");
Chris Wrene4b38802015-07-07 15:54:19 -04002029 ArrayMap<Integer, ArrayList<String>> packageBans = getPackageBans(filter);
2030 for (Integer userId : packageBans.keySet()) {
2031 for (String packageName : packageBans.get(userId)) {
2032 pw.println(" " + userId + ": " + packageName);
Chris Wren66189fc2015-06-25 14:04:33 -04002033 }
2034 }
2035 } catch (NameNotFoundException e) {
2036 // pass
2037 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002038 }
2039 }
2040
Chris Wrene4b38802015-07-07 15:54:19 -04002041 private ArrayMap<Integer, ArrayList<String>> getPackageBans(DumpFilter filter)
2042 throws NameNotFoundException {
2043 ArrayMap<Integer, ArrayList<String>> packageBans = new ArrayMap<>();
2044 ArrayList<String> packageNames = new ArrayList<>();
2045 for (UserInfo user : UserManager.get(getContext()).getUsers()) {
2046 final int userId = user.getUserHandle().getIdentifier();
2047 final PackageManager packageManager = getContext().getPackageManager();
2048 List<PackageInfo> packages = packageManager.getInstalledPackages(0, userId);
2049 final int packageCount = packages.size();
2050 for (int p = 0; p < packageCount; p++) {
2051 final String packageName = packages.get(p).packageName;
2052 if (filter == null || filter.matches(packageName)) {
2053 final int uid = packageManager.getPackageUid(packageName, userId);
2054 if (!checkNotificationOp(packageName, uid)) {
2055 packageNames.add(packageName);
2056 }
2057 }
2058 }
2059 if (!packageNames.isEmpty()) {
2060 packageBans.put(userId, packageNames);
2061 packageNames = new ArrayList<>();
2062 }
2063 }
2064 return packageBans;
2065 }
2066
Adam Lesinski182f73f2013-12-05 16:48:06 -08002067 /**
2068 * The private API only accessible to the system process.
2069 */
2070 private final NotificationManagerInternal mInternalService = new NotificationManagerInternal() {
2071 @Override
Christoph Studer8fd7f1e2014-04-11 17:35:05 -04002072 public void enqueueNotification(String pkg, String opPkg, int callingUid, int callingPid,
Adam Lesinski182f73f2013-12-05 16:48:06 -08002073 String tag, int id, Notification notification, int[] idReceived, int userId) {
Christoph Studer8fd7f1e2014-04-11 17:35:05 -04002074 enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, notification,
Adam Lesinski182f73f2013-12-05 16:48:06 -08002075 idReceived, userId);
2076 }
Christoph Studer365e4c32014-09-18 20:35:36 +02002077
2078 @Override
2079 public void removeForegroundServiceFlagFromNotification(String pkg, int notificationId,
2080 int userId) {
2081 checkCallerIsSystem();
2082 synchronized (mNotificationList) {
2083 int i = indexOfNotificationLocked(pkg, null, notificationId, userId);
2084 if (i < 0) {
2085 Log.d(TAG, "stripForegroundServiceFlag: Could not find notification with "
2086 + "pkg=" + pkg + " / id=" + notificationId + " / userId=" + userId);
2087 return;
2088 }
2089 NotificationRecord r = mNotificationList.get(i);
2090 StatusBarNotification sbn = r.sbn;
2091 // NoMan adds flags FLAG_NO_CLEAR and FLAG_ONGOING_EVENT when it sees
2092 // FLAG_FOREGROUND_SERVICE. Hence it's not enough to remove FLAG_FOREGROUND_SERVICE,
2093 // we have to revert to the flags we received initially *and* force remove
2094 // FLAG_FOREGROUND_SERVICE.
2095 sbn.getNotification().flags =
2096 (r.mOriginalFlags & ~Notification.FLAG_FOREGROUND_SERVICE);
2097 mRankingHelper.sort(mNotificationList);
2098 mListeners.notifyPostedLocked(sbn, sbn /* oldSbn */);
2099 }
2100 }
Adam Lesinski182f73f2013-12-05 16:48:06 -08002101 };
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002102
Christoph Studer8fd7f1e2014-04-11 17:35:05 -04002103 void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,
Scott Greenwald9b05c612013-06-25 23:44:05 -04002104 final int callingPid, final String tag, final int id, final Notification notification,
Adam Lesinski182f73f2013-12-05 16:48:06 -08002105 int[] idOut, int incomingUserId) {
Daniel Sandler0da673f2012-04-11 12:33:16 -04002106 if (DBG) {
Adam Lesinski182f73f2013-12-05 16:48:06 -08002107 Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id
2108 + " notification=" + notification);
Daniel Sandler0da673f2012-04-11 12:33:16 -04002109 }
John Spurlock7340fc82014-04-24 18:50:12 -04002110 checkCallerIsSystemOrSameApp(pkg);
2111 final boolean isSystemNotification = isUidSystem(callingUid) || ("android".equals(pkg));
Justin Koh38156c52014-06-04 13:57:49 -07002112 final boolean isNotificationFromListener = mListeners.isListenerPackage(pkg);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08002113
Scott Greenwald9b05c612013-06-25 23:44:05 -04002114 final int userId = ActivityManager.handleIncomingUser(callingPid,
2115 callingUid, incomingUserId, true, false, "enqueueNotification", pkg);
Jeff Sharkey65c4a2b2012-09-25 17:22:27 -07002116 final UserHandle user = new UserHandle(userId);
Dianne Hackborn41203752012-08-31 14:05:51 -07002117
Joe Onoratobd73d012010-06-04 11:44:54 -07002118 // Limit the number of notifications that any given package except the android
Justin Koh38156c52014-06-04 13:57:49 -07002119 // package or a registered listener can enqueue. Prevents DOS attacks and deals with leaks.
2120 if (!isSystemNotification && !isNotificationFromListener) {
Joe Onoratobd73d012010-06-04 11:44:54 -07002121 synchronized (mNotificationList) {
2122 int count = 0;
2123 final int N = mNotificationList.size();
2124 for (int i=0; i<N; i++) {
2125 final NotificationRecord r = mNotificationList.get(i);
Daniel Sandler4f91efd2013-04-25 16:38:41 -04002126 if (r.sbn.getPackageName().equals(pkg) && r.sbn.getUserId() == userId) {
Vladimir Marko2526f332013-09-11 11:13:55 +01002127 if (r.sbn.getId() == id && TextUtils.equals(r.sbn.getTag(), tag)) {
2128 break; // Allow updating existing notification
2129 }
Joe Onoratobd73d012010-06-04 11:44:54 -07002130 count++;
2131 if (count >= MAX_PACKAGE_NOTIFICATIONS) {
2132 Slog.e(TAG, "Package has already posted " + count
2133 + " notifications. Not showing more. package=" + pkg);
2134 return;
2135 }
2136 }
2137 }
2138 }
2139 }
2140
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002141 if (pkg == null || notification == null) {
2142 throw new IllegalArgumentException("null not allowed: pkg=" + pkg
2143 + " id=" + id + " notification=" + notification);
2144 }
Dan Sandlerd63f9322015-05-06 15:18:49 -04002145
Scott Greenwald9b05c612013-06-25 23:44:05 -04002146 mHandler.post(new Runnable() {
2147 @Override
2148 public void run() {
Daniel Sandler0da673f2012-04-11 12:33:16 -04002149
Scott Greenwald9b05c612013-06-25 23:44:05 -04002150 synchronized (mNotificationList) {
Christoph Studercd4adf82014-08-19 17:50:49 +02002151
Chris Wrenbdf33762015-12-04 15:50:51 -05002152 // Sanitize inputs
Christoph Studercd4adf82014-08-19 17:50:49 +02002153 notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN,
2154 Notification.PRIORITY_MAX);
Christoph Studercd4adf82014-08-19 17:50:49 +02002155
Chris Wrenbdf33762015-12-04 15:50:51 -05002156 // setup local book-keeping
Christoph Studercd4adf82014-08-19 17:50:49 +02002157 final StatusBarNotification n = new StatusBarNotification(
Chris Wrenbdf33762015-12-04 15:50:51 -05002158 pkg, opPkg, id, tag, callingUid, callingPid, 0, notification,
Christoph Studercd4adf82014-08-19 17:50:49 +02002159 user);
Chris Wrenbdf33762015-12-04 15:50:51 -05002160 NotificationRecord r = new NotificationRecord(getContext(), n);
Christoph Studercd4adf82014-08-19 17:50:49 +02002161 NotificationRecord old = mNotificationsByKey.get(n.getKey());
2162 if (old != null) {
2163 // Retain ranking information from previous record
2164 r.copyRankingInformation(old);
2165 }
Christoph Studer265c1052014-07-23 17:14:33 +02002166
2167 // Handle grouped notifications and bail out early if we
2168 // can to avoid extracting signals.
2169 handleGroupedNotificationLocked(r, old, callingUid, callingPid);
2170 boolean ignoreNotification =
Christoph Studer4a9849b2015-01-06 18:55:08 +01002171 removeUnusedGroupedNotificationLocked(r, old, callingUid, callingPid);
Christoph Studercd4adf82014-08-19 17:50:49 +02002172
Christoph Studer1382e672014-10-24 16:30:54 +02002173 // This conditional is a dirty hack to limit the logging done on
2174 // behalf of the download manager without affecting other apps.
2175 if (!pkg.equals("com.android.providers.downloads")
2176 || Log.isLoggable("DownloadManager", Log.VERBOSE)) {
Christoph Studer265c1052014-07-23 17:14:33 +02002177 int enqueueStatus = EVENTLOG_ENQUEUE_STATUS_NEW;
2178 if (ignoreNotification) {
2179 enqueueStatus = EVENTLOG_ENQUEUE_STATUS_IGNORED;
2180 } else if (old != null) {
2181 enqueueStatus = EVENTLOG_ENQUEUE_STATUS_UPDATE;
2182 }
Christoph Studer1382e672014-10-24 16:30:54 +02002183 EventLogTags.writeNotificationEnqueue(callingUid, callingPid,
2184 pkg, id, tag, userId, notification.toString(),
Christoph Studer265c1052014-07-23 17:14:33 +02002185 enqueueStatus);
Christoph Studer1382e672014-10-24 16:30:54 +02002186 }
Christoph Studer265c1052014-07-23 17:14:33 +02002187
2188 if (ignoreNotification) {
2189 return;
2190 }
2191
2192 mRankingHelper.extractSignals(r);
Julia Reynolds5d25ee72015-11-20 15:38:20 -05002193 savePolicyFile();
Christoph Studer265c1052014-07-23 17:14:33 +02002194
Christoph Studercd4adf82014-08-19 17:50:49 +02002195 // blocked apps
2196 if (ENABLE_BLOCKED_NOTIFICATIONS && !noteNotificationOp(pkg, callingUid)) {
2197 if (!isSystemNotification) {
Christoph Studercd4adf82014-08-19 17:50:49 +02002198 Slog.e(TAG, "Suppressing notification from package " + pkg
2199 + " by user request.");
Chris Wren5eab2b72015-06-16 13:56:22 -04002200 mUsageStats.registerBlocked(r);
Chris Wrenbdf33762015-12-04 15:50:51 -05002201 return;
Christoph Studercd4adf82014-08-19 17:50:49 +02002202 }
2203 }
2204
Christoph Studer71f18fd2014-05-20 17:02:04 +02002205 int index = indexOfNotificationLocked(n.getKey());
Scott Greenwald9b05c612013-06-25 23:44:05 -04002206 if (index < 0) {
2207 mNotificationList.add(r);
Christoph Studer546bec82014-03-14 12:17:12 +01002208 mUsageStats.registerPostedByApp(r);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002209 } else {
Christoph Studerd89188d2014-04-03 00:02:39 +02002210 old = mNotificationList.get(index);
2211 mNotificationList.set(index, r);
Christoph Studer061dee22014-05-09 12:28:55 +02002212 mUsageStats.registerUpdatedByApp(r, old);
Scott Greenwald9b05c612013-06-25 23:44:05 -04002213 // Make sure we don't lose the foreground service state.
Christoph Studer71f18fd2014-05-20 17:02:04 +02002214 notification.flags |=
Chris Wrena3446562014-06-03 18:11:47 -04002215 old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;
Chris Wrena3446562014-06-03 18:11:47 -04002216 r.isUpdate = true;
John Spurlocka4294292014-03-24 18:02:32 -04002217 }
Christoph Studer1cd5add2014-07-03 00:23:05 +02002218
John Spurlocka4294292014-03-24 18:02:32 -04002219 mNotificationsByKey.put(n.getKey(), r);
Scott Greenwald9b05c612013-06-25 23:44:05 -04002220
Christoph Studer1cd5add2014-07-03 00:23:05 +02002221 // Ensure if this is a foreground service that the proper additional
2222 // flags are set.
2223 if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
2224 notification.flags |= Notification.FLAG_ONGOING_EVENT
2225 | Notification.FLAG_NO_CLEAR;
2226 }
2227
Chris Wren52eba542014-06-02 15:40:32 -04002228 applyZenModeLocked(r);
Christoph Studercd4adf82014-08-19 17:50:49 +02002229 mRankingHelper.sort(mNotificationList);
Chris Wrenf9536642014-04-17 10:01:54 -04002230
Dan Sandlerd63f9322015-05-06 15:18:49 -04002231 if (notification.getSmallIcon() != null) {
Christoph Studercef37cf2014-07-25 14:18:17 +02002232 StatusBarNotification oldSbn = (old != null) ? old.sbn : null;
2233 mListeners.notifyPostedLocked(n, oldSbn);
Scott Greenwald9b05c612013-06-25 23:44:05 -04002234 } else {
Dan Sandlerd63f9322015-05-06 15:18:49 -04002235 Slog.e(TAG, "Not posting notification without small icon: " + notification);
Christoph Studer71f18fd2014-05-20 17:02:04 +02002236 if (old != null && !old.isCanceled) {
Chris Wrena3446562014-06-03 18:11:47 -04002237 mListeners.notifyRemovedLocked(n);
Scott Greenwald9b05c612013-06-25 23:44:05 -04002238 }
2239 // ATTENTION: in a future release we will bail out here
Adam Lesinski182f73f2013-12-05 16:48:06 -08002240 // so that we do not play sounds, show lights, etc. for invalid
2241 // notifications
Scott Greenwald9b05c612013-06-25 23:44:05 -04002242 Slog.e(TAG, "WARNING: In a future release this will crash the app: "
2243 + n.getPackageName());
2244 }
2245
Chris Wrena3446562014-06-03 18:11:47 -04002246 buzzBeepBlinkLocked(r);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002247 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002248 }
Scott Greenwald9b05c612013-06-25 23:44:05 -04002249 });
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002250
2251 idOut[0] = id;
2252 }
2253
Christoph Studer265c1052014-07-23 17:14:33 +02002254 /**
2255 * Ensures that grouped notification receive their special treatment.
2256 *
2257 * <p>Cancels group children if the new notification causes a group to lose
2258 * its summary.</p>
2259 *
2260 * <p>Updates mSummaryByGroupKey.</p>
2261 */
2262 private void handleGroupedNotificationLocked(NotificationRecord r, NotificationRecord old,
2263 int callingUid, int callingPid) {
2264 StatusBarNotification sbn = r.sbn;
2265 Notification n = sbn.getNotification();
2266 String group = sbn.getGroupKey();
2267 boolean isSummary = n.isGroupSummary();
2268
2269 Notification oldN = old != null ? old.sbn.getNotification() : null;
2270 String oldGroup = old != null ? old.sbn.getGroupKey() : null;
2271 boolean oldIsSummary = old != null && oldN.isGroupSummary();
2272
2273 if (oldIsSummary) {
2274 NotificationRecord removedSummary = mSummaryByGroupKey.remove(oldGroup);
2275 if (removedSummary != old) {
2276 String removedKey =
2277 removedSummary != null ? removedSummary.getKey() : "<null>";
2278 Slog.w(TAG, "Removed summary didn't match old notification: old=" + old.getKey() +
2279 ", removed=" + removedKey);
2280 }
2281 }
2282 if (isSummary) {
2283 mSummaryByGroupKey.put(group, r);
2284 }
2285
2286 // Clear out group children of the old notification if the update
2287 // causes the group summary to go away. This happens when the old
2288 // notification was a summary and the new one isn't, or when the old
2289 // notification was a summary and its group key changed.
2290 if (oldIsSummary && (!isSummary || !oldGroup.equals(group))) {
2291 cancelGroupChildrenLocked(old, callingUid, callingPid, null,
2292 REASON_GROUP_SUMMARY_CANCELED);
2293 }
2294 }
2295
2296 /**
2297 * Performs group notification optimizations if SysUI is the only active
2298 * notification listener and returns whether the given notification should
2299 * be ignored.
2300 *
2301 * <p>Returns true if the given notification is a child of a group with a
2302 * summary, which means that SysUI will never show it, and hence the new
Christoph Studer4a9849b2015-01-06 18:55:08 +01002303 * notification can be safely ignored. Also cancels any previous instance
2304 * of the ignored notification.</p>
Christoph Studer265c1052014-07-23 17:14:33 +02002305 *
2306 * <p>For summaries, cancels all children of that group, as SysUI will
2307 * never show them anymore.</p>
2308 *
2309 * @return true if the given notification can be ignored as an optimization
2310 */
2311 private boolean removeUnusedGroupedNotificationLocked(NotificationRecord r,
Christoph Studer4a9849b2015-01-06 18:55:08 +01002312 NotificationRecord old, int callingUid, int callingPid) {
Selim Cinekb5605e52015-02-20 18:21:41 +01002313 if (!ENABLE_CHILD_NOTIFICATIONS) {
2314 // No optimizations are possible if listeners want groups.
2315 if (mListeners.notificationGroupsDesired()) {
2316 return false;
Christoph Studer265c1052014-07-23 17:14:33 +02002317 }
Selim Cinekb5605e52015-02-20 18:21:41 +01002318
2319 StatusBarNotification sbn = r.sbn;
2320 String group = sbn.getGroupKey();
2321 boolean isSummary = sbn.getNotification().isGroupSummary();
2322 boolean isChild = sbn.getNotification().isGroupChild();
2323
2324 NotificationRecord summary = mSummaryByGroupKey.get(group);
2325 if (isChild && summary != null) {
2326 // Child with an active summary -> ignore
Christoph Studer4a9849b2015-01-06 18:55:08 +01002327 if (DBG) {
Selim Cinekb5605e52015-02-20 18:21:41 +01002328 Slog.d(TAG, "Ignoring group child " + sbn.getKey() + " due to existing summary "
2329 + summary.getKey());
Christoph Studer4a9849b2015-01-06 18:55:08 +01002330 }
Selim Cinekb5605e52015-02-20 18:21:41 +01002331 // Make sure we don't leave an old version of the notification around.
2332 if (old != null) {
2333 if (DBG) {
2334 Slog.d(TAG, "Canceling old version of ignored group child " + sbn.getKey());
2335 }
2336 cancelNotificationLocked(old, false, REASON_GROUP_OPTIMIZATION);
2337 }
2338 return true;
2339 } else if (isSummary) {
2340 // Summary -> cancel children
2341 cancelGroupChildrenLocked(r, callingUid, callingPid, null,
2342 REASON_GROUP_OPTIMIZATION);
Christoph Studer4a9849b2015-01-06 18:55:08 +01002343 }
Christoph Studer265c1052014-07-23 17:14:33 +02002344 }
2345 return false;
2346 }
2347
Chris Wrena3446562014-06-03 18:11:47 -04002348 private void buzzBeepBlinkLocked(NotificationRecord record) {
Chris Wren82ba59d2015-06-05 11:23:44 -04002349 boolean buzz = false;
2350 boolean beep = false;
2351 boolean blink = false;
2352
Chris Wrena3446562014-06-03 18:11:47 -04002353 final Notification notification = record.sbn.getNotification();
2354
2355 // Should this notification make noise, vibe, or use the LED?
Chris Wrenbdf33762015-12-04 15:50:51 -05002356 final boolean aboveThreshold = record.getImportance() >= IMPORTANCE_HIGH;
Chris Wrence00a232014-11-21 16:25:19 -05002357 final boolean canInterrupt = aboveThreshold && !record.isIntercepted();
Chris Wrena3446562014-06-03 18:11:47 -04002358 if (DBG || record.isIntercepted())
2359 Slog.v(TAG,
2360 "pkg=" + record.sbn.getPackageName() + " canInterrupt=" + canInterrupt +
2361 " intercept=" + record.isIntercepted()
2362 );
2363
2364 final int currentUser;
2365 final long token = Binder.clearCallingIdentity();
2366 try {
2367 currentUser = ActivityManager.getCurrentUser();
2368 } finally {
2369 Binder.restoreCallingIdentity(token);
2370 }
2371
2372 // If we're not supposed to beep, vibrate, etc. then don't.
John Spurlock32fe4c62014-10-02 12:16:02 -04002373 final String disableEffects = disableNotificationEffects(record);
2374 if (disableEffects != null) {
2375 ZenLog.traceDisableEffects(record, disableEffects);
2376 }
2377 if (disableEffects == null
Chris Wrena3446562014-06-03 18:11:47 -04002378 && (!(record.isUpdate
2379 && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 ))
2380 && (record.getUserId() == UserHandle.USER_ALL ||
2381 record.getUserId() == currentUser ||
2382 mUserProfiles.isCurrentProfile(record.getUserId()))
2383 && canInterrupt
2384 && mSystemReady
2385 && mAudioManager != null) {
2386 if (DBG) Slog.v(TAG, "Interrupting!");
2387
2388 sendAccessibilityEvent(notification, record.sbn.getPackageName());
2389
2390 // sound
2391
2392 // should we use the default notification sound? (indicated either by
2393 // DEFAULT_SOUND or because notification.sound is pointing at
2394 // Settings.System.NOTIFICATION_SOUND)
2395 final boolean useDefaultSound =
2396 (notification.defaults & Notification.DEFAULT_SOUND) != 0 ||
2397 Settings.System.DEFAULT_NOTIFICATION_URI
2398 .equals(notification.sound);
2399
2400 Uri soundUri = null;
2401 boolean hasValidSound = false;
2402
2403 if (useDefaultSound) {
2404 soundUri = Settings.System.DEFAULT_NOTIFICATION_URI;
2405
2406 // check to see if the default notification sound is silent
2407 ContentResolver resolver = getContext().getContentResolver();
2408 hasValidSound = Settings.System.getString(resolver,
2409 Settings.System.NOTIFICATION_SOUND) != null;
2410 } else if (notification.sound != null) {
2411 soundUri = notification.sound;
2412 hasValidSound = (soundUri != null);
2413 }
2414
2415 if (hasValidSound) {
2416 boolean looping =
2417 (notification.flags & Notification.FLAG_INSISTENT) != 0;
Marco Nelissen1c066302014-11-18 10:48:12 -08002418 AudioAttributes audioAttributes = audioAttributesForNotification(notification);
Chris Wren6054e612014-11-25 17:16:46 -05002419 mSoundNotificationKey = record.getKey();
Chris Wrena3446562014-06-03 18:11:47 -04002420 // do not play notifications if stream volume is 0 (typically because
2421 // ringer mode is silent) or if there is a user of exclusive audio focus
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -07002422 if ((mAudioManager.getStreamVolume(
2423 AudioAttributes.toLegacyStreamType(audioAttributes)) != 0)
2424 && !mAudioManager.isAudioFocusExclusive()) {
Chris Wrena3446562014-06-03 18:11:47 -04002425 final long identity = Binder.clearCallingIdentity();
2426 try {
2427 final IRingtonePlayer player =
2428 mAudioManager.getRingtonePlayer();
2429 if (player != null) {
2430 if (DBG) Slog.v(TAG, "Playing sound " + soundUri
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -07002431 + " with attributes " + audioAttributes);
Chris Wrena3446562014-06-03 18:11:47 -04002432 player.playAsync(soundUri, record.sbn.getUser(), looping,
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -07002433 audioAttributes);
Chris Wren82ba59d2015-06-05 11:23:44 -04002434 beep = true;
Chris Wrena3446562014-06-03 18:11:47 -04002435 }
2436 } catch (RemoteException e) {
2437 } finally {
2438 Binder.restoreCallingIdentity(identity);
2439 }
2440 }
2441 }
2442
2443 // vibrate
2444 // Does the notification want to specify its own vibration?
2445 final boolean hasCustomVibrate = notification.vibrate != null;
2446
2447 // new in 4.2: if there was supposed to be a sound and we're in vibrate
2448 // mode, and no other vibration is specified, we fall back to vibration
2449 final boolean convertSoundToVibration =
2450 !hasCustomVibrate
2451 && hasValidSound
John Spurlock661f2cf2014-11-17 10:29:10 -05002452 && (mAudioManager.getRingerModeInternal()
Chris Wrena3446562014-06-03 18:11:47 -04002453 == AudioManager.RINGER_MODE_VIBRATE);
2454
2455 // The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback.
2456 final boolean useDefaultVibrate =
2457 (notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
2458
2459 if ((useDefaultVibrate || convertSoundToVibration || hasCustomVibrate)
John Spurlock661f2cf2014-11-17 10:29:10 -05002460 && !(mAudioManager.getRingerModeInternal()
Chris Wrena3446562014-06-03 18:11:47 -04002461 == AudioManager.RINGER_MODE_SILENT)) {
Chris Wren6054e612014-11-25 17:16:46 -05002462 mVibrateNotificationKey = record.getKey();
Chris Wrena3446562014-06-03 18:11:47 -04002463
2464 if (useDefaultVibrate || convertSoundToVibration) {
2465 // Escalate privileges so we can use the vibrator even if the
2466 // notifying app does not have the VIBRATE permission.
2467 long identity = Binder.clearCallingIdentity();
2468 try {
2469 mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
2470 useDefaultVibrate ? mDefaultVibrationPattern
2471 : mFallbackVibrationPattern,
2472 ((notification.flags & Notification.FLAG_INSISTENT) != 0)
John Spurlock7b414672014-07-18 13:02:39 -04002473 ? 0: -1, audioAttributesForNotification(notification));
Chris Wren82ba59d2015-06-05 11:23:44 -04002474 buzz = true;
Chris Wrena3446562014-06-03 18:11:47 -04002475 } finally {
2476 Binder.restoreCallingIdentity(identity);
2477 }
2478 } else if (notification.vibrate.length > 1) {
2479 // If you want your own vibration pattern, you need the VIBRATE
2480 // permission
2481 mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
2482 notification.vibrate,
2483 ((notification.flags & Notification.FLAG_INSISTENT) != 0)
John Spurlock7b414672014-07-18 13:02:39 -04002484 ? 0: -1, audioAttributesForNotification(notification));
Chris Wren82ba59d2015-06-05 11:23:44 -04002485 buzz = true;
Chris Wrena3446562014-06-03 18:11:47 -04002486 }
2487 }
2488 }
2489
2490 // light
2491 // release the light
2492 boolean wasShowLights = mLights.remove(record.getKey());
Julia Reynoldsf612869ae2015-11-05 16:48:55 -05002493 if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0 && aboveThreshold
2494 && ((record.getSuppressedVisualEffects()
2495 & NotificationListenerService.SUPPRESSED_EFFECT_LIGHTS) == 0)) {
Chris Wrena3446562014-06-03 18:11:47 -04002496 mLights.add(record.getKey());
2497 updateLightsLocked();
Chris Wren5116a822014-06-04 15:59:50 -04002498 if (mUseAttentionLight) {
2499 mAttentionLight.pulse();
2500 }
Chris Wren82ba59d2015-06-05 11:23:44 -04002501 blink = true;
Chris Wrena3446562014-06-03 18:11:47 -04002502 } else if (wasShowLights) {
2503 updateLightsLocked();
2504 }
Chris Wren82ba59d2015-06-05 11:23:44 -04002505 if (buzz || beep || blink) {
2506 EventLogTags.writeNotificationAlert(record.getKey(),
2507 buzz ? 1 : 0, beep ? 1 : 0, blink ? 1 : 0);
John Spurlockcad57682014-07-26 17:09:56 -04002508 mHandler.post(mBuzzBeepBlinked);
2509 }
Chris Wrena3446562014-06-03 18:11:47 -04002510 }
2511
John Spurlock7b414672014-07-18 13:02:39 -04002512 private static AudioAttributes audioAttributesForNotification(Notification n) {
Marco Nelissen1c066302014-11-18 10:48:12 -08002513 if (n.audioAttributes != null
2514 && !Notification.AUDIO_ATTRIBUTES_DEFAULT.equals(n.audioAttributes)) {
2515 // the audio attributes are set and different from the default, use them
John Spurlockbfa5dc42014-07-28 23:30:45 -04002516 return n.audioAttributes;
Jean-Michel Triviceb79bc2014-09-05 11:09:14 -07002517 } else if (n.audioStreamType >= 0 && n.audioStreamType < AudioSystem.getNumStreamTypes()) {
2518 // the stream type is valid, use it
2519 return new AudioAttributes.Builder()
2520 .setInternalLegacyStreamType(n.audioStreamType)
2521 .build();
2522 } else if (n.audioStreamType == AudioSystem.STREAM_DEFAULT) {
2523 return Notification.AUDIO_ATTRIBUTES_DEFAULT;
2524 } else {
2525 Log.w(TAG, String.format("Invalid stream type: %d", n.audioStreamType));
2526 return Notification.AUDIO_ATTRIBUTES_DEFAULT;
John Spurlockbfa5dc42014-07-28 23:30:45 -04002527 }
John Spurlock7b414672014-07-18 13:02:39 -04002528 }
2529
Adam Lesinski182f73f2013-12-05 16:48:06 -08002530 void showNextToastLocked() {
2531 ToastRecord record = mToastQueue.get(0);
2532 while (record != null) {
2533 if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback);
2534 try {
2535 record.callback.show();
2536 scheduleTimeoutLocked(record);
2537 return;
2538 } catch (RemoteException e) {
2539 Slog.w(TAG, "Object died trying to show notification " + record.callback
2540 + " in package " + record.pkg);
2541 // remove it from the list and let the process die
2542 int index = mToastQueue.indexOf(record);
2543 if (index >= 0) {
2544 mToastQueue.remove(index);
2545 }
2546 keepProcessAliveLocked(record.pid);
2547 if (mToastQueue.size() > 0) {
2548 record = mToastQueue.get(0);
2549 } else {
2550 record = null;
2551 }
2552 }
2553 }
2554 }
2555
2556 void cancelToastLocked(int index) {
2557 ToastRecord record = mToastQueue.get(index);
2558 try {
2559 record.callback.hide();
2560 } catch (RemoteException e) {
2561 Slog.w(TAG, "Object died trying to hide notification " + record.callback
2562 + " in package " + record.pkg);
2563 // don't worry about this, we're about to remove it from
2564 // the list anyway
2565 }
2566 mToastQueue.remove(index);
2567 keepProcessAliveLocked(record.pid);
2568 if (mToastQueue.size() > 0) {
2569 // Show the next one. If the callback fails, this will remove
2570 // it from the list, so don't assume that the list hasn't changed
2571 // after this point.
2572 showNextToastLocked();
2573 }
2574 }
2575
2576 private void scheduleTimeoutLocked(ToastRecord r)
2577 {
2578 mHandler.removeCallbacksAndMessages(r);
2579 Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r);
2580 long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY;
2581 mHandler.sendMessageDelayed(m, delay);
2582 }
2583
2584 private void handleTimeout(ToastRecord record)
2585 {
2586 if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback);
2587 synchronized (mToastQueue) {
2588 int index = indexOfToastLocked(record.pkg, record.callback);
2589 if (index >= 0) {
2590 cancelToastLocked(index);
2591 }
2592 }
2593 }
2594
2595 // lock on mToastQueue
2596 int indexOfToastLocked(String pkg, ITransientNotification callback)
2597 {
2598 IBinder cbak = callback.asBinder();
2599 ArrayList<ToastRecord> list = mToastQueue;
2600 int len = list.size();
2601 for (int i=0; i<len; i++) {
2602 ToastRecord r = list.get(i);
2603 if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) {
2604 return i;
2605 }
2606 }
2607 return -1;
2608 }
2609
2610 // lock on mToastQueue
2611 void keepProcessAliveLocked(int pid)
2612 {
2613 int toastCount = 0; // toasts from this pid
2614 ArrayList<ToastRecord> list = mToastQueue;
2615 int N = list.size();
2616 for (int i=0; i<N; i++) {
2617 ToastRecord r = list.get(i);
2618 if (r.pid == pid) {
2619 toastCount++;
2620 }
2621 }
2622 try {
2623 mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0);
2624 } catch (RemoteException e) {
2625 // Shouldn't happen.
2626 }
2627 }
2628
Chris Wrenf9536642014-04-17 10:01:54 -04002629 private void handleRankingReconsideration(Message message) {
Chris Wren470c1ac2014-05-21 15:28:10 -04002630 if (!(message.obj instanceof RankingReconsideration)) return;
2631 RankingReconsideration recon = (RankingReconsideration) message.obj;
2632 recon.run();
Chris Wren333a61c2014-05-28 16:40:57 -04002633 boolean changed;
Chris Wren470c1ac2014-05-21 15:28:10 -04002634 synchronized (mNotificationList) {
2635 final NotificationRecord record = mNotificationsByKey.get(recon.getKey());
2636 if (record == null) {
2637 return;
Chris Wrenf9536642014-04-17 10:01:54 -04002638 }
Chris Wren333a61c2014-05-28 16:40:57 -04002639 int indexBefore = findNotificationRecordIndexLocked(record);
2640 boolean interceptBefore = record.isIntercepted();
Chris Wren3ad4e3a2014-09-02 17:23:51 -04002641 int visibilityBefore = record.getPackageVisibilityOverride();
Chris Wren470c1ac2014-05-21 15:28:10 -04002642 recon.applyChangesLocked(record);
Chris Wren333a61c2014-05-28 16:40:57 -04002643 applyZenModeLocked(record);
Chris Wren54bbef42014-07-09 18:37:56 -04002644 mRankingHelper.sort(mNotificationList);
Chris Wren333a61c2014-05-28 16:40:57 -04002645 int indexAfter = findNotificationRecordIndexLocked(record);
2646 boolean interceptAfter = record.isIntercepted();
Chris Wren3ad4e3a2014-09-02 17:23:51 -04002647 int visibilityAfter = record.getPackageVisibilityOverride();
2648 changed = indexBefore != indexAfter || interceptBefore != interceptAfter
2649 || visibilityBefore != visibilityAfter;
Chris Wrena3446562014-06-03 18:11:47 -04002650 if (interceptBefore && !interceptAfter) {
2651 buzzBeepBlinkLocked(record);
2652 }
Chris Wrenf9536642014-04-17 10:01:54 -04002653 }
Chris Wren333a61c2014-05-28 16:40:57 -04002654 if (changed) {
Chris Wren470c1ac2014-05-21 15:28:10 -04002655 scheduleSendRankingUpdate();
2656 }
2657 }
2658
Chris Wren54bbef42014-07-09 18:37:56 -04002659 private void handleRankingConfigChange() {
2660 synchronized (mNotificationList) {
2661 final int N = mNotificationList.size();
2662 ArrayList<String> orderBefore = new ArrayList<String>(N);
Chris Wren3ad4e3a2014-09-02 17:23:51 -04002663 int[] visibilities = new int[N];
Chris Wren54bbef42014-07-09 18:37:56 -04002664 for (int i = 0; i < N; i++) {
2665 final NotificationRecord r = mNotificationList.get(i);
2666 orderBefore.add(r.getKey());
Chris Wren3ad4e3a2014-09-02 17:23:51 -04002667 visibilities[i] = r.getPackageVisibilityOverride();
Chris Wren54bbef42014-07-09 18:37:56 -04002668 mRankingHelper.extractSignals(r);
2669 }
Chris Wren54bbef42014-07-09 18:37:56 -04002670 for (int i = 0; i < N; i++) {
Chris Wren3ad4e3a2014-09-02 17:23:51 -04002671 mRankingHelper.sort(mNotificationList);
2672 final NotificationRecord r = mNotificationList.get(i);
2673 if (!orderBefore.get(i).equals(r.getKey())
2674 || visibilities[i] != r.getPackageVisibilityOverride()) {
Chris Wren54bbef42014-07-09 18:37:56 -04002675 scheduleSendRankingUpdate();
2676 return;
2677 }
2678 }
2679 }
2680 }
2681
Christoph Studerd5092bc2014-07-03 17:47:58 +02002682 // let zen mode evaluate this record
Chris Wren333a61c2014-05-28 16:40:57 -04002683 private void applyZenModeLocked(NotificationRecord record) {
Christoph Studerd5092bc2014-07-03 17:47:58 +02002684 record.setIntercepted(mZenModeHelper.shouldIntercept(record));
Julia Reynoldsf612869ae2015-11-05 16:48:55 -05002685 if (record.isIntercepted()) {
2686 int suppressed = (mZenModeHelper.shouldSuppressLight() ? SUPPRESSED_EFFECT_LIGHTS : 0)
2687 | (mZenModeHelper.shouldSuppressPeek() ? SUPPRESSED_EFFECT_PEEK : 0);
2688 record.setSuppressedVisualEffects(suppressed);
2689 }
Chris Wren333a61c2014-05-28 16:40:57 -04002690 }
2691
Chris Wren470c1ac2014-05-21 15:28:10 -04002692 // lock on mNotificationList
2693 private int findNotificationRecordIndexLocked(NotificationRecord target) {
Chris Wren54bbef42014-07-09 18:37:56 -04002694 return mRankingHelper.indexOf(mNotificationList, target);
Chris Wrenf9536642014-04-17 10:01:54 -04002695 }
2696
2697 private void scheduleSendRankingUpdate() {
2698 mHandler.removeMessages(MESSAGE_SEND_RANKING_UPDATE);
2699 Message m = Message.obtain(mHandler, MESSAGE_SEND_RANKING_UPDATE);
2700 mHandler.sendMessage(m);
2701 }
2702
2703 private void handleSendRankingUpdate() {
2704 synchronized (mNotificationList) {
Chris Wren333a61c2014-05-28 16:40:57 -04002705 mListeners.notifyRankingUpdateLocked();
Chris Wrenf9536642014-04-17 10:01:54 -04002706 }
2707 }
2708
John Spurlockd8afe3c2014-08-01 14:04:07 -04002709 private void scheduleListenerHintsChanged(int state) {
2710 mHandler.removeMessages(MESSAGE_LISTENER_HINTS_CHANGED);
2711 mHandler.obtainMessage(MESSAGE_LISTENER_HINTS_CHANGED, state, 0).sendToTarget();
John Spurlock1fa865f2014-07-21 14:56:39 -04002712 }
2713
Christoph Studer85a384b2014-08-27 20:16:15 +02002714 private void scheduleInterruptionFilterChanged(int listenerInterruptionFilter) {
2715 mHandler.removeMessages(MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED);
2716 mHandler.obtainMessage(
2717 MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED,
2718 listenerInterruptionFilter,
2719 0).sendToTarget();
2720 }
2721
John Spurlockd8afe3c2014-08-01 14:04:07 -04002722 private void handleListenerHintsChanged(int hints) {
John Spurlock1fa865f2014-07-21 14:56:39 -04002723 synchronized (mNotificationList) {
John Spurlockd8afe3c2014-08-01 14:04:07 -04002724 mListeners.notifyListenerHintsChangedLocked(hints);
John Spurlock1fa865f2014-07-21 14:56:39 -04002725 }
2726 }
2727
Christoph Studer85a384b2014-08-27 20:16:15 +02002728 private void handleListenerInterruptionFilterChanged(int interruptionFilter) {
2729 synchronized (mNotificationList) {
2730 mListeners.notifyInterruptionFilterChanged(interruptionFilter);
2731 }
2732 }
2733
Adam Lesinski182f73f2013-12-05 16:48:06 -08002734 private final class WorkerHandler extends Handler
2735 {
2736 @Override
2737 public void handleMessage(Message msg)
2738 {
2739 switch (msg.what)
2740 {
2741 case MESSAGE_TIMEOUT:
2742 handleTimeout((ToastRecord)msg.obj);
2743 break;
John Spurlock056c5192014-04-20 21:52:01 -04002744 case MESSAGE_SAVE_POLICY_FILE:
2745 handleSavePolicyFile();
2746 break;
Chris Wrenf9536642014-04-17 10:01:54 -04002747 case MESSAGE_SEND_RANKING_UPDATE:
2748 handleSendRankingUpdate();
2749 break;
John Spurlockd8afe3c2014-08-01 14:04:07 -04002750 case MESSAGE_LISTENER_HINTS_CHANGED:
2751 handleListenerHintsChanged(msg.arg1);
John Spurlock1fa865f2014-07-21 14:56:39 -04002752 break;
Christoph Studer85a384b2014-08-27 20:16:15 +02002753 case MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED:
2754 handleListenerInterruptionFilterChanged(msg.arg1);
2755 break;
Chris Wrenf9536642014-04-17 10:01:54 -04002756 }
2757 }
2758
2759 }
2760
2761 private final class RankingWorkerHandler extends Handler
2762 {
2763 public RankingWorkerHandler(Looper looper) {
2764 super(looper);
2765 }
2766
2767 @Override
2768 public void handleMessage(Message msg) {
2769 switch (msg.what) {
2770 case MESSAGE_RECONSIDER_RANKING:
2771 handleRankingReconsideration(msg);
2772 break;
Chris Wren54bbef42014-07-09 18:37:56 -04002773 case MESSAGE_RANKING_CONFIG_CHANGE:
2774 handleRankingConfigChange();
2775 break;
Adam Lesinski182f73f2013-12-05 16:48:06 -08002776 }
2777 }
2778 }
2779
Adam Lesinski182f73f2013-12-05 16:48:06 -08002780 // Notifications
2781 // ============================================================================
2782 static int clamp(int x, int low, int high) {
2783 return (x < low) ? low : ((x > high) ? high : x);
2784 }
2785
2786 void sendAccessibilityEvent(Notification notification, CharSequence packageName) {
2787 AccessibilityManager manager = AccessibilityManager.getInstance(getContext());
svetoslavganov75986cf2009-05-14 22:28:01 -07002788 if (!manager.isEnabled()) {
2789 return;
2790 }
2791
2792 AccessibilityEvent event =
2793 AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
2794 event.setPackageName(packageName);
2795 event.setClassName(Notification.class.getName());
2796 event.setParcelableData(notification);
2797 CharSequence tickerText = notification.tickerText;
2798 if (!TextUtils.isEmpty(tickerText)) {
2799 event.getText().add(tickerText);
2800 }
2801
2802 manager.sendAccessibilityEvent(event);
2803 }
2804
Christoph Studer546bec82014-03-14 12:17:12 +01002805 private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason) {
Joe Onorato46439ce2010-11-19 13:56:21 -08002806 // tell the app
2807 if (sendDelete) {
Daniel Sandlerfde19b12013-01-17 00:21:05 -05002808 if (r.getNotification().deleteIntent != null) {
Joe Onorato46439ce2010-11-19 13:56:21 -08002809 try {
Daniel Sandlerfde19b12013-01-17 00:21:05 -05002810 r.getNotification().deleteIntent.send();
Joe Onorato46439ce2010-11-19 13:56:21 -08002811 } catch (PendingIntent.CanceledException ex) {
2812 // do nothing - there's no relevant way to recover, and
2813 // no reason to let this propagate
Daniel Sandler4f91efd2013-04-25 16:38:41 -04002814 Slog.w(TAG, "canceled PendingIntent for " + r.sbn.getPackageName(), ex);
Joe Onorato46439ce2010-11-19 13:56:21 -08002815 }
2816 }
2817 }
2818
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002819 // status bar
Dan Sandlerd63f9322015-05-06 15:18:49 -04002820 if (r.getNotification().getSmallIcon() != null) {
Christoph Studer71f18fd2014-05-20 17:02:04 +02002821 r.isCanceled = true;
Chris Wren333a61c2014-05-28 16:40:57 -04002822 mListeners.notifyRemovedLocked(r.sbn);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002823 }
2824
Chris Wren6054e612014-11-25 17:16:46 -05002825 final String canceledKey = r.getKey();
2826
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002827 // sound
Chris Wren6054e612014-11-25 17:16:46 -05002828 if (canceledKey.equals(mSoundNotificationKey)) {
2829 mSoundNotificationKey = null;
Jeff Sharkey098d5802012-04-26 17:30:34 -07002830 final long identity = Binder.clearCallingIdentity();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002831 try {
Adam Lesinski182f73f2013-12-05 16:48:06 -08002832 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
Jeff Sharkey098d5802012-04-26 17:30:34 -07002833 if (player != null) {
2834 player.stopAsync();
2835 }
2836 } catch (RemoteException e) {
2837 } finally {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002838 Binder.restoreCallingIdentity(identity);
2839 }
2840 }
2841
2842 // vibrate
Chris Wren6054e612014-11-25 17:16:46 -05002843 if (canceledKey.equals(mVibrateNotificationKey)) {
2844 mVibrateNotificationKey = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002845 long identity = Binder.clearCallingIdentity();
2846 try {
2847 mVibrator.cancel();
2848 }
2849 finally {
2850 Binder.restoreCallingIdentity(identity);
2851 }
2852 }
2853
2854 // light
Chris Wren6054e612014-11-25 17:16:46 -05002855 mLights.remove(canceledKey);
Daniel Sandler23d7c702013-03-07 16:32:06 -05002856
Christoph Studer546bec82014-03-14 12:17:12 +01002857 // Record usage stats
2858 switch (reason) {
2859 case REASON_DELEGATE_CANCEL:
2860 case REASON_DELEGATE_CANCEL_ALL:
2861 case REASON_LISTENER_CANCEL:
2862 case REASON_LISTENER_CANCEL_ALL:
2863 mUsageStats.registerDismissedByUser(r);
2864 break;
Chris Wren9fa689f2015-11-20 16:44:53 -05002865 case REASON_APP_CANCEL:
2866 case REASON_APP_CANCEL_ALL:
Christoph Studer546bec82014-03-14 12:17:12 +01002867 mUsageStats.registerRemovedByApp(r);
2868 break;
Christoph Studer546bec82014-03-14 12:17:12 +01002869 }
2870
Christoph Studercef37cf2014-07-25 14:18:17 +02002871 mNotificationsByKey.remove(r.sbn.getKey());
Christoph Studer265c1052014-07-23 17:14:33 +02002872 String groupKey = r.getGroupKey();
2873 NotificationRecord groupSummary = mSummaryByGroupKey.get(groupKey);
2874 if (groupSummary != null && groupSummary.getKey().equals(r.getKey())) {
2875 mSummaryByGroupKey.remove(groupKey);
2876 }
Christoph Studercef37cf2014-07-25 14:18:17 +02002877
Daniel Sandler23d7c702013-03-07 16:32:06 -05002878 // Save it for users of getHistoricalNotifications()
2879 mArchive.record(r.sbn);
Christoph Studer81e5b5f2014-10-22 17:19:56 +02002880
Chris Wren6650e572015-05-15 17:19:25 -04002881 final long now = System.currentTimeMillis();
Chris Wrene6ddb8a2015-05-27 15:21:00 -04002882 EventLogTags.writeNotificationCanceled(canceledKey, reason,
2883 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002884 }
2885
2886 /**
Dianne Hackbornd8a43f62009-08-17 23:33:56 -07002887 * Cancels a notification ONLY if it has all of the {@code mustHaveFlags}
Doug Zongkerab5c49c2009-12-04 10:31:43 -08002888 * and none of the {@code mustNotHaveFlags}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002889 */
John Spurlocke6a7d932014-03-13 12:29:00 -04002890 void cancelNotification(final int callingUid, final int callingPid,
2891 final String pkg, final String tag, final int id,
Svetoslav Ganov835835e2013-08-04 20:17:52 -07002892 final int mustHaveFlags, final int mustNotHaveFlags, final boolean sendDelete,
John Spurlock7340fc82014-04-24 18:50:12 -04002893 final int userId, final int reason, final ManagedServiceInfo listener) {
Svetoslav Ganov835835e2013-08-04 20:17:52 -07002894 // In enqueueNotificationInternal notifications are added by scheduling the
2895 // work on the worker handler. Hence, we also schedule the cancel on this
2896 // handler to avoid a scenario where an add notification call followed by a
2897 // remove notification call ends up in not removing the notification.
2898 mHandler.post(new Runnable() {
2899 @Override
2900 public void run() {
Christoph Studere4ef156b2014-07-04 18:41:57 +02002901 String listenerName = listener == null ? null : listener.component.toShortString();
Chris Wrenbddb5bc2015-03-04 08:47:46 -08002902 if (DBG) EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, id, tag,
2903 userId, mustHaveFlags, mustNotHaveFlags, reason, listenerName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002904
Svetoslav Ganov835835e2013-08-04 20:17:52 -07002905 synchronized (mNotificationList) {
2906 int index = indexOfNotificationLocked(pkg, tag, id, userId);
2907 if (index >= 0) {
2908 NotificationRecord r = mNotificationList.get(index);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08002909
Christoph Studer546bec82014-03-14 12:17:12 +01002910 // Ideally we'd do this in the caller of this method. However, that would
2911 // require the caller to also find the notification.
2912 if (reason == REASON_DELEGATE_CLICK) {
2913 mUsageStats.registerClickedByUser(r);
2914 }
2915
Svetoslav Ganov835835e2013-08-04 20:17:52 -07002916 if ((r.getNotification().flags & mustHaveFlags) != mustHaveFlags) {
2917 return;
2918 }
2919 if ((r.getNotification().flags & mustNotHaveFlags) != 0) {
2920 return;
2921 }
2922
2923 mNotificationList.remove(index);
2924
Christoph Studer546bec82014-03-14 12:17:12 +01002925 cancelNotificationLocked(r, sendDelete, reason);
Christoph Studer265c1052014-07-23 17:14:33 +02002926 cancelGroupChildrenLocked(r, callingUid, callingPid, listenerName,
2927 REASON_GROUP_SUMMARY_CANCELED);
Svetoslav Ganov835835e2013-08-04 20:17:52 -07002928 updateLightsLocked();
2929 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002930 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002931 }
Svetoslav Ganov835835e2013-08-04 20:17:52 -07002932 });
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002933 }
2934
2935 /**
Daniel Sandler321e9c52012-10-12 10:59:26 -07002936 * Determine whether the userId applies to the notification in question, either because
2937 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard).
2938 */
2939 private boolean notificationMatchesUserId(NotificationRecord r, int userId) {
2940 return
2941 // looking for USER_ALL notifications? match everything
2942 userId == UserHandle.USER_ALL
2943 // a notification sent to USER_ALL matches any query
Daniel Sandlerfde19b12013-01-17 00:21:05 -05002944 || r.getUserId() == UserHandle.USER_ALL
Daniel Sandler321e9c52012-10-12 10:59:26 -07002945 // an exact user match
Daniel Sandlerfde19b12013-01-17 00:21:05 -05002946 || r.getUserId() == userId;
Daniel Sandler321e9c52012-10-12 10:59:26 -07002947 }
2948
2949 /**
Kenny Guy3a7c4a52014-03-03 18:24:03 +00002950 * Determine whether the userId applies to the notification in question, either because
2951 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard) or
Kenny Guy2a764942014-04-02 13:29:20 +01002952 * because it matches one of the users profiles.
Kenny Guy3a7c4a52014-03-03 18:24:03 +00002953 */
Kenny Guy2a764942014-04-02 13:29:20 +01002954 private boolean notificationMatchesCurrentProfiles(NotificationRecord r, int userId) {
Kenny Guya263e4e2014-03-03 18:24:03 +00002955 return notificationMatchesUserId(r, userId)
John Spurlockb408e8e2014-04-23 21:12:45 -04002956 || mUserProfiles.isCurrentProfile(r.getUserId());
Kenny Guy3a7c4a52014-03-03 18:24:03 +00002957 }
2958
2959 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002960 * Cancels all notifications from a given package that have all of the
2961 * {@code mustHaveFlags}.
2962 */
John Spurlocke6a7d932014-03-13 12:29:00 -04002963 boolean cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, int mustHaveFlags,
2964 int mustNotHaveFlags, boolean doit, int userId, int reason,
John Spurlock7340fc82014-04-24 18:50:12 -04002965 ManagedServiceInfo listener) {
Christoph Studere4ef156b2014-07-04 18:41:57 +02002966 String listenerName = listener == null ? null : listener.component.toShortString();
John Spurlocke6a7d932014-03-13 12:29:00 -04002967 EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
2968 pkg, userId, mustHaveFlags, mustNotHaveFlags, reason,
Christoph Studere4ef156b2014-07-04 18:41:57 +02002969 listenerName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002970
2971 synchronized (mNotificationList) {
2972 final int N = mNotificationList.size();
Christoph Studere4ef156b2014-07-04 18:41:57 +02002973 ArrayList<NotificationRecord> canceledNotifications = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002974 for (int i = N-1; i >= 0; --i) {
2975 NotificationRecord r = mNotificationList.get(i);
Daniel Sandler321e9c52012-10-12 10:59:26 -07002976 if (!notificationMatchesUserId(r, userId)) {
Dianne Hackborn41203752012-08-31 14:05:51 -07002977 continue;
2978 }
Amith Yamasani5ec00e92012-11-07 16:58:30 -08002979 // Don't remove notifications to all, if there's no package name specified
Daniel Sandlerfde19b12013-01-17 00:21:05 -05002980 if (r.getUserId() == UserHandle.USER_ALL && pkg == null) {
Amith Yamasani5ec00e92012-11-07 16:58:30 -08002981 continue;
2982 }
Daniel Sandlerfde19b12013-01-17 00:21:05 -05002983 if ((r.getFlags() & mustHaveFlags) != mustHaveFlags) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002984 continue;
2985 }
Daniel Sandlerfde19b12013-01-17 00:21:05 -05002986 if ((r.getFlags() & mustNotHaveFlags) != 0) {
Dianne Hackbornd8a43f62009-08-17 23:33:56 -07002987 continue;
2988 }
Daniel Sandler4f91efd2013-04-25 16:38:41 -04002989 if (pkg != null && !r.sbn.getPackageName().equals(pkg)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002990 continue;
2991 }
Christoph Studere4ef156b2014-07-04 18:41:57 +02002992 if (canceledNotifications == null) {
2993 canceledNotifications = new ArrayList<>();
2994 }
2995 canceledNotifications.add(r);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08002996 if (!doit) {
2997 return true;
2998 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002999 mNotificationList.remove(i);
Christoph Studer546bec82014-03-14 12:17:12 +01003000 cancelNotificationLocked(r, false, reason);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003001 }
Christoph Studere4ef156b2014-07-04 18:41:57 +02003002 if (doit && canceledNotifications != null) {
3003 final int M = canceledNotifications.size();
3004 for (int i = 0; i < M; i++) {
3005 cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid,
Christoph Studer265c1052014-07-23 17:14:33 +02003006 listenerName, REASON_GROUP_SUMMARY_CANCELED);
Christoph Studere4ef156b2014-07-04 18:41:57 +02003007 }
3008 }
3009 if (canceledNotifications != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003010 updateLightsLocked();
3011 }
Christoph Studere4ef156b2014-07-04 18:41:57 +02003012 return canceledNotifications != null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003013 }
3014 }
3015
Adam Lesinski350159c2014-03-27 11:15:11 -07003016 void cancelAllLocked(int callingUid, int callingPid, int userId, int reason,
John Spurlock7340fc82014-04-24 18:50:12 -04003017 ManagedServiceInfo listener, boolean includeCurrentProfiles) {
Christoph Studere4ef156b2014-07-04 18:41:57 +02003018 String listenerName = listener == null ? null : listener.component.toShortString();
John Spurlocke6a7d932014-03-13 12:29:00 -04003019 EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
Christoph Studere4ef156b2014-07-04 18:41:57 +02003020 null, userId, 0, 0, reason, listenerName);
Christoph Studer546bec82014-03-14 12:17:12 +01003021
Christoph Studere4ef156b2014-07-04 18:41:57 +02003022 ArrayList<NotificationRecord> canceledNotifications = null;
Adam Lesinskie8240262014-03-26 16:01:00 -07003023 final int N = mNotificationList.size();
3024 for (int i=N-1; i>=0; i--) {
3025 NotificationRecord r = mNotificationList.get(i);
Kenny Guya263e4e2014-03-03 18:24:03 +00003026 if (includeCurrentProfiles) {
3027 if (!notificationMatchesCurrentProfiles(r, userId)) {
3028 continue;
3029 }
3030 } else {
3031 if (!notificationMatchesUserId(r, userId)) {
3032 continue;
3033 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003034 }
3035
Adam Lesinskie8240262014-03-26 16:01:00 -07003036 if ((r.getFlags() & (Notification.FLAG_ONGOING_EVENT
3037 | Notification.FLAG_NO_CLEAR)) == 0) {
3038 mNotificationList.remove(i);
Christoph Studer546bec82014-03-14 12:17:12 +01003039 cancelNotificationLocked(r, true, reason);
Christoph Studere4ef156b2014-07-04 18:41:57 +02003040 // Make a note so we can cancel children later.
3041 if (canceledNotifications == null) {
3042 canceledNotifications = new ArrayList<>();
3043 }
3044 canceledNotifications.add(r);
Adam Lesinskie8240262014-03-26 16:01:00 -07003045 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003046 }
Christoph Studere4ef156b2014-07-04 18:41:57 +02003047 int M = canceledNotifications != null ? canceledNotifications.size() : 0;
3048 for (int i = 0; i < M; i++) {
3049 cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid,
Christoph Studer265c1052014-07-23 17:14:33 +02003050 listenerName, REASON_GROUP_SUMMARY_CANCELED);
Christoph Studere4ef156b2014-07-04 18:41:57 +02003051 }
Adam Lesinskie8240262014-03-26 16:01:00 -07003052 updateLightsLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003053 }
3054
Christoph Studere4ef156b2014-07-04 18:41:57 +02003055 // Warning: The caller is responsible for invoking updateLightsLocked().
3056 private void cancelGroupChildrenLocked(NotificationRecord r, int callingUid, int callingPid,
Christoph Studer265c1052014-07-23 17:14:33 +02003057 String listenerName, int reason) {
Christoph Studere4ef156b2014-07-04 18:41:57 +02003058 Notification n = r.getNotification();
Christoph Studer3f31f5d2014-07-31 16:55:32 +02003059 if (!n.isGroupSummary()) {
Christoph Studere4ef156b2014-07-04 18:41:57 +02003060 return;
3061 }
3062
3063 String pkg = r.sbn.getPackageName();
3064 int userId = r.getUserId();
3065
3066 if (pkg == null) {
3067 if (DBG) Log.e(TAG, "No package for group summary: " + r.getKey());
3068 return;
3069 }
3070
3071 final int N = mNotificationList.size();
3072 for (int i = N - 1; i >= 0; i--) {
3073 NotificationRecord childR = mNotificationList.get(i);
Christoph Studere4ef156b2014-07-04 18:41:57 +02003074 StatusBarNotification childSbn = childR.sbn;
Christoph Studerc44caa92014-08-22 19:16:00 +02003075 if (childR.getNotification().isGroupChild() &&
3076 childR.getGroupKey().equals(r.getGroupKey())) {
Christoph Studer265c1052014-07-23 17:14:33 +02003077 EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, childSbn.getId(),
3078 childSbn.getTag(), userId, 0, 0, reason, listenerName);
Christoph Studere4ef156b2014-07-04 18:41:57 +02003079 mNotificationList.remove(i);
Christoph Studer265c1052014-07-23 17:14:33 +02003080 cancelNotificationLocked(childR, false, reason);
Christoph Studere4ef156b2014-07-04 18:41:57 +02003081 }
3082 }
3083 }
3084
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003085 // lock on mNotificationList
Adam Lesinski182f73f2013-12-05 16:48:06 -08003086 void updateLightsLocked()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003087 {
The Android Open Source Project10592532009-03-18 17:39:46 -07003088 // handle notification lights
Chris Wren6054e612014-11-25 17:16:46 -05003089 NotificationRecord ledNotification = null;
3090 while (ledNotification == null && !mLights.isEmpty()) {
3091 final String owner = mLights.get(mLights.size() - 1);
3092 ledNotification = mNotificationsByKey.get(owner);
3093 if (ledNotification == null) {
3094 Slog.wtfStack(TAG, "LED Notification does not exist: " + owner);
3095 mLights.remove(owner);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003096 }
3097 }
Mike Lockwoodc22404a2009-12-02 11:15:02 -05003098
Mike Lockwood63b5ad92011-08-30 09:55:30 -04003099 // Don't flash while we are in a call or screen is on
Chris Wren6054e612014-11-25 17:16:46 -05003100 if (ledNotification == null || mInCall || mScreenOn) {
Mike Lockwood3cb67a32009-11-27 14:25:58 -05003101 mNotificationLight.turnOff();
John Spurlockcb566aa2014-08-03 22:58:28 -04003102 mStatusBar.notificationLightOff();
The Android Open Source Project10592532009-03-18 17:39:46 -07003103 } else {
Chris Wren6054e612014-11-25 17:16:46 -05003104 final Notification ledno = ledNotification.sbn.getNotification();
Daniel Sandlerfde19b12013-01-17 00:21:05 -05003105 int ledARGB = ledno.ledARGB;
3106 int ledOnMS = ledno.ledOnMS;
3107 int ledOffMS = ledno.ledOffMS;
3108 if ((ledno.defaults & Notification.DEFAULT_LIGHTS) != 0) {
Mike Lockwood670f9322010-01-20 12:13:36 -05003109 ledARGB = mDefaultNotificationColor;
3110 ledOnMS = mDefaultNotificationLedOn;
3111 ledOffMS = mDefaultNotificationLedOff;
3112 }
3113 if (mNotificationPulseEnabled) {
3114 // pulse repeatedly
Adam Lesinski182f73f2013-12-05 16:48:06 -08003115 mNotificationLight.setFlashing(ledARGB, Light.LIGHT_FLASH_TIMED,
Mike Lockwood670f9322010-01-20 12:13:36 -05003116 ledOnMS, ledOffMS);
Mike Lockwood670f9322010-01-20 12:13:36 -05003117 }
John Spurlock39292322014-08-13 11:00:59 -04003118 // let SystemUI make an independent decision
3119 mStatusBar.notificationLightPulse(ledARGB, ledOnMS, ledOffMS);
The Android Open Source Project10592532009-03-18 17:39:46 -07003120 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003121 }
3122
3123 // lock on mNotificationList
Adam Lesinski182f73f2013-12-05 16:48:06 -08003124 int indexOfNotificationLocked(String pkg, String tag, int id, int userId)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003125 {
3126 ArrayList<NotificationRecord> list = mNotificationList;
3127 final int len = list.size();
3128 for (int i=0; i<len; i++) {
3129 NotificationRecord r = list.get(i);
Vladimir Marko2526f332013-09-11 11:13:55 +01003130 if (notificationMatchesUserId(r, userId) && r.sbn.getId() == id &&
3131 TextUtils.equals(r.sbn.getTag(), tag) && r.sbn.getPackageName().equals(pkg)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003132 return i;
3133 }
3134 }
3135 return -1;
3136 }
3137
Christoph Studer71f18fd2014-05-20 17:02:04 +02003138 // lock on mNotificationList
3139 int indexOfNotificationLocked(String key) {
Christoph Studerc5115552014-06-12 20:22:31 +02003140 final int N = mNotificationList.size();
3141 for (int i = 0; i < N; i++) {
3142 if (key.equals(mNotificationList.get(i).getKey())) {
3143 return i;
3144 }
Christoph Studer71f18fd2014-05-20 17:02:04 +02003145 }
Christoph Studerc5115552014-06-12 20:22:31 +02003146 return -1;
Christoph Studer71f18fd2014-05-20 17:02:04 +02003147 }
3148
Mike Lockwoodc22404a2009-12-02 11:15:02 -05003149 private void updateNotificationPulse() {
3150 synchronized (mNotificationList) {
3151 updateLightsLocked();
3152 }
3153 }
John Spurlocke677d712014-02-13 12:52:19 -05003154
John Spurlock7340fc82014-04-24 18:50:12 -04003155 private static boolean isUidSystem(int uid) {
3156 final int appid = UserHandle.getAppId(uid);
3157 return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0);
3158 }
John Spurlockb408e8e2014-04-23 21:12:45 -04003159
John Spurlock7340fc82014-04-24 18:50:12 -04003160 private static boolean isCallerSystem() {
3161 return isUidSystem(Binder.getCallingUid());
3162 }
3163
3164 private static void checkCallerIsSystem() {
3165 if (isCallerSystem()) {
3166 return;
3167 }
3168 throw new SecurityException("Disallowed call for uid " + Binder.getCallingUid());
3169 }
3170
3171 private static void checkCallerIsSystemOrSameApp(String pkg) {
3172 if (isCallerSystem()) {
3173 return;
3174 }
3175 final int uid = Binder.getCallingUid();
3176 try {
3177 ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo(
3178 pkg, 0, UserHandle.getCallingUserId());
Dan Sandler09afc2e2014-07-18 14:29:20 -04003179 if (ai == null) {
3180 throw new SecurityException("Unknown package " + pkg);
3181 }
John Spurlock7340fc82014-04-24 18:50:12 -04003182 if (!UserHandle.isSameApp(ai.uid, uid)) {
3183 throw new SecurityException("Calling uid " + uid + " gave package"
3184 + pkg + " which is owned by uid " + ai.uid);
3185 }
3186 } catch (RemoteException re) {
3187 throw new SecurityException("Unknown package " + pkg + "\n" + re);
3188 }
3189 }
3190
John Spurlock32fe4c62014-10-02 12:16:02 -04003191 private static String callStateToString(int state) {
3192 switch (state) {
3193 case TelephonyManager.CALL_STATE_IDLE: return "CALL_STATE_IDLE";
3194 case TelephonyManager.CALL_STATE_RINGING: return "CALL_STATE_RINGING";
3195 case TelephonyManager.CALL_STATE_OFFHOOK: return "CALL_STATE_OFFHOOK";
3196 default: return "CALL_STATE_UNKNOWN_" + state;
3197 }
3198 }
3199
3200 private void listenForCallState() {
3201 TelephonyManager.from(getContext()).listen(new PhoneStateListener() {
3202 @Override
3203 public void onCallStateChanged(int state, String incomingNumber) {
3204 if (mCallState == state) return;
3205 if (DBG) Slog.d(TAG, "Call state changed: " + callStateToString(state));
3206 mCallState = state;
3207 }
3208 }, PhoneStateListener.LISTEN_CALL_STATE);
3209 }
3210
Christoph Studer05ad4822014-05-16 14:16:03 +02003211 /**
3212 * Generates a NotificationRankingUpdate from 'sbns', considering only
3213 * notifications visible to the given listener.
Chris Wren333a61c2014-05-28 16:40:57 -04003214 *
3215 * <p>Caller must hold a lock on mNotificationList.</p>
Christoph Studer05ad4822014-05-16 14:16:03 +02003216 */
Chris Wren333a61c2014-05-28 16:40:57 -04003217 private NotificationRankingUpdate makeRankingUpdateLocked(ManagedServiceInfo info) {
Christoph Studer05ad4822014-05-16 14:16:03 +02003218 int speedBumpIndex = -1;
Chris Wren333a61c2014-05-28 16:40:57 -04003219 final int N = mNotificationList.size();
3220 ArrayList<String> keys = new ArrayList<String>(N);
Christoph Studer1d599da2014-06-12 15:25:59 +02003221 ArrayList<String> interceptedKeys = new ArrayList<String>(N);
Chris Wrenbdf33762015-12-04 15:50:51 -05003222 ArrayList<Integer> importance = new ArrayList<>(N);
Chris Wren3ad4e3a2014-09-02 17:23:51 -04003223 Bundle visibilityOverrides = new Bundle();
Julia Reynoldsf612869ae2015-11-05 16:48:55 -05003224 Bundle suppressedVisualEffects = new Bundle();
Chris Wrenbdf33762015-12-04 15:50:51 -05003225 Bundle explanation = new Bundle();
Chris Wren333a61c2014-05-28 16:40:57 -04003226 for (int i = 0; i < N; i++) {
3227 NotificationRecord record = mNotificationList.get(i);
Christoph Studercef37cf2014-07-25 14:18:17 +02003228 if (!isVisibleToListener(record.sbn, info)) {
Christoph Studer05ad4822014-05-16 14:16:03 +02003229 continue;
3230 }
Chris Wrenbdf33762015-12-04 15:50:51 -05003231 final String key = record.sbn.getKey();
3232 keys.add(key);
3233 importance.add(record.getImportance());
3234 if (record.getImportanceExplanation() != null) {
3235 explanation.putCharSequence(key, record.getImportanceExplanation());
3236 }
Chris Wren333a61c2014-05-28 16:40:57 -04003237 if (record.isIntercepted()) {
Chris Wrenbdf33762015-12-04 15:50:51 -05003238 interceptedKeys.add(key);
Julia Reynoldsf612869ae2015-11-05 16:48:55 -05003239
Christoph Studer05ad4822014-05-16 14:16:03 +02003240 }
Chris Wrenbdf33762015-12-04 15:50:51 -05003241 suppressedVisualEffects.putInt(key, record.getSuppressedVisualEffects());
Chris Wren3ad4e3a2014-09-02 17:23:51 -04003242 if (record.getPackageVisibilityOverride()
3243 != NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE) {
Chris Wrenbdf33762015-12-04 15:50:51 -05003244 visibilityOverrides.putInt(key, record.getPackageVisibilityOverride());
Chris Wren3ad4e3a2014-09-02 17:23:51 -04003245 }
Christoph Studer327bf782014-09-05 11:40:01 +02003246 // Find first min-prio notification for speedbump placement.
Christoph Studer05ad4822014-05-16 14:16:03 +02003247 if (speedBumpIndex == -1 &&
Christoph Studer327bf782014-09-05 11:40:01 +02003248 // Intrusiveness trumps priority, hence ignore intrusives.
3249 !record.isRecentlyIntrusive() &&
Christoph Studerfbac2142014-09-05 17:40:43 +02003250 // Currently, package priority is either PRIORITY_DEFAULT or PRIORITY_MAX, so
3251 // scanning for PRIORITY_MIN within the package bucket PRIORITY_DEFAULT
3252 // (or lower as a safeguard) is sufficient to find the speedbump index.
3253 // We'll have to revisit this when more package priority buckets are introduced.
3254 record.getPackagePriority() <= Notification.PRIORITY_DEFAULT &&
Chris Wren333a61c2014-05-28 16:40:57 -04003255 record.sbn.getNotification().priority == Notification.PRIORITY_MIN) {
Christoph Studer05ad4822014-05-16 14:16:03 +02003256 speedBumpIndex = keys.size() - 1;
3257 }
3258 }
Chris Wrenbdf33762015-12-04 15:50:51 -05003259 final int M = keys.size();
3260 String[] keysAr = keys.toArray(new String[M]);
Christoph Studer1d599da2014-06-12 15:25:59 +02003261 String[] interceptedKeysAr = interceptedKeys.toArray(new String[interceptedKeys.size()]);
Chris Wrenbdf33762015-12-04 15:50:51 -05003262 int[] importanceAr = new int[M];
3263 for (int i = 0; i < M; i++) {
3264 importanceAr[i] = importance.get(i);
3265 }
Chris Wren3ad4e3a2014-09-02 17:23:51 -04003266 return new NotificationRankingUpdate(keysAr, interceptedKeysAr, visibilityOverrides,
Chris Wrenbdf33762015-12-04 15:50:51 -05003267 speedBumpIndex, suppressedVisualEffects, importanceAr, explanation);
Christoph Studer05ad4822014-05-16 14:16:03 +02003268 }
3269
Christoph Studercef37cf2014-07-25 14:18:17 +02003270 private boolean isVisibleToListener(StatusBarNotification sbn, ManagedServiceInfo listener) {
3271 if (!listener.enabledAndUserMatches(sbn.getUserId())) {
3272 return false;
3273 }
Justin Koh8d11a5a2014-08-04 18:29:49 -07003274 // TODO: remove this for older listeners.
Christoph Studercef37cf2014-07-25 14:18:17 +02003275 return true;
3276 }
3277
John Spurlock7340fc82014-04-24 18:50:12 -04003278 public class NotificationListeners extends ManagedServices {
3279
Christoph Studerb82bc782014-08-20 14:29:43 +02003280 private final ArraySet<ManagedServiceInfo> mLightTrimListeners = new ArraySet<>();
Christoph Studer265c1052014-07-23 17:14:33 +02003281 private boolean mNotificationGroupsDesired;
Christoph Studerb82bc782014-08-20 14:29:43 +02003282
John Spurlock7340fc82014-04-24 18:50:12 -04003283 public NotificationListeners() {
3284 super(getContext(), mHandler, mNotificationList, mUserProfiles);
3285 }
3286
3287 @Override
3288 protected Config getConfig() {
3289 Config c = new Config();
3290 c.caption = "notification listener";
3291 c.serviceInterface = NotificationListenerService.SERVICE_INTERFACE;
3292 c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_LISTENERS;
3293 c.bindPermission = android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE;
3294 c.settingsAction = Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS;
3295 c.clientLabel = R.string.notification_listener_binding_label;
3296 return c;
3297 }
3298
3299 @Override
3300 protected IInterface asInterface(IBinder binder) {
3301 return INotificationListener.Stub.asInterface(binder);
3302 }
3303
3304 @Override
John Spurlock3b98b3f2014-05-01 09:08:48 -04003305 public void onServiceAdded(ManagedServiceInfo info) {
3306 final INotificationListener listener = (INotificationListener) info.service;
Chris Wren333a61c2014-05-28 16:40:57 -04003307 final NotificationRankingUpdate update;
Christoph Studer05ad4822014-05-16 14:16:03 +02003308 synchronized (mNotificationList) {
Christoph Studer265c1052014-07-23 17:14:33 +02003309 updateNotificationGroupsDesiredLocked();
Chris Wren333a61c2014-05-28 16:40:57 -04003310 update = makeRankingUpdateLocked(info);
Christoph Studer05ad4822014-05-16 14:16:03 +02003311 }
John Spurlock7340fc82014-04-24 18:50:12 -04003312 try {
Chris Wren333a61c2014-05-28 16:40:57 -04003313 listener.onListenerConnected(update);
John Spurlock7340fc82014-04-24 18:50:12 -04003314 } catch (RemoteException e) {
3315 // we tried
3316 }
3317 }
3318
John Spurlock1fa865f2014-07-21 14:56:39 -04003319 @Override
3320 protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
John Spurlockd8afe3c2014-08-01 14:04:07 -04003321 if (mListenersDisablingEffects.remove(removed)) {
3322 updateListenerHintsLocked();
Christoph Studer0d6ef4b2014-12-02 15:00:48 +01003323 updateEffectsSuppressorLocked();
John Spurlock1fa865f2014-07-21 14:56:39 -04003324 }
Christoph Studerb82bc782014-08-20 14:29:43 +02003325 mLightTrimListeners.remove(removed);
Christoph Studer265c1052014-07-23 17:14:33 +02003326 updateNotificationGroupsDesiredLocked();
Christoph Studerb82bc782014-08-20 14:29:43 +02003327 }
3328
3329 public void setOnNotificationPostedTrimLocked(ManagedServiceInfo info, int trim) {
3330 if (trim == TRIM_LIGHT) {
3331 mLightTrimListeners.add(info);
3332 } else {
3333 mLightTrimListeners.remove(info);
3334 }
3335 }
3336
3337 public int getOnNotificationPostedTrim(ManagedServiceInfo info) {
3338 return mLightTrimListeners.contains(info) ? TRIM_LIGHT : TRIM_FULL;
3339
John Spurlock1fa865f2014-07-21 14:56:39 -04003340 }
3341
John Spurlock7340fc82014-04-24 18:50:12 -04003342 /**
3343 * asynchronously notify all listeners about a new notification
Christoph Studercef37cf2014-07-25 14:18:17 +02003344 *
3345 * <p>
3346 * Also takes care of removing a notification that has been visible to a listener before,
3347 * but isn't anymore.
John Spurlock7340fc82014-04-24 18:50:12 -04003348 */
Christoph Studercef37cf2014-07-25 14:18:17 +02003349 public void notifyPostedLocked(StatusBarNotification sbn, StatusBarNotification oldSbn) {
Christoph Studerb82bc782014-08-20 14:29:43 +02003350 // Lazily initialized snapshots of the notification.
3351 StatusBarNotification sbnClone = null;
3352 StatusBarNotification sbnCloneLight = null;
3353
John Spurlock7340fc82014-04-24 18:50:12 -04003354 for (final ManagedServiceInfo info : mServices) {
Christoph Studercef37cf2014-07-25 14:18:17 +02003355 boolean sbnVisible = isVisibleToListener(sbn, info);
3356 boolean oldSbnVisible = oldSbn != null ? isVisibleToListener(oldSbn, info) : false;
3357 // This notification hasn't been and still isn't visible -> ignore.
3358 if (!oldSbnVisible && !sbnVisible) {
Christoph Studer05ad4822014-05-16 14:16:03 +02003359 continue;
Chris Wrenf9536642014-04-17 10:01:54 -04003360 }
Chris Wren333a61c2014-05-28 16:40:57 -04003361 final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
Christoph Studercef37cf2014-07-25 14:18:17 +02003362
3363 // This notification became invisible -> remove the old one.
3364 if (oldSbnVisible && !sbnVisible) {
3365 final StatusBarNotification oldSbnLightClone = oldSbn.cloneLight();
3366 mHandler.post(new Runnable() {
3367 @Override
3368 public void run() {
3369 notifyRemoved(info, oldSbnLightClone, update);
3370 }
3371 });
Christoph Studer05ad4822014-05-16 14:16:03 +02003372 continue;
3373 }
Christoph Studercef37cf2014-07-25 14:18:17 +02003374
Christoph Studerb82bc782014-08-20 14:29:43 +02003375 final int trim = mListeners.getOnNotificationPostedTrim(info);
3376
3377 if (trim == TRIM_LIGHT && sbnCloneLight == null) {
3378 sbnCloneLight = sbn.cloneLight();
3379 } else if (trim == TRIM_FULL && sbnClone == null) {
3380 sbnClone = sbn.clone();
3381 }
3382 final StatusBarNotification sbnToPost =
3383 (trim == TRIM_FULL) ? sbnClone : sbnCloneLight;
3384
Christoph Studer05ad4822014-05-16 14:16:03 +02003385 mHandler.post(new Runnable() {
3386 @Override
3387 public void run() {
Christoph Studerb82bc782014-08-20 14:29:43 +02003388 notifyPosted(info, sbnToPost, update);
Christoph Studer05ad4822014-05-16 14:16:03 +02003389 }
3390 });
Kenny Guy3a7c4a52014-03-03 18:24:03 +00003391 }
3392 }
Kenny Guy3a7c4a52014-03-03 18:24:03 +00003393
John Spurlock7340fc82014-04-24 18:50:12 -04003394 /**
3395 * asynchronously notify all listeners about a removed notification
3396 */
Chris Wren333a61c2014-05-28 16:40:57 -04003397 public void notifyRemovedLocked(StatusBarNotification sbn) {
John Spurlock7340fc82014-04-24 18:50:12 -04003398 // make a copy in case changes are made to the underlying Notification object
3399 // NOTE: this copy is lightweight: it doesn't include heavyweight parts of the
3400 // notification
3401 final StatusBarNotification sbnLight = sbn.cloneLight();
Chris Wrenf9536642014-04-17 10:01:54 -04003402 for (final ManagedServiceInfo info : mServices) {
Christoph Studercef37cf2014-07-25 14:18:17 +02003403 if (!isVisibleToListener(sbn, info)) {
Christoph Studer05ad4822014-05-16 14:16:03 +02003404 continue;
Chris Wrenf9536642014-04-17 10:01:54 -04003405 }
Chris Wren333a61c2014-05-28 16:40:57 -04003406 final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
Christoph Studer05ad4822014-05-16 14:16:03 +02003407 mHandler.post(new Runnable() {
3408 @Override
3409 public void run() {
Christoph Studercef37cf2014-07-25 14:18:17 +02003410 notifyRemoved(info, sbnLight, update);
Christoph Studer05ad4822014-05-16 14:16:03 +02003411 }
3412 });
Chris Wrenf9536642014-04-17 10:01:54 -04003413 }
3414 }
3415
3416 /**
3417 * asynchronously notify all listeners about a reordering of notifications
Chris Wrenf9536642014-04-17 10:01:54 -04003418 */
Chris Wren333a61c2014-05-28 16:40:57 -04003419 public void notifyRankingUpdateLocked() {
Chris Wrenf9536642014-04-17 10:01:54 -04003420 for (final ManagedServiceInfo serviceInfo : mServices) {
Christoph Studer05ad4822014-05-16 14:16:03 +02003421 if (!serviceInfo.isEnabledForCurrentProfiles()) {
3422 continue;
3423 }
Christoph Studercef37cf2014-07-25 14:18:17 +02003424 final NotificationRankingUpdate update = makeRankingUpdateLocked(serviceInfo);
John Spurlock7340fc82014-04-24 18:50:12 -04003425 mHandler.post(new Runnable() {
3426 @Override
3427 public void run() {
Chris Wren333a61c2014-05-28 16:40:57 -04003428 notifyRankingUpdate(serviceInfo, update);
John Spurlock7340fc82014-04-24 18:50:12 -04003429 }
3430 });
Kenny Guya263e4e2014-03-03 18:24:03 +00003431 }
Kenny Guya263e4e2014-03-03 18:24:03 +00003432 }
Kenny Guya263e4e2014-03-03 18:24:03 +00003433
John Spurlockd8afe3c2014-08-01 14:04:07 -04003434 public void notifyListenerHintsChangedLocked(final int hints) {
John Spurlock1fa865f2014-07-21 14:56:39 -04003435 for (final ManagedServiceInfo serviceInfo : mServices) {
3436 if (!serviceInfo.isEnabledForCurrentProfiles()) {
3437 continue;
3438 }
3439 mHandler.post(new Runnable() {
3440 @Override
3441 public void run() {
John Spurlockd8afe3c2014-08-01 14:04:07 -04003442 notifyListenerHintsChanged(serviceInfo, hints);
John Spurlock1fa865f2014-07-21 14:56:39 -04003443 }
3444 });
3445 }
3446 }
3447
Christoph Studer85a384b2014-08-27 20:16:15 +02003448 public void notifyInterruptionFilterChanged(final int interruptionFilter) {
3449 for (final ManagedServiceInfo serviceInfo : mServices) {
3450 if (!serviceInfo.isEnabledForCurrentProfiles()) {
3451 continue;
3452 }
3453 mHandler.post(new Runnable() {
3454 @Override
3455 public void run() {
3456 notifyInterruptionFilterChanged(serviceInfo, interruptionFilter);
3457 }
3458 });
3459 }
3460 }
3461
Christoph Studercef37cf2014-07-25 14:18:17 +02003462 private void notifyPosted(final ManagedServiceInfo info,
Christoph Studer05ad4822014-05-16 14:16:03 +02003463 final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) {
John Spurlock7340fc82014-04-24 18:50:12 -04003464 final INotificationListener listener = (INotificationListener)info.service;
Griff Hazen84a00ea2014-09-02 17:10:47 -07003465 StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
John Spurlock7340fc82014-04-24 18:50:12 -04003466 try {
Griff Hazen84a00ea2014-09-02 17:10:47 -07003467 listener.onNotificationPosted(sbnHolder, rankingUpdate);
John Spurlock7340fc82014-04-24 18:50:12 -04003468 } catch (RemoteException ex) {
3469 Log.e(TAG, "unable to notify listener (posted): " + listener, ex);
3470 }
3471 }
3472
Christoph Studercef37cf2014-07-25 14:18:17 +02003473 private void notifyRemoved(ManagedServiceInfo info, StatusBarNotification sbn,
Christoph Studer05ad4822014-05-16 14:16:03 +02003474 NotificationRankingUpdate rankingUpdate) {
John Spurlock7340fc82014-04-24 18:50:12 -04003475 if (!info.enabledAndUserMatches(sbn.getUserId())) {
3476 return;
3477 }
Christoph Studer05ad4822014-05-16 14:16:03 +02003478 final INotificationListener listener = (INotificationListener) info.service;
Griff Hazen84a00ea2014-09-02 17:10:47 -07003479 StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
John Spurlock7340fc82014-04-24 18:50:12 -04003480 try {
Griff Hazen84a00ea2014-09-02 17:10:47 -07003481 listener.onNotificationRemoved(sbnHolder, rankingUpdate);
John Spurlock7340fc82014-04-24 18:50:12 -04003482 } catch (RemoteException ex) {
3483 Log.e(TAG, "unable to notify listener (removed): " + listener, ex);
John Spurlockb408e8e2014-04-23 21:12:45 -04003484 }
Kenny Guya263e4e2014-03-03 18:24:03 +00003485 }
Chris Wrenf9536642014-04-17 10:01:54 -04003486
Christoph Studer05ad4822014-05-16 14:16:03 +02003487 private void notifyRankingUpdate(ManagedServiceInfo info,
3488 NotificationRankingUpdate rankingUpdate) {
3489 final INotificationListener listener = (INotificationListener) info.service;
Chris Wrenf9536642014-04-17 10:01:54 -04003490 try {
Christoph Studer05ad4822014-05-16 14:16:03 +02003491 listener.onNotificationRankingUpdate(rankingUpdate);
Chris Wrenf9536642014-04-17 10:01:54 -04003492 } catch (RemoteException ex) {
3493 Log.e(TAG, "unable to notify listener (ranking update): " + listener, ex);
3494 }
3495 }
John Spurlock1fa865f2014-07-21 14:56:39 -04003496
John Spurlockd8afe3c2014-08-01 14:04:07 -04003497 private void notifyListenerHintsChanged(ManagedServiceInfo info, int hints) {
John Spurlock1fa865f2014-07-21 14:56:39 -04003498 final INotificationListener listener = (INotificationListener) info.service;
3499 try {
John Spurlockd8afe3c2014-08-01 14:04:07 -04003500 listener.onListenerHintsChanged(hints);
John Spurlock1fa865f2014-07-21 14:56:39 -04003501 } catch (RemoteException ex) {
John Spurlockd8afe3c2014-08-01 14:04:07 -04003502 Log.e(TAG, "unable to notify listener (listener hints): " + listener, ex);
John Spurlock1fa865f2014-07-21 14:56:39 -04003503 }
3504 }
Justin Koh38156c52014-06-04 13:57:49 -07003505
Christoph Studer85a384b2014-08-27 20:16:15 +02003506 private void notifyInterruptionFilterChanged(ManagedServiceInfo info,
3507 int interruptionFilter) {
3508 final INotificationListener listener = (INotificationListener) info.service;
3509 try {
3510 listener.onInterruptionFilterChanged(interruptionFilter);
3511 } catch (RemoteException ex) {
3512 Log.e(TAG, "unable to notify listener (interruption filter): " + listener, ex);
3513 }
3514 }
3515
Justin Koh38156c52014-06-04 13:57:49 -07003516 private boolean isListenerPackage(String packageName) {
3517 if (packageName == null) {
3518 return false;
3519 }
3520 // TODO: clean up locking object later
3521 synchronized (mNotificationList) {
3522 for (final ManagedServiceInfo serviceInfo : mServices) {
3523 if (packageName.equals(serviceInfo.component.getPackageName())) {
3524 return true;
3525 }
3526 }
3527 }
3528 return false;
3529 }
Christoph Studer265c1052014-07-23 17:14:33 +02003530
3531 /**
3532 * Returns whether any of the currently registered listeners wants to receive notification
3533 * groups.
3534 *
3535 * <p>Currently we assume groups are desired by non-SystemUI listeners.</p>
3536 */
3537 public boolean notificationGroupsDesired() {
3538 return mNotificationGroupsDesired;
3539 }
3540
3541 private void updateNotificationGroupsDesiredLocked() {
3542 mNotificationGroupsDesired = true;
3543 // No listeners, no groups.
3544 if (mServices.isEmpty()) {
3545 mNotificationGroupsDesired = false;
3546 return;
3547 }
3548 // One listener: Check whether it's SysUI.
3549 if (mServices.size() == 1 &&
3550 mServices.get(0).component.getPackageName().equals("com.android.systemui")) {
3551 mNotificationGroupsDesired = false;
3552 return;
3553 }
3554 }
Kenny Guya263e4e2014-03-03 18:24:03 +00003555 }
John Spurlock25e2d242014-06-27 13:58:23 -04003556
3557 public static final class DumpFilter {
Dan Sandlera1770312015-07-10 13:59:29 -04003558 public boolean filtered = false;
John Spurlock25e2d242014-06-27 13:58:23 -04003559 public String pkgFilter;
John Spurlock50806fc2014-07-15 10:22:02 -04003560 public boolean zen;
Chris Wrene4b38802015-07-07 15:54:19 -04003561 public long since;
3562 public boolean stats;
Dan Sandlera1770312015-07-10 13:59:29 -04003563 public boolean redact = true;
John Spurlock25e2d242014-06-27 13:58:23 -04003564
3565 public static DumpFilter parseFromArguments(String[] args) {
Dan Sandlera1770312015-07-10 13:59:29 -04003566 final DumpFilter filter = new DumpFilter();
3567 for (int ai = 0; ai < args.length; ai++) {
3568 final String a = args[ai];
3569 if ("--noredact".equals(a) || "--reveal".equals(a)) {
3570 filter.redact = false;
3571 } else if ("p".equals(a) || "pkg".equals(a) || "--package".equals(a)) {
3572 if (ai < args.length-1) {
3573 ai++;
3574 filter.pkgFilter = args[ai].trim().toLowerCase();
3575 if (filter.pkgFilter.isEmpty()) {
3576 filter.pkgFilter = null;
3577 } else {
3578 filter.filtered = true;
3579 }
3580 }
3581 } else if ("--zen".equals(a) || "zen".equals(a)) {
3582 filter.filtered = true;
3583 filter.zen = true;
3584 } else if ("--stats".equals(a)) {
3585 filter.stats = true;
3586 if (ai < args.length-1) {
3587 ai++;
3588 filter.since = Long.valueOf(args[ai]);
3589 } else {
3590 filter.since = 0;
3591 }
3592 }
John Spurlock25e2d242014-06-27 13:58:23 -04003593 }
Dan Sandlera1770312015-07-10 13:59:29 -04003594 return filter;
John Spurlock25e2d242014-06-27 13:58:23 -04003595 }
3596
3597 public boolean matches(StatusBarNotification sbn) {
Dan Sandlera1770312015-07-10 13:59:29 -04003598 if (!filtered) return true;
3599 return zen ? true : sbn != null
John Spurlock50806fc2014-07-15 10:22:02 -04003600 && (matches(sbn.getPackageName()) || matches(sbn.getOpPkg()));
John Spurlock25e2d242014-06-27 13:58:23 -04003601 }
3602
3603 public boolean matches(ComponentName component) {
Dan Sandlera1770312015-07-10 13:59:29 -04003604 if (!filtered) return true;
3605 return zen ? true : component != null && matches(component.getPackageName());
John Spurlock25e2d242014-06-27 13:58:23 -04003606 }
3607
3608 public boolean matches(String pkg) {
Dan Sandlera1770312015-07-10 13:59:29 -04003609 if (!filtered) return true;
3610 return zen ? true : pkg != null && pkg.toLowerCase().contains(pkgFilter);
John Spurlock50806fc2014-07-15 10:22:02 -04003611 }
3612
3613 @Override
3614 public String toString() {
Chris Wrene4b38802015-07-07 15:54:19 -04003615 return stats ? "stats" : zen ? "zen" : ('\'' + pkgFilter + '\'');
John Spurlock25e2d242014-06-27 13:58:23 -04003616 }
3617 }
Griff Hazen84a00ea2014-09-02 17:10:47 -07003618
3619 /**
3620 * Wrapper for a StatusBarNotification object that allows transfer across a oneway
3621 * binder without sending large amounts of data over a oneway transaction.
3622 */
3623 private static final class StatusBarNotificationHolder
3624 extends IStatusBarNotificationHolder.Stub {
Griff Hazene9aac5f2014-09-05 20:04:09 -07003625 private StatusBarNotification mValue;
Griff Hazen84a00ea2014-09-02 17:10:47 -07003626
3627 public StatusBarNotificationHolder(StatusBarNotification value) {
3628 mValue = value;
3629 }
3630
Griff Hazene9aac5f2014-09-05 20:04:09 -07003631 /** Get the held value and clear it. This function should only be called once per holder */
Griff Hazen84a00ea2014-09-02 17:10:47 -07003632 @Override
3633 public StatusBarNotification get() {
Griff Hazene9aac5f2014-09-05 20:04:09 -07003634 StatusBarNotification value = mValue;
3635 mValue = null;
3636 return value;
Griff Hazen84a00ea2014-09-02 17:10:47 -07003637 }
3638 }
John Spurlock7c74f782015-06-04 13:01:42 -04003639
3640 private final class PolicyAccess {
3641 private static final String SEPARATOR = ":";
3642 private final String[] PERM = {
3643 android.Manifest.permission.ACCESS_NOTIFICATION_POLICY
3644 };
3645
3646 public boolean isPackageGranted(String pkg) {
3647 return pkg != null && getGrantedPackages().contains(pkg);
3648 }
3649
3650 public void put(String pkg, boolean granted) {
3651 if (pkg == null) return;
3652 final ArraySet<String> pkgs = getGrantedPackages();
3653 boolean changed;
3654 if (granted) {
3655 changed = pkgs.add(pkg);
3656 } else {
3657 changed = pkgs.remove(pkg);
3658 }
3659 if (!changed) return;
3660 final String setting = TextUtils.join(SEPARATOR, pkgs);
3661 final int currentUser = ActivityManager.getCurrentUser();
3662 Settings.Secure.putStringForUser(getContext().getContentResolver(),
3663 Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES,
3664 setting,
3665 currentUser);
3666 getContext().sendBroadcastAsUser(new Intent(NotificationManager
3667 .ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED)
3668 .setPackage(pkg)
3669 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), new UserHandle(currentUser), null);
3670 }
3671
3672 public ArraySet<String> getGrantedPackages() {
3673 final ArraySet<String> pkgs = new ArraySet<>();
Julia Reynoldsea6c4482015-08-13 09:01:33 -04003674
3675 long identity = Binder.clearCallingIdentity();
3676 try {
3677 final String setting = Settings.Secure.getStringForUser(
3678 getContext().getContentResolver(),
3679 Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES,
3680 ActivityManager.getCurrentUser());
3681 if (setting != null) {
3682 final String[] tokens = setting.split(SEPARATOR);
3683 for (int i = 0; i < tokens.length; i++) {
3684 String token = tokens[i];
3685 if (token != null) {
Andreas Gampe1ed71f32015-12-11 15:49:07 -08003686 token = token.trim();
Julia Reynoldsea6c4482015-08-13 09:01:33 -04003687 }
3688 if (TextUtils.isEmpty(token)) {
3689 continue;
3690 }
3691 pkgs.add(token);
John Spurlock7c74f782015-06-04 13:01:42 -04003692 }
John Spurlock7c74f782015-06-04 13:01:42 -04003693 }
Julia Reynoldsea6c4482015-08-13 09:01:33 -04003694 } finally {
3695 Binder.restoreCallingIdentity(identity);
John Spurlock7c74f782015-06-04 13:01:42 -04003696 }
3697 return pkgs;
3698 }
3699
3700 public String[] getRequestingPackages() throws RemoteException {
3701 final ParceledListSlice list = AppGlobals.getPackageManager()
3702 .getPackagesHoldingPermissions(PERM, 0 /*flags*/,
3703 ActivityManager.getCurrentUser());
3704 final List<PackageInfo> pkgs = list.getList();
3705 if (pkgs == null || pkgs.isEmpty()) return new String[0];
3706 final int N = pkgs.size();
3707 final String[] rt = new String[N];
3708 for (int i = 0; i < N; i++) {
3709 rt[i] = pkgs.get(i).packageName;
3710 }
3711 return rt;
3712 }
3713 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003714}