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.NotificationManager; 25 import android.app.NotificationManager.Policy; 26 import android.content.ComponentName; 27 import android.content.ContentResolver; 28 import android.content.Context; 29 import android.content.res.Resources; 30 import android.content.res.XmlResourceParser; 31 import android.database.ContentObserver; 32 import android.media.AudioManager; 33 import android.media.AudioManagerInternal; 34 import android.media.AudioSystem; 35 import android.media.VolumePolicy; 36 import android.net.Uri; 37 import android.os.Bundle; 38 import android.os.Handler; 39 import android.os.Looper; 40 import android.os.Message; 41 import android.os.SystemClock; 42 import android.os.UserHandle; 43 import android.provider.Settings.Global; 44 import android.service.notification.IConditionListener; 45 import android.service.notification.ZenModeConfig; 46 import android.service.notification.ZenModeConfig.EventInfo; 47 import android.service.notification.ZenModeConfig.ScheduleInfo; 48 import android.service.notification.ZenModeConfig.ZenRule; 49 import android.util.ArraySet; 50 import android.util.Log; 51 import android.util.SparseArray; 52 53 import com.android.internal.R; 54 import com.android.internal.logging.MetricsLogger; 55 import com.android.server.LocalServices; 56 57 import libcore.io.IoUtils; 58 59 import org.xmlpull.v1.XmlPullParser; 60 import org.xmlpull.v1.XmlPullParserException; 61 import org.xmlpull.v1.XmlSerializer; 62 63 import java.io.IOException; 64 import java.io.PrintWriter; 65 import java.util.ArrayList; 66 import java.util.Objects; 67 68 /** 69 * NotificationManagerService helper for functionality related to zen mode. 70 */ 71 public class ZenModeHelper { 72 static final String TAG = "ZenModeHelper"; 73 static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 74 75 private final Context mContext; 76 private final H mHandler; 77 private final SettingsObserver mSettingsObserver; 78 private final AppOpsManager mAppOps; 79 private final ZenModeConfig mDefaultConfig; 80 private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>(); 81 private final ZenModeFiltering mFiltering; 82 private final RingerModeDelegate mRingerModeDelegate = new RingerModeDelegate(); 83 private final ZenModeConditions mConditions; 84 private final SparseArray<ZenModeConfig> mConfigs = new SparseArray<>(); 85 private final Metrics mMetrics = new Metrics(); 86 87 private int mZenMode; 88 private int mUser = UserHandle.USER_OWNER; 89 private ZenModeConfig mConfig; 90 private AudioManagerInternal mAudioManager; 91 private boolean mEffectsSuppressed; 92 93 public ZenModeHelper(Context context, Looper looper, ConditionProviders conditionProviders) { 94 mContext = context; 95 mHandler = new H(looper); 96 addCallback(mMetrics); 97 mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); 98 mDefaultConfig = readDefaultConfig(context.getResources()); 99 appendDefaultScheduleRules(mDefaultConfig); 100 appendDefaultEventRules(mDefaultConfig); 101 mConfig = mDefaultConfig; 102 mConfigs.put(UserHandle.USER_OWNER, mConfig); 103 mSettingsObserver = new SettingsObserver(mHandler); 104 mSettingsObserver.observe(); 105 mFiltering = new ZenModeFiltering(mContext); 106 mConditions = new ZenModeConditions(this, conditionProviders); 107 } 108 109 public Looper getLooper() { 110 return mHandler.getLooper(); 111 } 112 113 @Override 114 public String toString() { 115 return TAG; 116 } 117 118 public boolean matchesCallFilter(UserHandle userHandle, Bundle extras, 119 ValidateNotificationPeople validator, int contactsTimeoutMs, float timeoutAffinity) { 120 return ZenModeFiltering.matchesCallFilter(mContext, mZenMode, mConfig, userHandle, extras, 121 validator, contactsTimeoutMs, timeoutAffinity); 122 } 123 124 public boolean isCall(NotificationRecord record) { 125 return mFiltering.isCall(record); 126 } 127 128 public boolean shouldIntercept(NotificationRecord record) { 129 return mFiltering.shouldIntercept(mZenMode, mConfig, record); 130 } 131 132 public void addCallback(Callback callback) { 133 mCallbacks.add(callback); 134 } 135 136 public void removeCallback(Callback callback) { 137 mCallbacks.remove(callback); 138 } 139 140 public void initZenMode() { 141 if (DEBUG) Log.d(TAG, "initZenMode"); 142 evaluateZenMode("init", true /*setRingerMode*/); 143 } 144 145 public void onSystemReady() { 146 if (DEBUG) Log.d(TAG, "onSystemReady"); 147 mAudioManager = LocalServices.getService(AudioManagerInternal.class); 148 if (mAudioManager != null) { 149 mAudioManager.setRingerModeDelegate(mRingerModeDelegate); 150 } 151 mHandler.postMetricsTimer(); 152 } 153 154 public void onUserSwitched(int user) { 155 if (mUser == user || user < UserHandle.USER_OWNER) return; 156 mUser = user; 157 if (DEBUG) Log.d(TAG, "onUserSwitched u=" + user); 158 ZenModeConfig config = mConfigs.get(user); 159 if (config == null) { 160 if (DEBUG) Log.d(TAG, "onUserSwitched: generating default config for user " + user); 161 config = mDefaultConfig.copy(); 162 config.user = user; 163 } 164 setConfig(config, "onUserSwitched"); 165 } 166 167 public void onUserRemoved(int user) { 168 if (user < UserHandle.USER_OWNER) return; 169 if (DEBUG) Log.d(TAG, "onUserRemoved u=" + user); 170 mConfigs.remove(user); 171 } 172 173 public void requestZenModeConditions(IConditionListener callback, int relevance) { 174 mConditions.requestConditions(callback, relevance); 175 } 176 177 public int getZenModeListenerInterruptionFilter() { 178 return NotificationManager.zenModeToInterruptionFilter(mZenMode); 179 } 180 181 public void requestFromListener(ComponentName name, int filter) { 182 final int newZen = NotificationManager.zenModeFromInterruptionFilter(filter, -1); 183 if (newZen != -1) { 184 setManualZenMode(newZen, null, 185 "listener:" + (name != null ? name.flattenToShortString() : null)); 186 } 187 } 188 189 public void setEffectsSuppressed(boolean effectsSuppressed) { 190 if (mEffectsSuppressed == effectsSuppressed) return; 191 mEffectsSuppressed = effectsSuppressed; 192 applyRestrictions(); 193 } 194 195 public int getZenMode() { 196 return mZenMode; 197 } 198 199 public void setManualZenMode(int zenMode, Uri conditionId, String reason) { 200 setManualZenMode(zenMode, conditionId, reason, true /*setRingerMode*/); 201 } 202 203 private void setManualZenMode(int zenMode, Uri conditionId, String reason, 204 boolean setRingerMode) { 205 if (mConfig == null) return; 206 if (!Global.isValidZenMode(zenMode)) return; 207 if (DEBUG) Log.d(TAG, "setManualZenMode " + Global.zenModeToString(zenMode) 208 + " conditionId=" + conditionId + " reason=" + reason 209 + " setRingerMode=" + setRingerMode); 210 final ZenModeConfig newConfig = mConfig.copy(); 211 if (zenMode == Global.ZEN_MODE_OFF) { 212 newConfig.manualRule = null; 213 for (ZenRule automaticRule : newConfig.automaticRules.values()) { 214 if (automaticRule.isAutomaticActive()) { 215 automaticRule.snoozing = true; 216 } 217 } 218 } else { 219 final ZenRule newRule = new ZenRule(); 220 newRule.enabled = true; 221 newRule.zenMode = zenMode; 222 newRule.conditionId = conditionId; 223 newConfig.manualRule = newRule; 224 } 225 setConfig(newConfig, reason, setRingerMode); 226 } 227 228 public void dump(PrintWriter pw, String prefix) { 229 pw.print(prefix); pw.print("mZenMode="); 230 pw.println(Global.zenModeToString(mZenMode)); 231 dump(pw, prefix, "mDefaultConfig", mDefaultConfig); 232 final int N = mConfigs.size(); 233 for (int i = 0; i < N; i++) { 234 dump(pw, prefix, "mConfigs[u=" + mConfigs.keyAt(i) + "]", mConfigs.valueAt(i)); 235 } 236 pw.print(prefix); pw.print("mUser="); pw.println(mUser); 237 dump(pw, prefix, "mConfig", mConfig); 238 pw.print(prefix); pw.print("mEffectsSuppressed="); pw.println(mEffectsSuppressed); 239 mFiltering.dump(pw, prefix); 240 mConditions.dump(pw, prefix); 241 } 242 243 private static void dump(PrintWriter pw, String prefix, String var, ZenModeConfig config) { 244 pw.print(prefix); pw.print(var); pw.print('='); 245 if (config == null) { 246 pw.println(config); 247 return; 248 } 249 pw.printf("allow(calls=%s,callsFrom=%s,repeatCallers=%s,messages=%s,messagesFrom=%s," 250 + "events=%s,reminders=%s)\n", 251 config.allowCalls, config.allowCallsFrom, config.allowRepeatCallers, 252 config.allowMessages, config.allowMessagesFrom, 253 config.allowEvents, config.allowReminders); 254 pw.print(prefix); pw.print(" manualRule="); pw.println(config.manualRule); 255 if (config.automaticRules.isEmpty()) return; 256 final int N = config.automaticRules.size(); 257 for (int i = 0; i < N; i++) { 258 pw.print(prefix); pw.print(i == 0 ? " automaticRules=" : " "); 259 pw.println(config.automaticRules.valueAt(i)); 260 } 261 } 262 263 public void readXml(XmlPullParser parser, boolean forRestore) 264 throws XmlPullParserException, IOException { 265 final ZenModeConfig config = ZenModeConfig.readXml(parser, mConfigMigration); 266 if (config != null) { 267 if (forRestore) { 268 if (config.user != UserHandle.USER_OWNER) { 269 return; 270 } 271 config.manualRule = null; // don't restore the manual rule 272 if (config.automaticRules != null) { 273 for (ZenModeConfig.ZenRule automaticRule : config.automaticRules.values()) { 274 // don't restore transient state from restored automatic rules 275 automaticRule.snoozing = false; 276 automaticRule.condition = null; 277 } 278 } 279 } 280 if (DEBUG) Log.d(TAG, "readXml"); 281 setConfig(config, "readXml"); 282 } 283 } 284 285 public void writeXml(XmlSerializer out, boolean forBackup) throws IOException { 286 final int N = mConfigs.size(); 287 for (int i = 0; i < N; i++) { 288 if (forBackup && mConfigs.keyAt(i) != UserHandle.USER_OWNER) { 289 continue; 290 } 291 mConfigs.valueAt(i).writeXml(out); 292 } 293 } 294 295 public Policy getNotificationPolicy() { 296 return getNotificationPolicy(mConfig); 297 } 298 299 private static Policy getNotificationPolicy(ZenModeConfig config) { 300 return config == null ? null : config.toNotificationPolicy(); 301 } 302 303 public void setNotificationPolicy(Policy policy) { 304 if (policy == null || mConfig == null) return; 305 final ZenModeConfig newConfig = mConfig.copy(); 306 newConfig.applyNotificationPolicy(policy); 307 setConfig(newConfig, "setNotificationPolicy"); 308 } 309 310 public ZenModeConfig getConfig() { 311 return mConfig; 312 } 313 314 public boolean setConfig(ZenModeConfig config, String reason) { 315 return setConfig(config, reason, true /*setRingerMode*/); 316 } 317 318 private boolean setConfig(ZenModeConfig config, String reason, boolean setRingerMode) { 319 if (config == null || !config.isValid()) { 320 Log.w(TAG, "Invalid config in setConfig; " + config); 321 return false; 322 } 323 if (config.user != mUser) { 324 // simply store away for background users 325 mConfigs.put(config.user, config); 326 if (DEBUG) Log.d(TAG, "setConfig: store config for user " + config.user); 327 return true; 328 } 329 mConditions.evaluateConfig(config, false /*processSubscriptions*/); // may modify config 330 mConfigs.put(config.user, config); 331 if (config.equals(mConfig)) return true; 332 if (DEBUG) Log.d(TAG, "setConfig reason=" + reason, new Throwable()); 333 ZenLog.traceConfig(reason, mConfig, config); 334 final boolean policyChanged = !Objects.equals(getNotificationPolicy(mConfig), 335 getNotificationPolicy(config)); 336 mConfig = config; 337 dispatchOnConfigChanged(); 338 if (policyChanged){ 339 dispatchOnPolicyChanged(); 340 } 341 final String val = Integer.toString(mConfig.hashCode()); 342 Global.putString(mContext.getContentResolver(), Global.ZEN_MODE_CONFIG_ETAG, val); 343 if (!evaluateZenMode(reason, setRingerMode)) { 344 applyRestrictions(); // evaluateZenMode will also apply restrictions if changed 345 } 346 mConditions.evaluateConfig(config, true /*processSubscriptions*/); 347 return true; 348 } 349 350 private int getZenModeSetting() { 351 return Global.getInt(mContext.getContentResolver(), Global.ZEN_MODE, Global.ZEN_MODE_OFF); 352 } 353 354 private void setZenModeSetting(int zen) { 355 Global.putInt(mContext.getContentResolver(), Global.ZEN_MODE, zen); 356 } 357 358 private int getPreviousRingerModeSetting() { 359 return Global.getInt(mContext.getContentResolver(), 360 Global.ZEN_MODE_RINGER_LEVEL, AudioManager.RINGER_MODE_NORMAL); 361 } 362 363 private void setPreviousRingerModeSetting(Integer previousRingerLevel) { 364 Global.putString( 365 mContext.getContentResolver(), Global.ZEN_MODE_RINGER_LEVEL, 366 previousRingerLevel == null ? null : Integer.toString(previousRingerLevel)); 367 } 368 369 private boolean evaluateZenMode(String reason, boolean setRingerMode) { 370 if (DEBUG) Log.d(TAG, "evaluateZenMode"); 371 final ArraySet<ZenRule> automaticRules = new ArraySet<ZenRule>(); 372 final int zen = computeZenMode(automaticRules); 373 if (zen == mZenMode) return false; 374 ZenLog.traceSetZenMode(zen, reason); 375 mZenMode = zen; 376 updateRingerModeAffectedStreams(); 377 setZenModeSetting(mZenMode); 378 if (setRingerMode) { 379 applyZenToRingerMode(); 380 } 381 applyRestrictions(); 382 mHandler.postDispatchOnZenModeChanged(); 383 return true; 384 } 385 386 private void updateRingerModeAffectedStreams() { 387 if (mAudioManager != null) { 388 mAudioManager.updateRingerModeAffectedStreamsInternal(); 389 } 390 } 391 392 private int computeZenMode(ArraySet<ZenRule> automaticRulesOut) { 393 if (mConfig == null) return Global.ZEN_MODE_OFF; 394 if (mConfig.manualRule != null) return mConfig.manualRule.zenMode; 395 int zen = Global.ZEN_MODE_OFF; 396 for (ZenRule automaticRule : mConfig.automaticRules.values()) { 397 if (automaticRule.isAutomaticActive()) { 398 if (zenSeverity(automaticRule.zenMode) > zenSeverity(zen)) { 399 zen = automaticRule.zenMode; 400 } 401 } 402 } 403 return zen; 404 } 405 406 private void applyRestrictions() { 407 final boolean zen = mZenMode != Global.ZEN_MODE_OFF; 408 409 // notification restrictions 410 final boolean muteNotifications = mEffectsSuppressed; 411 applyRestrictions(muteNotifications, USAGE_NOTIFICATION); 412 413 // call restrictions 414 final boolean muteCalls = zen && !mConfig.allowCalls && !mConfig.allowRepeatCallers 415 || mEffectsSuppressed; 416 applyRestrictions(muteCalls, USAGE_NOTIFICATION_RINGTONE); 417 418 // alarm restrictions 419 final boolean muteAlarms = mZenMode == Global.ZEN_MODE_NO_INTERRUPTIONS; 420 applyRestrictions(muteAlarms, USAGE_ALARM); 421 } 422 423 private void applyRestrictions(boolean mute, int usage) { 424 final String[] exceptionPackages = null; // none (for now) 425 mAppOps.setRestriction(AppOpsManager.OP_VIBRATE, usage, 426 mute ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED, 427 exceptionPackages); 428 mAppOps.setRestriction(AppOpsManager.OP_PLAY_AUDIO, usage, 429 mute ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED, 430 exceptionPackages); 431 } 432 433 private void applyZenToRingerMode() { 434 if (mAudioManager == null) return; 435 // force the ringer mode into compliance 436 final int ringerModeInternal = mAudioManager.getRingerModeInternal(); 437 int newRingerModeInternal = ringerModeInternal; 438 switch (mZenMode) { 439 case Global.ZEN_MODE_NO_INTERRUPTIONS: 440 case Global.ZEN_MODE_ALARMS: 441 if (ringerModeInternal != AudioManager.RINGER_MODE_SILENT) { 442 setPreviousRingerModeSetting(ringerModeInternal); 443 newRingerModeInternal = AudioManager.RINGER_MODE_SILENT; 444 } 445 break; 446 case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS: 447 case Global.ZEN_MODE_OFF: 448 if (ringerModeInternal == AudioManager.RINGER_MODE_SILENT) { 449 newRingerModeInternal = getPreviousRingerModeSetting(); 450 setPreviousRingerModeSetting(null); 451 } 452 break; 453 } 454 if (newRingerModeInternal != -1) { 455 mAudioManager.setRingerModeInternal(newRingerModeInternal, TAG); 456 } 457 } 458 459 private void dispatchOnConfigChanged() { 460 for (Callback callback : mCallbacks) { 461 callback.onConfigChanged(); 462 } 463 } 464 465 private void dispatchOnPolicyChanged() { 466 for (Callback callback : mCallbacks) { 467 callback.onPolicyChanged(); 468 } 469 } 470 471 private void dispatchOnZenModeChanged() { 472 for (Callback callback : mCallbacks) { 473 callback.onZenModeChanged(); 474 } 475 } 476 477 private ZenModeConfig readDefaultConfig(Resources resources) { 478 XmlResourceParser parser = null; 479 try { 480 parser = resources.getXml(R.xml.default_zen_mode_config); 481 while (parser.next() != XmlPullParser.END_DOCUMENT) { 482 final ZenModeConfig config = ZenModeConfig.readXml(parser, mConfigMigration); 483 if (config != null) return config; 484 } 485 } catch (Exception e) { 486 Log.w(TAG, "Error reading default zen mode config from resource", e); 487 } finally { 488 IoUtils.closeQuietly(parser); 489 } 490 return new ZenModeConfig(); 491 } 492 493 private void appendDefaultScheduleRules(ZenModeConfig config) { 494 if (config == null) return; 495 496 final ScheduleInfo weeknights = new ScheduleInfo(); 497 weeknights.days = ZenModeConfig.WEEKNIGHT_DAYS; 498 weeknights.startHour = 22; 499 weeknights.endHour = 7; 500 final ZenRule rule1 = new ZenRule(); 501 rule1.enabled = false; 502 rule1.name = mContext.getResources() 503 .getString(R.string.zen_mode_default_weeknights_name); 504 rule1.conditionId = ZenModeConfig.toScheduleConditionId(weeknights); 505 rule1.zenMode = Global.ZEN_MODE_ALARMS; 506 config.automaticRules.put(config.newRuleId(), rule1); 507 508 final ScheduleInfo weekends = new ScheduleInfo(); 509 weekends.days = ZenModeConfig.WEEKEND_DAYS; 510 weekends.startHour = 23; 511 weekends.startMinute = 30; 512 weekends.endHour = 10; 513 final ZenRule rule2 = new ZenRule(); 514 rule2.enabled = false; 515 rule2.name = mContext.getResources() 516 .getString(R.string.zen_mode_default_weekends_name); 517 rule2.conditionId = ZenModeConfig.toScheduleConditionId(weekends); 518 rule2.zenMode = Global.ZEN_MODE_ALARMS; 519 config.automaticRules.put(config.newRuleId(), rule2); 520 } 521 522 private void appendDefaultEventRules(ZenModeConfig config) { 523 if (config == null) return; 524 525 final EventInfo events = new EventInfo(); 526 events.calendar = null; // any calendar 527 events.reply = EventInfo.REPLY_YES_OR_MAYBE; 528 final ZenRule rule = new ZenRule(); 529 rule.enabled = false; 530 rule.name = mContext.getResources().getString(R.string.zen_mode_default_events_name); 531 rule.conditionId = ZenModeConfig.toEventConditionId(events); 532 rule.zenMode = Global.ZEN_MODE_ALARMS; 533 config.automaticRules.put(config.newRuleId(), rule); 534 } 535 536 private static int zenSeverity(int zen) { 537 switch (zen) { 538 case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS: return 1; 539 case Global.ZEN_MODE_ALARMS: return 2; 540 case Global.ZEN_MODE_NO_INTERRUPTIONS: return 3; 541 default: return 0; 542 } 543 } 544 545 private final ZenModeConfig.Migration mConfigMigration = new ZenModeConfig.Migration() { 546 @Override 547 public ZenModeConfig migrate(ZenModeConfig.XmlV1 v1) { 548 if (v1 == null) return null; 549 final ZenModeConfig rt = new ZenModeConfig(); 550 rt.allowCalls = v1.allowCalls; 551 rt.allowEvents = v1.allowEvents; 552 rt.allowCallsFrom = v1.allowFrom; 553 rt.allowMessages = v1.allowMessages; 554 rt.allowMessagesFrom = v1.allowFrom; 555 rt.allowReminders = v1.allowReminders; 556 // don't migrate current exit condition 557 final int[] days = ZenModeConfig.XmlV1.tryParseDays(v1.sleepMode); 558 if (days != null && days.length > 0) { 559 Log.i(TAG, "Migrating existing V1 downtime to single schedule"); 560 final ScheduleInfo schedule = new ScheduleInfo(); 561 schedule.days = days; 562 schedule.startHour = v1.sleepStartHour; 563 schedule.startMinute = v1.sleepStartMinute; 564 schedule.endHour = v1.sleepEndHour; 565 schedule.endMinute = v1.sleepEndMinute; 566 final ZenRule rule = new ZenRule(); 567 rule.enabled = true; 568 rule.name = mContext.getResources() 569 .getString(R.string.zen_mode_downtime_feature_name); 570 rule.conditionId = ZenModeConfig.toScheduleConditionId(schedule); 571 rule.zenMode = v1.sleepNone ? Global.ZEN_MODE_NO_INTERRUPTIONS 572 : Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS; 573 rt.automaticRules.put(rt.newRuleId(), rule); 574 } else { 575 Log.i(TAG, "No existing V1 downtime found, generating default schedules"); 576 appendDefaultScheduleRules(rt); 577 } 578 appendDefaultEventRules(rt); 579 return rt; 580 } 581 }; 582 583 private final class RingerModeDelegate implements AudioManagerInternal.RingerModeDelegate { 584 @Override 585 public String toString() { 586 return TAG; 587 } 588 589 @Override 590 public int onSetRingerModeInternal(int ringerModeOld, int ringerModeNew, String caller, 591 int ringerModeExternal, VolumePolicy policy) { 592 final boolean isChange = ringerModeOld != ringerModeNew; 593 594 int ringerModeExternalOut = ringerModeNew; 595 596 int newZen = -1; 597 switch (ringerModeNew) { 598 case AudioManager.RINGER_MODE_SILENT: 599 if (isChange && policy.doNotDisturbWhenSilent) { 600 if (mZenMode != Global.ZEN_MODE_NO_INTERRUPTIONS 601 && mZenMode != Global.ZEN_MODE_ALARMS) { 602 newZen = Global.ZEN_MODE_ALARMS; 603 } 604 setPreviousRingerModeSetting(ringerModeOld); 605 } 606 break; 607 case AudioManager.RINGER_MODE_VIBRATE: 608 case AudioManager.RINGER_MODE_NORMAL: 609 if (isChange && ringerModeOld == AudioManager.RINGER_MODE_SILENT 610 && (mZenMode == Global.ZEN_MODE_NO_INTERRUPTIONS 611 || mZenMode == Global.ZEN_MODE_ALARMS)) { 612 newZen = Global.ZEN_MODE_OFF; 613 } else if (mZenMode != Global.ZEN_MODE_OFF) { 614 ringerModeExternalOut = AudioManager.RINGER_MODE_SILENT; 615 } 616 break; 617 } 618 if (newZen != -1) { 619 setManualZenMode(newZen, null, "ringerModeInternal", false /*setRingerMode*/); 620 } 621 622 if (isChange || newZen != -1 || ringerModeExternal != ringerModeExternalOut) { 623 ZenLog.traceSetRingerModeInternal(ringerModeOld, ringerModeNew, caller, 624 ringerModeExternal, ringerModeExternalOut); 625 } 626 return ringerModeExternalOut; 627 } 628 629 @Override 630 public int onSetRingerModeExternal(int ringerModeOld, int ringerModeNew, String caller, 631 int ringerModeInternal, VolumePolicy policy) { 632 int ringerModeInternalOut = ringerModeNew; 633 final boolean isChange = ringerModeOld != ringerModeNew; 634 final boolean isVibrate = ringerModeInternal == AudioManager.RINGER_MODE_VIBRATE; 635 636 int newZen = -1; 637 switch (ringerModeNew) { 638 case AudioManager.RINGER_MODE_SILENT: 639 if (isChange) { 640 if (mZenMode == Global.ZEN_MODE_OFF) { 641 newZen = Global.ZEN_MODE_ALARMS; 642 } 643 ringerModeInternalOut = isVibrate ? AudioManager.RINGER_MODE_VIBRATE 644 : AudioManager.RINGER_MODE_SILENT; 645 } else { 646 ringerModeInternalOut = ringerModeInternal; 647 } 648 break; 649 case AudioManager.RINGER_MODE_VIBRATE: 650 case AudioManager.RINGER_MODE_NORMAL: 651 if (mZenMode != Global.ZEN_MODE_OFF) { 652 newZen = Global.ZEN_MODE_OFF; 653 } 654 break; 655 } 656 if (newZen != -1) { 657 setManualZenMode(newZen, null, "ringerModeExternal", false /*setRingerMode*/); 658 } 659 660 ZenLog.traceSetRingerModeExternal(ringerModeOld, ringerModeNew, caller, 661 ringerModeInternal, ringerModeInternalOut); 662 return ringerModeInternalOut; 663 } 664 665 @Override 666 public boolean canVolumeDownEnterSilent() { 667 return mZenMode == Global.ZEN_MODE_OFF; 668 } 669 670 @Override 671 public int getRingerModeAffectedStreams(int streams) { 672 // ringtone, notification and system streams are always affected by ringer mode 673 streams |= (1 << AudioSystem.STREAM_RING) | 674 (1 << AudioSystem.STREAM_NOTIFICATION) | 675 (1 << AudioSystem.STREAM_SYSTEM); 676 677 // alarm and music streams are only affected by ringer mode when in total silence 678 if (mZenMode == Global.ZEN_MODE_NO_INTERRUPTIONS) { 679 streams |= (1 << AudioSystem.STREAM_ALARM) | 680 (1 << AudioSystem.STREAM_MUSIC); 681 } else { 682 streams &= ~((1 << AudioSystem.STREAM_ALARM) | 683 (1 << AudioSystem.STREAM_MUSIC)); 684 } 685 return streams; 686 } 687 } 688 689 private final class SettingsObserver extends ContentObserver { 690 private final Uri ZEN_MODE = Global.getUriFor(Global.ZEN_MODE); 691 692 public SettingsObserver(Handler handler) { 693 super(handler); 694 } 695 696 public void observe() { 697 final ContentResolver resolver = mContext.getContentResolver(); 698 resolver.registerContentObserver(ZEN_MODE, false /*notifyForDescendents*/, this); 699 update(null); 700 } 701 702 @Override 703 public void onChange(boolean selfChange, Uri uri) { 704 update(uri); 705 } 706 707 public void update(Uri uri) { 708 if (ZEN_MODE.equals(uri)) { 709 if (mZenMode != getZenModeSetting()) { 710 if (DEBUG) Log.d(TAG, "Fixing zen mode setting"); 711 setZenModeSetting(mZenMode); 712 } 713 } 714 } 715 } 716 717 private final class Metrics extends Callback { 718 private static final String COUNTER_PREFIX = "dnd_mode_"; 719 private static final long MINIMUM_LOG_PERIOD_MS = 60 * 1000; 720 721 private int mPreviousZenMode = -1; 722 private long mBeginningMs = 0L; 723 724 @Override 725 void onZenModeChanged() { 726 emit(); 727 } 728 729 private void emit() { 730 mHandler.postMetricsTimer(); 731 final long now = SystemClock.elapsedRealtime(); 732 final long since = (now - mBeginningMs); 733 if (mPreviousZenMode != mZenMode || since > MINIMUM_LOG_PERIOD_MS) { 734 if (mPreviousZenMode != -1) { 735 MetricsLogger.count(mContext, COUNTER_PREFIX + mPreviousZenMode, (int) since); 736 } 737 mPreviousZenMode = mZenMode; 738 mBeginningMs = now; 739 } 740 } 741 } 742 743 private final class H extends Handler { 744 private static final int MSG_DISPATCH = 1; 745 private static final int MSG_METRICS = 2; 746 747 private static final long METRICS_PERIOD_MS = 6 * 60 * 60 * 1000; 748 749 private H(Looper looper) { 750 super(looper); 751 } 752 753 private void postDispatchOnZenModeChanged() { 754 removeMessages(MSG_DISPATCH); 755 sendEmptyMessage(MSG_DISPATCH); 756 } 757 758 private void postMetricsTimer() { 759 removeMessages(MSG_METRICS); 760 sendEmptyMessageDelayed(MSG_METRICS, METRICS_PERIOD_MS); 761 } 762 763 @Override 764 public void handleMessage(Message msg) { 765 switch (msg.what) { 766 case MSG_DISPATCH: 767 dispatchOnZenModeChanged(); 768 break; 769 case MSG_METRICS: 770 mMetrics.emit(); 771 break; 772 } 773 } 774 } 775 776 public static class Callback { 777 void onConfigChanged() {} 778 void onZenModeChanged() {} 779 void onPolicyChanged() {} 780 } 781 782 } 783