blob: 71cee052d9a1bd1b1dd414f027d789bebc0e43b3 [file] [log] [blame]
John Spurlockb2278d62015-04-07 12:47:12 -04001/**
2 * Copyright (c) 2015, 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
17package com.android.server.notification;
18
Julia Reynoldsccc6ae62018-03-01 16:24:49 -050019import static android.provider.Settings.Global.ZEN_MODE_OFF;
20
John Spurlockb2278d62015-04-07 12:47:12 -040021import android.app.Notification;
Julia Reynoldsccc6ae62018-03-01 16:24:49 -050022import android.app.NotificationManager;
John Spurlockb2278d62015-04-07 12:47:12 -040023import android.content.ComponentName;
24import android.content.Context;
25import android.media.AudioAttributes;
26import android.media.AudioManager;
27import android.os.Bundle;
28import android.os.UserHandle;
29import android.provider.Settings.Global;
30import android.provider.Settings.Secure;
31import android.service.notification.ZenModeConfig;
32import android.telecom.TelecomManager;
John Spurlock1d7d2242015-04-10 08:10:22 -040033import android.util.ArrayMap;
John Spurlockb2278d62015-04-07 12:47:12 -040034import android.util.Slog;
35
Julia Reynoldsccc6ae62018-03-01 16:24:49 -050036import com.android.internal.messages.nano.SystemMessageProto;
Julia Reynolds51eb78f82018-03-07 07:35:21 -050037import com.android.internal.util.NotificationMessagingUtil;
Julia Reynoldsccc6ae62018-03-01 16:24:49 -050038
John Spurlock1d7d2242015-04-10 08:10:22 -040039import java.io.PrintWriter;
40import java.util.Date;
John Spurlockb2278d62015-04-07 12:47:12 -040041import java.util.Objects;
42
43public class ZenModeFiltering {
44 private static final String TAG = ZenModeHelper.TAG;
45 private static final boolean DEBUG = ZenModeHelper.DEBUG;
46
John Spurlock1d7d2242015-04-10 08:10:22 -040047 static final RepeatCallers REPEAT_CALLERS = new RepeatCallers();
48
John Spurlockb2278d62015-04-07 12:47:12 -040049 private final Context mContext;
50
51 private ComponentName mDefaultPhoneApp;
Julia Reynolds51eb78f82018-03-07 07:35:21 -050052 private final NotificationMessagingUtil mMessagingUtil;
John Spurlockb2278d62015-04-07 12:47:12 -040053
54 public ZenModeFiltering(Context context) {
55 mContext = context;
Julia Reynolds51eb78f82018-03-07 07:35:21 -050056 mMessagingUtil = new NotificationMessagingUtil(mContext);
57 }
58
59 public ZenModeFiltering(Context context, NotificationMessagingUtil messagingUtil) {
60 mContext = context;
61 mMessagingUtil = messagingUtil;
John Spurlockb2278d62015-04-07 12:47:12 -040062 }
63
John Spurlock1d7d2242015-04-10 08:10:22 -040064 public void dump(PrintWriter pw, String prefix) {
65 pw.print(prefix); pw.print("mDefaultPhoneApp="); pw.println(mDefaultPhoneApp);
66 pw.print(prefix); pw.print("RepeatCallers.mThresholdMinutes=");
67 pw.println(REPEAT_CALLERS.mThresholdMinutes);
68 synchronized (REPEAT_CALLERS) {
69 if (!REPEAT_CALLERS.mCalls.isEmpty()) {
70 pw.print(prefix); pw.println("RepeatCallers.mCalls=");
71 for (int i = 0; i < REPEAT_CALLERS.mCalls.size(); i++) {
72 pw.print(prefix); pw.print(" ");
73 pw.print(REPEAT_CALLERS.mCalls.keyAt(i));
74 pw.print(" at ");
75 pw.println(ts(REPEAT_CALLERS.mCalls.valueAt(i)));
76 }
77 }
78 }
79 }
80
81 private static String ts(long time) {
82 return new Date(time) + " (" + time + ")";
John Spurlockb2278d62015-04-07 12:47:12 -040083 }
84
85 /**
86 * @param extras extras of the notification with EXTRA_PEOPLE populated
87 * @param contactsTimeoutMs timeout in milliseconds to wait for contacts response
88 * @param timeoutAffinity affinity to return when the timeout specified via
89 * <code>contactsTimeoutMs</code> is hit
90 */
John Spurlock1d7d2242015-04-10 08:10:22 -040091 public static boolean matchesCallFilter(Context context, int zen, ZenModeConfig config,
92 UserHandle userHandle, Bundle extras, ValidateNotificationPeople validator,
93 int contactsTimeoutMs, float timeoutAffinity) {
John Spurlockb2278d62015-04-07 12:47:12 -040094 if (zen == Global.ZEN_MODE_NO_INTERRUPTIONS) return false; // nothing gets through
95 if (zen == Global.ZEN_MODE_ALARMS) return false; // not an alarm
96 if (zen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS) {
Julia Reynoldsc6b371b2016-06-14 08:31:03 -040097 if (config.allowRepeatCallers && REPEAT_CALLERS.isRepeat(context, extras)) {
98 return true;
99 }
John Spurlock1d7d2242015-04-10 08:10:22 -0400100 if (!config.allowCalls) return false; // no other calls get through
John Spurlockb2278d62015-04-07 12:47:12 -0400101 if (validator != null) {
102 final float contactAffinity = validator.getContactAffinity(userHandle, extras,
103 contactsTimeoutMs, timeoutAffinity);
John Spurlocka492d1d2015-05-05 18:30:28 -0400104 return audienceMatches(config.allowCallsFrom, contactAffinity);
John Spurlockb2278d62015-04-07 12:47:12 -0400105 }
106 }
107 return true;
108 }
109
John Spurlock1d7d2242015-04-10 08:10:22 -0400110 private static Bundle extras(NotificationRecord record) {
111 return record != null && record.sbn != null && record.sbn.getNotification() != null
112 ? record.sbn.getNotification().extras : null;
113 }
114
Julia Reynoldsc6b371b2016-06-14 08:31:03 -0400115 protected void recordCall(NotificationRecord record) {
116 REPEAT_CALLERS.recordCall(mContext, extras(record));
117 }
118
John Spurlockb2278d62015-04-07 12:47:12 -0400119 public boolean shouldIntercept(int zen, ZenModeConfig config, NotificationRecord record) {
Julia Reynoldsccc6ae62018-03-01 16:24:49 -0500120 if (zen == ZEN_MODE_OFF) {
121 return false;
122 }
123 // Make an exception to policy for the notification saying that policy has changed
124 if (NotificationManager.Policy.areAllVisualEffectsSuppressed(config.suppressedVisualEffects)
125 && "android".equals(record.sbn.getPackageName())
126 && SystemMessageProto.SystemMessage.NOTE_ZEN_UPGRADE == record.sbn.getId()) {
127 ZenLog.traceNotIntercepted(record, "systemDndChangedNotification");
128 return false;
129 }
John Spurlockb2278d62015-04-07 12:47:12 -0400130 switch (zen) {
131 case Global.ZEN_MODE_NO_INTERRUPTIONS:
132 // #notevenalarms
133 ZenLog.traceIntercepted(record, "none");
134 return true;
135 case Global.ZEN_MODE_ALARMS:
136 if (isAlarm(record)) {
137 // Alarms only
138 return false;
139 }
140 ZenLog.traceIntercepted(record, "alarmsOnly");
141 return true;
142 case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS:
John Spurlockb2278d62015-04-07 12:47:12 -0400143 // allow user-prioritized packages through in priority mode
144 if (record.getPackagePriority() == Notification.PRIORITY_MAX) {
145 ZenLog.traceNotIntercepted(record, "priorityApp");
146 return false;
147 }
Beverly04216872017-09-28 10:55:32 -0400148
149 if (isAlarm(record)) {
150 if (!config.allowAlarms) {
151 ZenLog.traceIntercepted(record, "!allowAlarms");
152 return true;
153 }
154 return false;
155 }
John Spurlockb2278d62015-04-07 12:47:12 -0400156 if (isCall(record)) {
John Spurlock1d7d2242015-04-10 08:10:22 -0400157 if (config.allowRepeatCallers
158 && REPEAT_CALLERS.isRepeat(mContext, extras(record))) {
159 ZenLog.traceNotIntercepted(record, "repeatCaller");
160 return false;
161 }
John Spurlockb2278d62015-04-07 12:47:12 -0400162 if (!config.allowCalls) {
163 ZenLog.traceIntercepted(record, "!allowCalls");
164 return true;
165 }
John Spurlocka492d1d2015-05-05 18:30:28 -0400166 return shouldInterceptAudience(config.allowCallsFrom, record);
John Spurlockb2278d62015-04-07 12:47:12 -0400167 }
168 if (isMessage(record)) {
169 if (!config.allowMessages) {
170 ZenLog.traceIntercepted(record, "!allowMessages");
171 return true;
172 }
John Spurlocka492d1d2015-05-05 18:30:28 -0400173 return shouldInterceptAudience(config.allowMessagesFrom, record);
John Spurlockb2278d62015-04-07 12:47:12 -0400174 }
175 if (isEvent(record)) {
176 if (!config.allowEvents) {
177 ZenLog.traceIntercepted(record, "!allowEvents");
178 return true;
179 }
180 return false;
181 }
182 if (isReminder(record)) {
183 if (!config.allowReminders) {
184 ZenLog.traceIntercepted(record, "!allowReminders");
185 return true;
186 }
187 return false;
188 }
Beverlyd6964762018-02-16 14:07:03 -0500189 if (isMedia(record)) {
190 if (!config.allowMedia) {
191 ZenLog.traceIntercepted(record, "!allowMedia");
192 return true;
193 }
194 return false;
195 }
196 if (isSystem(record)) {
197 if (!config.allowSystem) {
198 ZenLog.traceIntercepted(record, "!allowSystem");
Beverly04216872017-09-28 10:55:32 -0400199 return true;
200 }
201 return false;
202 }
John Spurlockb2278d62015-04-07 12:47:12 -0400203 ZenLog.traceIntercepted(record, "!priority");
204 return true;
205 default:
206 return false;
207 }
208 }
209
John Spurlocka492d1d2015-05-05 18:30:28 -0400210 private static boolean shouldInterceptAudience(int source, NotificationRecord record) {
211 if (!audienceMatches(source, record.getContactAffinity())) {
John Spurlockb2278d62015-04-07 12:47:12 -0400212 ZenLog.traceIntercepted(record, "!audienceMatches");
213 return true;
214 }
215 return false;
216 }
217
Julia Reynolds51eb78f82018-03-07 07:35:21 -0500218 protected static boolean isAlarm(NotificationRecord record) {
John Spurlockb2278d62015-04-07 12:47:12 -0400219 return record.isCategory(Notification.CATEGORY_ALARM)
John Spurlockb2278d62015-04-07 12:47:12 -0400220 || record.isAudioAttributesUsage(AudioAttributes.USAGE_ALARM);
221 }
222
223 private static boolean isEvent(NotificationRecord record) {
224 return record.isCategory(Notification.CATEGORY_EVENT);
225 }
226
227 private static boolean isReminder(NotificationRecord record) {
228 return record.isCategory(Notification.CATEGORY_REMINDER);
229 }
230
231 public boolean isCall(NotificationRecord record) {
232 return record != null && (isDefaultPhoneApp(record.sbn.getPackageName())
233 || record.isCategory(Notification.CATEGORY_CALL));
234 }
235
Beverlyd6964762018-02-16 14:07:03 -0500236 public boolean isMedia(NotificationRecord record) {
237 AudioAttributes aa = record.getAudioAttributes();
238 return aa != null && AudioAttributes.SUPPRESSIBLE_USAGES.get(aa.getUsage()) ==
239 AudioAttributes.SUPPRESSIBLE_MEDIA;
240 }
241
242 public boolean isSystem(NotificationRecord record) {
243 AudioAttributes aa = record.getAudioAttributes();
244 return aa != null && AudioAttributes.SUPPRESSIBLE_USAGES.get(aa.getUsage()) ==
245 AudioAttributes.SUPPRESSIBLE_SYSTEM;
246 }
247
John Spurlockb2278d62015-04-07 12:47:12 -0400248 private boolean isDefaultPhoneApp(String pkg) {
249 if (mDefaultPhoneApp == null) {
250 final TelecomManager telecomm =
251 (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
252 mDefaultPhoneApp = telecomm != null ? telecomm.getDefaultPhoneApp() : null;
253 if (DEBUG) Slog.d(TAG, "Default phone app: " + mDefaultPhoneApp);
254 }
255 return pkg != null && mDefaultPhoneApp != null
256 && pkg.equals(mDefaultPhoneApp.getPackageName());
257 }
258
Julia Reynolds51eb78f82018-03-07 07:35:21 -0500259 protected boolean isMessage(NotificationRecord record) {
260 return mMessagingUtil.isMessaging(record.sbn);
John Spurlockb2278d62015-04-07 12:47:12 -0400261 }
262
John Spurlocka492d1d2015-05-05 18:30:28 -0400263 private static boolean audienceMatches(int source, float contactAffinity) {
264 switch (source) {
John Spurlockb2278d62015-04-07 12:47:12 -0400265 case ZenModeConfig.SOURCE_ANYONE:
266 return true;
267 case ZenModeConfig.SOURCE_CONTACT:
268 return contactAffinity >= ValidateNotificationPeople.VALID_CONTACT;
269 case ZenModeConfig.SOURCE_STAR:
270 return contactAffinity >= ValidateNotificationPeople.STARRED_CONTACT;
271 default:
John Spurlocka492d1d2015-05-05 18:30:28 -0400272 Slog.w(TAG, "Encountered unknown source: " + source);
John Spurlockb2278d62015-04-07 12:47:12 -0400273 return true;
274 }
275 }
John Spurlock1d7d2242015-04-10 08:10:22 -0400276
277 private static class RepeatCallers {
Julia Reynoldsc6b371b2016-06-14 08:31:03 -0400278 // Person : time
John Spurlock1d7d2242015-04-10 08:10:22 -0400279 private final ArrayMap<String, Long> mCalls = new ArrayMap<>();
280 private int mThresholdMinutes;
281
Julia Reynoldsc6b371b2016-06-14 08:31:03 -0400282 private synchronized void recordCall(Context context, Bundle extras) {
283 setThresholdMinutes(context);
284 if (mThresholdMinutes <= 0 || extras == null) return;
285 final String peopleString = peopleString(extras);
286 if (peopleString == null) return;
287 final long now = System.currentTimeMillis();
288 cleanUp(mCalls, now);
289 mCalls.put(peopleString, now);
290 }
291
John Spurlock1d7d2242015-04-10 08:10:22 -0400292 private synchronized boolean isRepeat(Context context, Bundle extras) {
Julia Reynoldsc6b371b2016-06-14 08:31:03 -0400293 setThresholdMinutes(context);
John Spurlock1d7d2242015-04-10 08:10:22 -0400294 if (mThresholdMinutes <= 0 || extras == null) return false;
295 final String peopleString = peopleString(extras);
296 if (peopleString == null) return false;
297 final long now = System.currentTimeMillis();
Julia Reynoldsc6b371b2016-06-14 08:31:03 -0400298 cleanUp(mCalls, now);
299 return mCalls.containsKey(peopleString);
300 }
301
302 private synchronized void cleanUp(ArrayMap<String, Long> calls, long now) {
303 final int N = calls.size();
John Spurlock1d7d2242015-04-10 08:10:22 -0400304 for (int i = N - 1; i >= 0; i--) {
305 final long time = mCalls.valueAt(i);
306 if (time > now || (now - time) > mThresholdMinutes * 1000 * 60) {
Julia Reynoldsc6b371b2016-06-14 08:31:03 -0400307 calls.removeAt(i);
John Spurlock1d7d2242015-04-10 08:10:22 -0400308 }
309 }
Julia Reynoldsc6b371b2016-06-14 08:31:03 -0400310 }
311
312 private void setThresholdMinutes(Context context) {
313 if (mThresholdMinutes <= 0) {
314 mThresholdMinutes = context.getResources().getInteger(com.android.internal.R.integer
315 .config_zen_repeat_callers_threshold);
316 }
John Spurlock1d7d2242015-04-10 08:10:22 -0400317 }
318
319 private static String peopleString(Bundle extras) {
320 final String[] extraPeople = ValidateNotificationPeople.getExtraPeople(extras);
321 if (extraPeople == null || extraPeople.length == 0) return null;
322 final StringBuilder sb = new StringBuilder();
323 for (int i = 0; i < extraPeople.length; i++) {
324 String extraPerson = extraPeople[i];
325 if (extraPerson == null) continue;
326 extraPerson = extraPerson.trim();
327 if (extraPerson.isEmpty()) continue;
328 if (sb.length() > 0) {
329 sb.append('|');
330 }
331 sb.append(extraPerson);
332 }
333 return sb.length() == 0 ? null : sb.toString();
334 }
335 }
336
John Spurlockb2278d62015-04-07 12:47:12 -0400337}