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