Home | History | Annotate | Download | only in notification
      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