1 /** 2 * Copyright (c) 2014, 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 17 package com.android.server.notification; 18 19 import static android.media.AudioAttributes.USAGE_ALARM; 20 import static android.media.AudioAttributes.USAGE_NOTIFICATION; 21 import static android.media.AudioAttributes.USAGE_NOTIFICATION_RINGTONE; 22 23 import android.app.AppOpsManager; 24 import android.app.Notification; 25 import android.content.ComponentName; 26 import android.content.ContentResolver; 27 import android.content.Context; 28 import android.content.res.Resources; 29 import android.content.res.XmlResourceParser; 30 import android.database.ContentObserver; 31 import android.media.AudioAttributes; 32 import android.media.AudioManager; 33 import android.media.AudioManagerInternal; 34 import android.net.Uri; 35 import android.os.Bundle; 36 import android.os.Handler; 37 import android.os.Looper; 38 import android.os.Message; 39 import android.os.UserHandle; 40 import android.provider.Settings.Global; 41 import android.provider.Settings.Secure; 42 import android.service.notification.NotificationListenerService; 43 import android.service.notification.ZenModeConfig; 44 import android.telecom.TelecomManager; 45 import android.util.Log; 46 import android.util.Slog; 47 48 import com.android.internal.R; 49 import com.android.server.LocalServices; 50 51 import libcore.io.IoUtils; 52 53 import org.xmlpull.v1.XmlPullParser; 54 import org.xmlpull.v1.XmlPullParserException; 55 import org.xmlpull.v1.XmlSerializer; 56 57 import java.io.IOException; 58 import java.io.PrintWriter; 59 import java.util.ArrayList; 60 import java.util.Objects; 61 62 /** 63 * NotificationManagerService helper for functionality related to zen mode. 64 */ 65 public class ZenModeHelper implements AudioManagerInternal.RingerModeDelegate { 66 private static final String TAG = "ZenModeHelper"; 67 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 68 69 private final Context mContext; 70 private final H mHandler; 71 private final SettingsObserver mSettingsObserver; 72 private final AppOpsManager mAppOps; 73 private final ZenModeConfig mDefaultConfig; 74 private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>(); 75 76 private ComponentName mDefaultPhoneApp; 77 private int mZenMode; 78 private ZenModeConfig mConfig; 79 private AudioManagerInternal mAudioManager; 80 private int mPreviousRingerMode = -1; 81 private boolean mEffectsSuppressed; 82 83 public ZenModeHelper(Context context, Looper looper) { 84 mContext = context; 85 mHandler = new H(looper); 86 mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); 87 mDefaultConfig = readDefaultConfig(context.getResources()); 88 mConfig = mDefaultConfig; 89 mSettingsObserver = new SettingsObserver(mHandler); 90 mSettingsObserver.observe(); 91 } 92 93 public static ZenModeConfig readDefaultConfig(Resources resources) { 94 XmlResourceParser parser = null; 95 try { 96 parser = resources.getXml(R.xml.default_zen_mode_config); 97 while (parser.next() != XmlPullParser.END_DOCUMENT) { 98 final ZenModeConfig config = ZenModeConfig.readXml(parser); 99 if (config != null) return config; 100 } 101 } catch (Exception e) { 102 Slog.w(TAG, "Error reading default zen mode config from resource", e); 103 } finally { 104 IoUtils.closeQuietly(parser); 105 } 106 return new ZenModeConfig(); 107 } 108 109 public void addCallback(Callback callback) { 110 mCallbacks.add(callback); 111 } 112 113 public void removeCallback(Callback callback) { 114 mCallbacks.remove(callback); 115 } 116 117 public void onSystemReady() { 118 mAudioManager = LocalServices.getService(AudioManagerInternal.class); 119 if (mAudioManager != null) { 120 mAudioManager.setRingerModeDelegate(this); 121 } 122 } 123 124 public int getZenModeListenerInterruptionFilter() { 125 switch (mZenMode) { 126 case Global.ZEN_MODE_OFF: 127 return NotificationListenerService.INTERRUPTION_FILTER_ALL; 128 case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS: 129 return NotificationListenerService.INTERRUPTION_FILTER_PRIORITY; 130 case Global.ZEN_MODE_NO_INTERRUPTIONS: 131 return NotificationListenerService.INTERRUPTION_FILTER_NONE; 132 default: 133 return 0; 134 } 135 } 136 137 private static int zenModeFromListenerInterruptionFilter(int listenerInterruptionFilter, 138 int defValue) { 139 switch (listenerInterruptionFilter) { 140 case NotificationListenerService.INTERRUPTION_FILTER_ALL: 141 return Global.ZEN_MODE_OFF; 142 case NotificationListenerService.INTERRUPTION_FILTER_PRIORITY: 143 return Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS; 144 case NotificationListenerService.INTERRUPTION_FILTER_NONE: 145 return Global.ZEN_MODE_NO_INTERRUPTIONS; 146 default: 147 return defValue; 148 } 149 } 150 151 public void requestFromListener(ComponentName name, int interruptionFilter) { 152 final int newZen = zenModeFromListenerInterruptionFilter(interruptionFilter, -1); 153 if (newZen != -1) { 154 setZenMode(newZen, "listener:" + (name != null ? name.flattenToShortString() : null)); 155 } 156 } 157 158 public void setEffectsSuppressed(boolean effectsSuppressed) { 159 if (mEffectsSuppressed == effectsSuppressed) return; 160 mEffectsSuppressed = effectsSuppressed; 161 applyRestrictions(); 162 } 163 164 public boolean shouldIntercept(NotificationRecord record) { 165 if (isSystem(record)) { 166 return false; 167 } 168 switch (mZenMode) { 169 case Global.ZEN_MODE_NO_INTERRUPTIONS: 170 // #notevenalarms 171 ZenLog.traceIntercepted(record, "none"); 172 return true; 173 case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS: 174 if (isAlarm(record)) { 175 // Alarms are always priority 176 return false; 177 } 178 // allow user-prioritized packages through in priority mode 179 if (record.getPackagePriority() == Notification.PRIORITY_MAX) { 180 ZenLog.traceNotIntercepted(record, "priorityApp"); 181 return false; 182 } 183 if (isCall(record)) { 184 if (!mConfig.allowCalls) { 185 ZenLog.traceIntercepted(record, "!allowCalls"); 186 return true; 187 } 188 return shouldInterceptAudience(record); 189 } 190 if (isMessage(record)) { 191 if (!mConfig.allowMessages) { 192 ZenLog.traceIntercepted(record, "!allowMessages"); 193 return true; 194 } 195 return shouldInterceptAudience(record); 196 } 197 if (isEvent(record)) { 198 if (!mConfig.allowEvents) { 199 ZenLog.traceIntercepted(record, "!allowEvents"); 200 return true; 201 } 202 return false; 203 } 204 ZenLog.traceIntercepted(record, "!priority"); 205 return true; 206 default: 207 return false; 208 } 209 } 210 211 private boolean shouldInterceptAudience(NotificationRecord record) { 212 if (!audienceMatches(record.getContactAffinity())) { 213 ZenLog.traceIntercepted(record, "!audienceMatches"); 214 return true; 215 } 216 return false; 217 } 218 219 public int getZenMode() { 220 return mZenMode; 221 } 222 223 public void setZenMode(int zenMode, String reason) { 224 setZenMode(zenMode, reason, true); 225 } 226 227 private void setZenMode(int zenMode, String reason, boolean setRingerMode) { 228 ZenLog.traceSetZenMode(zenMode, reason); 229 if (mZenMode == zenMode) return; 230 ZenLog.traceUpdateZenMode(mZenMode, zenMode); 231 mZenMode = zenMode; 232 Global.putInt(mContext.getContentResolver(), Global.ZEN_MODE, mZenMode); 233 if (setRingerMode) { 234 applyZenToRingerMode(); 235 } 236 applyRestrictions(); 237 mHandler.postDispatchOnZenModeChanged(); 238 } 239 240 public void readZenModeFromSetting() { 241 final int newMode = Global.getInt(mContext.getContentResolver(), 242 Global.ZEN_MODE, Global.ZEN_MODE_OFF); 243 setZenMode(newMode, "setting"); 244 } 245 246 private void applyRestrictions() { 247 final boolean zen = mZenMode != Global.ZEN_MODE_OFF; 248 249 // notification restrictions 250 final boolean muteNotifications = mEffectsSuppressed; 251 applyRestrictions(muteNotifications, USAGE_NOTIFICATION); 252 253 // call restrictions 254 final boolean muteCalls = zen && !mConfig.allowCalls || mEffectsSuppressed; 255 applyRestrictions(muteCalls, USAGE_NOTIFICATION_RINGTONE); 256 257 // alarm restrictions 258 final boolean muteAlarms = mZenMode == Global.ZEN_MODE_NO_INTERRUPTIONS; 259 applyRestrictions(muteAlarms, USAGE_ALARM); 260 } 261 262 private void applyRestrictions(boolean mute, int usage) { 263 final String[] exceptionPackages = null; // none (for now) 264 mAppOps.setRestriction(AppOpsManager.OP_VIBRATE, usage, 265 mute ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED, 266 exceptionPackages); 267 mAppOps.setRestriction(AppOpsManager.OP_PLAY_AUDIO, usage, 268 mute ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED, 269 exceptionPackages); 270 } 271 272 public void dump(PrintWriter pw, String prefix) { 273 pw.print(prefix); pw.print("mZenMode="); 274 pw.println(Global.zenModeToString(mZenMode)); 275 pw.print(prefix); pw.print("mConfig="); pw.println(mConfig); 276 pw.print(prefix); pw.print("mDefaultConfig="); pw.println(mDefaultConfig); 277 pw.print(prefix); pw.print("mPreviousRingerMode="); pw.println(mPreviousRingerMode); 278 pw.print(prefix); pw.print("mDefaultPhoneApp="); pw.println(mDefaultPhoneApp); 279 pw.print(prefix); pw.print("mEffectsSuppressed="); pw.println(mEffectsSuppressed); 280 } 281 282 public void readXml(XmlPullParser parser) throws XmlPullParserException, IOException { 283 final ZenModeConfig config = ZenModeConfig.readXml(parser); 284 if (config != null) { 285 setConfig(config); 286 } 287 } 288 289 public void writeXml(XmlSerializer out) throws IOException { 290 mConfig.writeXml(out); 291 } 292 293 public ZenModeConfig getConfig() { 294 return mConfig; 295 } 296 297 public boolean setConfig(ZenModeConfig config) { 298 if (config == null || !config.isValid()) return false; 299 if (config.equals(mConfig)) return true; 300 ZenLog.traceConfig(mConfig, config); 301 mConfig = config; 302 dispatchOnConfigChanged(); 303 final String val = Integer.toString(mConfig.hashCode()); 304 Global.putString(mContext.getContentResolver(), Global.ZEN_MODE_CONFIG_ETAG, val); 305 applyRestrictions(); 306 return true; 307 } 308 309 private void applyZenToRingerMode() { 310 if (mAudioManager == null) return; 311 // force the ringer mode into compliance 312 final int ringerModeInternal = mAudioManager.getRingerModeInternal(); 313 int newRingerModeInternal = ringerModeInternal; 314 switch (mZenMode) { 315 case Global.ZEN_MODE_NO_INTERRUPTIONS: 316 if (ringerModeInternal != AudioManager.RINGER_MODE_SILENT) { 317 mPreviousRingerMode = ringerModeInternal; 318 newRingerModeInternal = AudioManager.RINGER_MODE_SILENT; 319 } 320 break; 321 case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS: 322 case Global.ZEN_MODE_OFF: 323 if (ringerModeInternal == AudioManager.RINGER_MODE_SILENT) { 324 newRingerModeInternal = mPreviousRingerMode != -1 ? mPreviousRingerMode 325 : AudioManager.RINGER_MODE_NORMAL; 326 mPreviousRingerMode = -1; 327 } 328 break; 329 } 330 if (newRingerModeInternal != -1) { 331 mAudioManager.setRingerModeInternal(newRingerModeInternal, TAG); 332 } 333 } 334 335 @Override // RingerModeDelegate 336 public int onSetRingerModeInternal(int ringerModeOld, int ringerModeNew, String caller, 337 int ringerModeExternal) { 338 final boolean isChange = ringerModeOld != ringerModeNew; 339 340 int ringerModeExternalOut = ringerModeNew; 341 342 int newZen = -1; 343 switch (ringerModeNew) { 344 case AudioManager.RINGER_MODE_SILENT: 345 if (isChange) { 346 if (mZenMode != Global.ZEN_MODE_NO_INTERRUPTIONS) { 347 newZen = Global.ZEN_MODE_NO_INTERRUPTIONS; 348 } 349 } 350 break; 351 case AudioManager.RINGER_MODE_VIBRATE: 352 case AudioManager.RINGER_MODE_NORMAL: 353 if (isChange && ringerModeOld == AudioManager.RINGER_MODE_SILENT 354 && mZenMode == Global.ZEN_MODE_NO_INTERRUPTIONS) { 355 newZen = Global.ZEN_MODE_OFF; 356 } else if (mZenMode != Global.ZEN_MODE_OFF) { 357 ringerModeExternalOut = AudioManager.RINGER_MODE_SILENT; 358 } 359 break; 360 } 361 if (newZen != -1) { 362 setZenMode(newZen, "ringerModeInternal", false /*setRingerMode*/); 363 } 364 365 if (isChange || newZen != -1 || ringerModeExternal != ringerModeExternalOut) { 366 ZenLog.traceSetRingerModeInternal(ringerModeOld, ringerModeNew, caller, 367 ringerModeExternal, ringerModeExternalOut); 368 } 369 return ringerModeExternalOut; 370 } 371 372 @Override // RingerModeDelegate 373 public int onSetRingerModeExternal(int ringerModeOld, int ringerModeNew, String caller, 374 int ringerModeInternal) { 375 int ringerModeInternalOut = ringerModeNew; 376 final boolean isChange = ringerModeOld != ringerModeNew; 377 final boolean isVibrate = ringerModeInternal == AudioManager.RINGER_MODE_VIBRATE; 378 379 int newZen = -1; 380 switch (ringerModeNew) { 381 case AudioManager.RINGER_MODE_SILENT: 382 if (isChange) { 383 if (mZenMode == Global.ZEN_MODE_OFF) { 384 newZen = Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS; 385 } 386 ringerModeInternalOut = isVibrate ? AudioManager.RINGER_MODE_VIBRATE 387 : AudioManager.RINGER_MODE_NORMAL; 388 } else { 389 ringerModeInternalOut = ringerModeInternal; 390 } 391 break; 392 case AudioManager.RINGER_MODE_VIBRATE: 393 case AudioManager.RINGER_MODE_NORMAL: 394 if (mZenMode != Global.ZEN_MODE_OFF) { 395 newZen = Global.ZEN_MODE_OFF; 396 } 397 break; 398 } 399 if (newZen != -1) { 400 setZenMode(newZen, "ringerModeExternal", false /*setRingerMode*/); 401 } 402 403 ZenLog.traceSetRingerModeExternal(ringerModeOld, ringerModeNew, caller, ringerModeInternal, 404 ringerModeInternalOut); 405 return ringerModeInternalOut; 406 } 407 408 private void dispatchOnConfigChanged() { 409 for (Callback callback : mCallbacks) { 410 callback.onConfigChanged(); 411 } 412 } 413 414 private void dispatchOnZenModeChanged() { 415 for (Callback callback : mCallbacks) { 416 callback.onZenModeChanged(); 417 } 418 } 419 420 private static boolean isSystem(NotificationRecord record) { 421 return record.isCategory(Notification.CATEGORY_SYSTEM); 422 } 423 424 private static boolean isAlarm(NotificationRecord record) { 425 return record.isCategory(Notification.CATEGORY_ALARM) 426 || record.isAudioStream(AudioManager.STREAM_ALARM) 427 || record.isAudioAttributesUsage(AudioAttributes.USAGE_ALARM); 428 } 429 430 private static boolean isEvent(NotificationRecord record) { 431 return record.isCategory(Notification.CATEGORY_EVENT); 432 } 433 434 public boolean isCall(NotificationRecord record) { 435 return record != null && (isDefaultPhoneApp(record.sbn.getPackageName()) 436 || record.isCategory(Notification.CATEGORY_CALL)); 437 } 438 439 private boolean isDefaultPhoneApp(String pkg) { 440 if (mDefaultPhoneApp == null) { 441 final TelecomManager telecomm = 442 (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE); 443 mDefaultPhoneApp = telecomm != null ? telecomm.getDefaultPhoneApp() : null; 444 if (DEBUG) Slog.d(TAG, "Default phone app: " + mDefaultPhoneApp); 445 } 446 return pkg != null && mDefaultPhoneApp != null 447 && pkg.equals(mDefaultPhoneApp.getPackageName()); 448 } 449 450 private boolean isDefaultMessagingApp(NotificationRecord record) { 451 final int userId = record.getUserId(); 452 if (userId == UserHandle.USER_NULL || userId == UserHandle.USER_ALL) return false; 453 final String defaultApp = Secure.getStringForUser(mContext.getContentResolver(), 454 Secure.SMS_DEFAULT_APPLICATION, userId); 455 return Objects.equals(defaultApp, record.sbn.getPackageName()); 456 } 457 458 private boolean isMessage(NotificationRecord record) { 459 return record.isCategory(Notification.CATEGORY_MESSAGE) || isDefaultMessagingApp(record); 460 } 461 462 /** 463 * @param extras extras of the notification with EXTRA_PEOPLE populated 464 * @param contactsTimeoutMs timeout in milliseconds to wait for contacts response 465 * @param timeoutAffinity affinity to return when the timeout specified via 466 * <code>contactsTimeoutMs</code> is hit 467 */ 468 public boolean matchesCallFilter(UserHandle userHandle, Bundle extras, 469 ValidateNotificationPeople validator, int contactsTimeoutMs, float timeoutAffinity) { 470 final int zen = mZenMode; 471 if (zen == Global.ZEN_MODE_NO_INTERRUPTIONS) return false; // nothing gets through 472 if (zen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS) { 473 if (!mConfig.allowCalls) return false; // no calls get through 474 if (validator != null) { 475 final float contactAffinity = validator.getContactAffinity(userHandle, extras, 476 contactsTimeoutMs, timeoutAffinity); 477 return audienceMatches(contactAffinity); 478 } 479 } 480 return true; 481 } 482 483 @Override 484 public String toString() { 485 return TAG; 486 } 487 488 private boolean audienceMatches(float contactAffinity) { 489 switch (mConfig.allowFrom) { 490 case ZenModeConfig.SOURCE_ANYONE: 491 return true; 492 case ZenModeConfig.SOURCE_CONTACT: 493 return contactAffinity >= ValidateNotificationPeople.VALID_CONTACT; 494 case ZenModeConfig.SOURCE_STAR: 495 return contactAffinity >= ValidateNotificationPeople.STARRED_CONTACT; 496 default: 497 Slog.w(TAG, "Encountered unknown source: " + mConfig.allowFrom); 498 return true; 499 } 500 } 501 502 private class SettingsObserver extends ContentObserver { 503 private final Uri ZEN_MODE = Global.getUriFor(Global.ZEN_MODE); 504 505 public SettingsObserver(Handler handler) { 506 super(handler); 507 } 508 509 public void observe() { 510 final ContentResolver resolver = mContext.getContentResolver(); 511 resolver.registerContentObserver(ZEN_MODE, false /*notifyForDescendents*/, this); 512 update(null); 513 } 514 515 @Override 516 public void onChange(boolean selfChange, Uri uri) { 517 update(uri); 518 } 519 520 public void update(Uri uri) { 521 if (ZEN_MODE.equals(uri)) { 522 readZenModeFromSetting(); 523 } 524 } 525 } 526 527 private class H extends Handler { 528 private static final int MSG_DISPATCH = 1; 529 530 private H(Looper looper) { 531 super(looper); 532 } 533 534 private void postDispatchOnZenModeChanged() { 535 removeMessages(MSG_DISPATCH); 536 sendEmptyMessage(MSG_DISPATCH); 537 } 538 539 @Override 540 public void handleMessage(Message msg) { 541 switch (msg.what) { 542 case MSG_DISPATCH: 543 dispatchOnZenModeChanged(); 544 break; 545 } 546 } 547 } 548 549 public static class Callback { 550 void onConfigChanged() {} 551 void onZenModeChanged() {} 552 } 553 } 554