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 android.app.AppOpsManager;
     20 import android.app.AutomaticZenRule;
     21 import android.app.NotificationManager;
     22 import android.app.NotificationManager.Policy;
     23 import android.content.ComponentName;
     24 import android.content.ContentResolver;
     25 import android.content.Context;
     26 import android.content.Intent;
     27 import android.content.pm.PackageManager;
     28 import android.content.pm.ResolveInfo;
     29 import android.content.pm.ServiceInfo;
     30 import android.content.res.Resources;
     31 import android.content.res.XmlResourceParser;
     32 import android.database.ContentObserver;
     33 import android.media.AudioAttributes;
     34 import android.media.AudioManager;
     35 import android.media.AudioManagerInternal;
     36 import android.media.AudioSystem;
     37 import android.media.VolumePolicy;
     38 import android.net.Uri;
     39 import android.os.Binder;
     40 import android.os.Bundle;
     41 import android.os.Handler;
     42 import android.os.Looper;
     43 import android.os.Message;
     44 import android.os.Process;
     45 import android.os.SystemClock;
     46 import android.os.UserHandle;
     47 import android.provider.Settings.Global;
     48 import android.service.notification.Condition;
     49 import android.service.notification.ConditionProviderService;
     50 import android.service.notification.ZenModeConfig;
     51 import android.service.notification.ZenModeConfig.EventInfo;
     52 import android.service.notification.ZenModeConfig.ScheduleInfo;
     53 import android.service.notification.ZenModeConfig.ZenRule;
     54 import android.service.notification.ZenModeProto;
     55 import android.util.AndroidRuntimeException;
     56 import android.util.Log;
     57 import android.util.Slog;
     58 import android.util.SparseArray;
     59 import android.util.proto.ProtoOutputStream;
     60 
     61 import com.android.internal.R;
     62 import com.android.internal.logging.MetricsLogger;
     63 import com.android.server.LocalServices;
     64 
     65 import libcore.io.IoUtils;
     66 
     67 import org.xmlpull.v1.XmlPullParser;
     68 import org.xmlpull.v1.XmlPullParserException;
     69 import org.xmlpull.v1.XmlSerializer;
     70 
     71 import java.io.IOException;
     72 import java.io.PrintWriter;
     73 import java.util.ArrayList;
     74 import java.util.List;
     75 import java.util.Objects;
     76 
     77 /**
     78  * NotificationManagerService helper for functionality related to zen mode.
     79  */
     80 public class ZenModeHelper {
     81     static final String TAG = "ZenModeHelper";
     82     static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
     83 
     84     // The amount of time rules instances can exist without their owning app being installed.
     85     private static final int RULE_INSTANCE_GRACE_PERIOD = 1000 * 60 * 60 * 72;
     86 
     87     private final Context mContext;
     88     private final H mHandler;
     89     private final SettingsObserver mSettingsObserver;
     90     private final AppOpsManager mAppOps;
     91     protected ZenModeConfig mDefaultConfig;
     92     private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
     93     private final ZenModeFiltering mFiltering;
     94     private final RingerModeDelegate mRingerModeDelegate = new RingerModeDelegate();
     95     private final ZenModeConditions mConditions;
     96     private final SparseArray<ZenModeConfig> mConfigs = new SparseArray<>();
     97     private final Metrics mMetrics = new Metrics();
     98     private final ConditionProviders.Config mServiceConfig;
     99 
    100     protected final ArrayList<String> mDefaultRuleIds = new ArrayList<>();
    101     private final String EVENTS_DEFAULT_RULE = "EVENTS_DEFAULT_RULE";
    102     private final String SCHEDULED_DEFAULT_RULE_1 = "SCHEDULED_DEFAULT_RULE_1";
    103     private final String SCHEDULED_DEFAULT_RULE_2 = "SCHEDULED_DEFAULT_RULE_2";
    104 
    105     private int mZenMode;
    106     private int mUser = UserHandle.USER_SYSTEM;
    107     protected ZenModeConfig mConfig;
    108     private AudioManagerInternal mAudioManager;
    109     protected PackageManager mPm;
    110     private long mSuppressedEffects;
    111 
    112     public static final long SUPPRESSED_EFFECT_NOTIFICATIONS = 1;
    113     public static final long SUPPRESSED_EFFECT_CALLS = 1 << 1;
    114     public static final long SUPPRESSED_EFFECT_ALL = SUPPRESSED_EFFECT_CALLS
    115             | SUPPRESSED_EFFECT_NOTIFICATIONS;
    116 
    117     protected String mDefaultRuleWeeknightsName;
    118     protected String mDefaultRuleEventsName;
    119     protected String mDefaultRuleWeekendsName;
    120 
    121     public ZenModeHelper(Context context, Looper looper, ConditionProviders conditionProviders) {
    122         mContext = context;
    123         mHandler = new H(looper);
    124         addCallback(mMetrics);
    125         mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
    126 
    127         mDefaultConfig = new ZenModeConfig();
    128         mDefaultRuleWeeknightsName = mContext.getResources()
    129                 .getString(R.string.zen_mode_default_weeknights_name);
    130         mDefaultRuleWeekendsName = mContext.getResources()
    131                 .getString(R.string.zen_mode_default_weekends_name);
    132         mDefaultRuleEventsName = mContext.getResources()
    133                 .getString(R.string.zen_mode_default_events_name);
    134         setDefaultZenRules(mContext);
    135         mConfig = mDefaultConfig;
    136         mConfigs.put(UserHandle.USER_SYSTEM, mConfig);
    137 
    138         mSettingsObserver = new SettingsObserver(mHandler);
    139         mSettingsObserver.observe();
    140         mFiltering = new ZenModeFiltering(mContext);
    141         mConditions = new ZenModeConditions(this, conditionProviders);
    142         mServiceConfig = conditionProviders.getConfig();
    143 
    144     }
    145 
    146     public Looper getLooper() {
    147         return mHandler.getLooper();
    148     }
    149 
    150     @Override
    151     public String toString() {
    152         return TAG;
    153     }
    154 
    155     public boolean matchesCallFilter(UserHandle userHandle, Bundle extras,
    156             ValidateNotificationPeople validator, int contactsTimeoutMs, float timeoutAffinity) {
    157         synchronized (mConfig) {
    158             return ZenModeFiltering.matchesCallFilter(mContext, mZenMode, mConfig, userHandle,
    159                     extras, validator, contactsTimeoutMs, timeoutAffinity);
    160         }
    161     }
    162 
    163     public boolean isCall(NotificationRecord record) {
    164         return mFiltering.isCall(record);
    165     }
    166 
    167     public void recordCaller(NotificationRecord record) {
    168         mFiltering.recordCall(record);
    169     }
    170 
    171     public boolean shouldIntercept(NotificationRecord record) {
    172         synchronized (mConfig) {
    173             return mFiltering.shouldIntercept(mZenMode, mConfig, record);
    174         }
    175     }
    176 
    177     public boolean shouldSuppressWhenScreenOff() {
    178         synchronized (mConfig) {
    179             return !mConfig.allowWhenScreenOff;
    180         }
    181     }
    182 
    183     public boolean shouldSuppressWhenScreenOn() {
    184         synchronized (mConfig) {
    185             return !mConfig.allowWhenScreenOn;
    186         }
    187     }
    188 
    189     public void addCallback(Callback callback) {
    190         mCallbacks.add(callback);
    191     }
    192 
    193     public void removeCallback(Callback callback) {
    194         mCallbacks.remove(callback);
    195     }
    196 
    197     public void initZenMode() {
    198         if (DEBUG) Log.d(TAG, "initZenMode");
    199         evaluateZenMode("init", true /*setRingerMode*/);
    200     }
    201 
    202     public void onSystemReady() {
    203         if (DEBUG) Log.d(TAG, "onSystemReady");
    204         mAudioManager = LocalServices.getService(AudioManagerInternal.class);
    205         if (mAudioManager != null) {
    206             mAudioManager.setRingerModeDelegate(mRingerModeDelegate);
    207         }
    208         mPm = mContext.getPackageManager();
    209         mHandler.postMetricsTimer();
    210         cleanUpZenRules();
    211         evaluateZenMode("onSystemReady", true);
    212     }
    213 
    214     public void onUserSwitched(int user) {
    215         loadConfigForUser(user, "onUserSwitched");
    216     }
    217 
    218     public void onUserRemoved(int user) {
    219         if (user < UserHandle.USER_SYSTEM) return;
    220         if (DEBUG) Log.d(TAG, "onUserRemoved u=" + user);
    221         mConfigs.remove(user);
    222     }
    223 
    224     public void onUserUnlocked(int user) {
    225         loadConfigForUser(user, "onUserUnlocked");
    226     }
    227 
    228     private void loadConfigForUser(int user, String reason) {
    229         if (mUser == user || user < UserHandle.USER_SYSTEM) return;
    230         mUser = user;
    231         if (DEBUG) Log.d(TAG, reason + " u=" + user);
    232         ZenModeConfig config = mConfigs.get(user);
    233         if (config == null) {
    234             if (DEBUG) Log.d(TAG, reason + " generating default config for user " + user);
    235             config = mDefaultConfig.copy();
    236             config.user = user;
    237         }
    238         synchronized (mConfig) {
    239             setConfigLocked(config, reason);
    240         }
    241         cleanUpZenRules();
    242     }
    243 
    244     public int getZenModeListenerInterruptionFilter() {
    245         return NotificationManager.zenModeToInterruptionFilter(mZenMode);
    246     }
    247 
    248     public void requestFromListener(ComponentName name, int filter) {
    249         final int newZen = NotificationManager.zenModeFromInterruptionFilter(filter, -1);
    250         if (newZen != -1) {
    251             setManualZenMode(newZen, null, name != null ? name.getPackageName() : null,
    252                     "listener:" + (name != null ? name.flattenToShortString() : null));
    253         }
    254     }
    255 
    256     public void setSuppressedEffects(long suppressedEffects) {
    257         if (mSuppressedEffects == suppressedEffects) return;
    258         mSuppressedEffects = suppressedEffects;
    259         applyRestrictions();
    260     }
    261 
    262     public long getSuppressedEffects() {
    263         return mSuppressedEffects;
    264     }
    265 
    266     public int getZenMode() {
    267         return mZenMode;
    268     }
    269 
    270     public List<ZenRule> getZenRules() {
    271         List<ZenRule> rules = new ArrayList<>();
    272         synchronized (mConfig) {
    273             if (mConfig == null) return rules;
    274             for (ZenRule rule : mConfig.automaticRules.values()) {
    275                 if (canManageAutomaticZenRule(rule)) {
    276                     rules.add(rule);
    277                 }
    278             }
    279         }
    280         return rules;
    281     }
    282 
    283     public AutomaticZenRule getAutomaticZenRule(String id) {
    284         ZenRule rule;
    285         synchronized (mConfig) {
    286             if (mConfig == null) return null;
    287              rule = mConfig.automaticRules.get(id);
    288         }
    289         if (rule == null) return null;
    290         if (canManageAutomaticZenRule(rule)) {
    291              return createAutomaticZenRule(rule);
    292         }
    293         return null;
    294     }
    295 
    296     public String addAutomaticZenRule(AutomaticZenRule automaticZenRule, String reason) {
    297         if (!isSystemRule(automaticZenRule)) {
    298             ServiceInfo owner = getServiceInfo(automaticZenRule.getOwner());
    299             if (owner == null) {
    300                 throw new IllegalArgumentException("Owner is not a condition provider service");
    301             }
    302 
    303             int ruleInstanceLimit = -1;
    304             if (owner.metaData != null) {
    305                 ruleInstanceLimit = owner.metaData.getInt(
    306                         ConditionProviderService.META_DATA_RULE_INSTANCE_LIMIT, -1);
    307             }
    308             if (ruleInstanceLimit > 0 && ruleInstanceLimit
    309                     < (getCurrentInstanceCount(automaticZenRule.getOwner()) + 1)) {
    310                 throw new IllegalArgumentException("Rule instance limit exceeded");
    311             }
    312         }
    313 
    314         ZenModeConfig newConfig;
    315         synchronized (mConfig) {
    316             if (mConfig == null) {
    317                 throw new AndroidRuntimeException("Could not create rule");
    318             }
    319             if (DEBUG) {
    320                 Log.d(TAG, "addAutomaticZenRule rule= " + automaticZenRule + " reason=" + reason);
    321             }
    322             newConfig = mConfig.copy();
    323             ZenRule rule = new ZenRule();
    324             populateZenRule(automaticZenRule, rule, true);
    325             newConfig.automaticRules.put(rule.id, rule);
    326             if (setConfigLocked(newConfig, reason, true)) {
    327                 return rule.id;
    328             } else {
    329                 throw new AndroidRuntimeException("Could not create rule");
    330             }
    331         }
    332     }
    333 
    334     public boolean updateAutomaticZenRule(String ruleId, AutomaticZenRule automaticZenRule,
    335             String reason) {
    336         ZenModeConfig newConfig;
    337         synchronized (mConfig) {
    338             if (mConfig == null) return false;
    339             if (DEBUG) {
    340                 Log.d(TAG, "updateAutomaticZenRule zenRule=" + automaticZenRule
    341                         + " reason=" + reason);
    342             }
    343             newConfig = mConfig.copy();
    344             ZenModeConfig.ZenRule rule;
    345             if (ruleId == null) {
    346                 throw new IllegalArgumentException("Rule doesn't exist");
    347             } else {
    348                 rule = newConfig.automaticRules.get(ruleId);
    349                 if (rule == null || !canManageAutomaticZenRule(rule)) {
    350                     throw new SecurityException(
    351                             "Cannot update rules not owned by your condition provider");
    352                 }
    353             }
    354             populateZenRule(automaticZenRule, rule, false);
    355             newConfig.automaticRules.put(ruleId, rule);
    356             return setConfigLocked(newConfig, reason, true);
    357         }
    358     }
    359 
    360     public boolean removeAutomaticZenRule(String id, String reason) {
    361         ZenModeConfig newConfig;
    362         synchronized (mConfig) {
    363             if (mConfig == null) return false;
    364             newConfig = mConfig.copy();
    365             ZenRule rule = newConfig.automaticRules.get(id);
    366             if (rule == null) return false;
    367             if (canManageAutomaticZenRule(rule)) {
    368                 newConfig.automaticRules.remove(id);
    369                 if (DEBUG) Log.d(TAG, "removeZenRule zenRule=" + id + " reason=" + reason);
    370             } else {
    371                 throw new SecurityException(
    372                         "Cannot delete rules not owned by your condition provider");
    373             }
    374             return setConfigLocked(newConfig, reason, true);
    375         }
    376     }
    377 
    378     public boolean removeAutomaticZenRules(String packageName, String reason) {
    379         ZenModeConfig newConfig;
    380         synchronized (mConfig) {
    381             if (mConfig == null) return false;
    382             newConfig = mConfig.copy();
    383             for (int i = newConfig.automaticRules.size() - 1; i >= 0; i--) {
    384                 ZenRule rule = newConfig.automaticRules.get(newConfig.automaticRules.keyAt(i));
    385                 if (rule.component.getPackageName().equals(packageName)
    386                         && canManageAutomaticZenRule(rule)) {
    387                     newConfig.automaticRules.removeAt(i);
    388                 }
    389             }
    390             return setConfigLocked(newConfig, reason, true);
    391         }
    392     }
    393 
    394     public int getCurrentInstanceCount(ComponentName owner) {
    395         int count = 0;
    396         synchronized (mConfig) {
    397             for (ZenRule rule : mConfig.automaticRules.values()) {
    398                 if (rule.component != null && rule.component.equals(owner)) {
    399                     count++;
    400                 }
    401             }
    402         }
    403         return count;
    404     }
    405 
    406     public boolean canManageAutomaticZenRule(ZenRule rule) {
    407         final int callingUid = Binder.getCallingUid();
    408         if (callingUid == 0 || callingUid == Process.SYSTEM_UID) {
    409             return true;
    410         } else if (mContext.checkCallingPermission(android.Manifest.permission.MANAGE_NOTIFICATIONS)
    411                 == PackageManager.PERMISSION_GRANTED) {
    412             return true;
    413         } else {
    414             String[] packages = mPm.getPackagesForUid(Binder.getCallingUid());
    415             if (packages != null) {
    416                 final int packageCount = packages.length;
    417                 for (int i = 0; i < packageCount; i++) {
    418                     if (packages[i].equals(rule.component.getPackageName())) {
    419                         return true;
    420                     }
    421                 }
    422             }
    423             return false;
    424         }
    425     }
    426 
    427     public void setDefaultZenRules(Context context) {
    428         mDefaultConfig = readDefaultConfig(context.getResources());
    429 
    430         mDefaultRuleIds.add(EVENTS_DEFAULT_RULE);
    431         mDefaultRuleIds.add(SCHEDULED_DEFAULT_RULE_1);
    432         mDefaultRuleIds.add(SCHEDULED_DEFAULT_RULE_2);
    433 
    434         appendDefaultRules(mDefaultConfig);
    435     }
    436 
    437     private void appendDefaultRules (ZenModeConfig config) {
    438         appendDefaultScheduleRules(config);
    439         appendDefaultEventRules(config);
    440     }
    441 
    442     // Checks zen rule properties are the same (doesn't check creation time, name nor enabled)
    443     // used to check if default rules were customized or not
    444     private boolean ruleValuesEqual(AutomaticZenRule rule, ZenRule defaultRule) {
    445         if (rule == null || defaultRule == null) {
    446             return false;
    447         }
    448         return rule.getInterruptionFilter() ==
    449                 NotificationManager.zenModeToInterruptionFilter(defaultRule.zenMode)
    450                 && rule.getConditionId().equals(defaultRule.conditionId)
    451                 && rule.getOwner().equals(defaultRule.component);
    452     }
    453 
    454     protected void updateDefaultZenRules() {
    455         ZenModeConfig configDefaultRules = new ZenModeConfig();
    456         appendDefaultRules(configDefaultRules); // "new" localized default rules
    457         for (String ruleId : mDefaultRuleIds) {
    458             AutomaticZenRule currRule = getAutomaticZenRule(ruleId);
    459             ZenRule defaultRule = configDefaultRules.automaticRules.get(ruleId);
    460             // if default rule wasn't customized, use localized name instead of previous
    461             if (ruleValuesEqual(currRule, defaultRule) &&
    462                     !defaultRule.name.equals(currRule.getName())) {
    463                 if (canManageAutomaticZenRule(defaultRule)) {
    464                     if (DEBUG) Slog.d(TAG, "Locale change - updating default zen rule name "
    465                             + "from " + currRule.getName() + " to " + defaultRule.name);
    466                     // update default rule (if locale changed, name of rule will change)
    467                     AutomaticZenRule defaultAutoRule = createAutomaticZenRule(defaultRule);
    468                     // ensure enabled state is carried over from current rule
    469                     defaultAutoRule.setEnabled(currRule.isEnabled());
    470                     updateAutomaticZenRule(ruleId, defaultAutoRule,
    471                             "locale changed");
    472                 }
    473             }
    474         }
    475     }
    476 
    477     private boolean isSystemRule(AutomaticZenRule rule) {
    478         return ZenModeConfig.SYSTEM_AUTHORITY.equals(rule.getOwner().getPackageName());
    479     }
    480 
    481     private ServiceInfo getServiceInfo(ComponentName owner) {
    482         Intent queryIntent = new Intent();
    483         queryIntent.setComponent(owner);
    484         List<ResolveInfo> installedServices = mPm.queryIntentServicesAsUser(
    485                 queryIntent,
    486                 PackageManager.GET_SERVICES | PackageManager.GET_META_DATA,
    487                 UserHandle.getCallingUserId());
    488         if (installedServices != null) {
    489             for (int i = 0, count = installedServices.size(); i < count; i++) {
    490                 ResolveInfo resolveInfo = installedServices.get(i);
    491                 ServiceInfo info = resolveInfo.serviceInfo;
    492                 if (mServiceConfig.bindPermission.equals(info.permission)) {
    493                     return info;
    494                 }
    495             }
    496         }
    497         return null;
    498     }
    499 
    500     private void populateZenRule(AutomaticZenRule automaticZenRule, ZenRule rule, boolean isNew) {
    501         if (isNew) {
    502             rule.id = ZenModeConfig.newRuleId();
    503             rule.creationTime = System.currentTimeMillis();
    504             rule.component = automaticZenRule.getOwner();
    505         }
    506 
    507         if (rule.enabled != automaticZenRule.isEnabled()) {
    508             rule.snoozing = false;
    509         }
    510         rule.name = automaticZenRule.getName();
    511         rule.condition = null;
    512         rule.conditionId = automaticZenRule.getConditionId();
    513         rule.enabled = automaticZenRule.isEnabled();
    514         rule.zenMode = NotificationManager.zenModeFromInterruptionFilter(
    515                 automaticZenRule.getInterruptionFilter(), Global.ZEN_MODE_OFF);
    516     }
    517 
    518     protected AutomaticZenRule createAutomaticZenRule(ZenRule rule) {
    519         return new AutomaticZenRule(rule.name, rule.component, rule.conditionId,
    520                 NotificationManager.zenModeToInterruptionFilter(rule.zenMode), rule.enabled,
    521                 rule.creationTime);
    522     }
    523 
    524     public void setManualZenMode(int zenMode, Uri conditionId, String caller, String reason) {
    525         setManualZenMode(zenMode, conditionId, reason, caller, true /*setRingerMode*/);
    526     }
    527 
    528     private void setManualZenMode(int zenMode, Uri conditionId, String reason, String caller,
    529             boolean setRingerMode) {
    530         ZenModeConfig newConfig;
    531         synchronized (mConfig) {
    532             if (mConfig == null) return;
    533             if (!Global.isValidZenMode(zenMode)) return;
    534             if (DEBUG) Log.d(TAG, "setManualZenMode " + Global.zenModeToString(zenMode)
    535                     + " conditionId=" + conditionId + " reason=" + reason
    536                     + " setRingerMode=" + setRingerMode);
    537             newConfig = mConfig.copy();
    538             if (zenMode == Global.ZEN_MODE_OFF) {
    539                 newConfig.manualRule = null;
    540                 for (ZenRule automaticRule : newConfig.automaticRules.values()) {
    541                     if (automaticRule.isAutomaticActive()) {
    542                         automaticRule.snoozing = true;
    543                     }
    544                 }
    545             } else {
    546                 final ZenRule newRule = new ZenRule();
    547                 newRule.enabled = true;
    548                 newRule.zenMode = zenMode;
    549                 newRule.conditionId = conditionId;
    550                 newRule.enabler = caller;
    551                 newConfig.manualRule = newRule;
    552             }
    553             setConfigLocked(newConfig, reason, setRingerMode);
    554         }
    555     }
    556 
    557     void dump(ProtoOutputStream proto) {
    558 
    559         proto.write(ZenModeProto.ZEN_MODE, mZenMode);
    560         synchronized (mConfig) {
    561             if (mConfig.manualRule != null) {
    562                 proto.write(ZenModeProto.ENABLED_ACTIVE_CONDITIONS, mConfig.manualRule.toString());
    563             }
    564             for (ZenRule rule : mConfig.automaticRules.values()) {
    565                 if (rule.enabled && rule.condition.state == Condition.STATE_TRUE
    566                         && !rule.snoozing) {
    567                     proto.write(ZenModeProto.ENABLED_ACTIVE_CONDITIONS, rule.toString());
    568                 }
    569             }
    570             proto.write(ZenModeProto.POLICY, mConfig.toNotificationPolicy().toString());
    571             proto.write(ZenModeProto.SUPPRESSED_EFFECTS, mSuppressedEffects);
    572         }
    573     }
    574 
    575     public void dump(PrintWriter pw, String prefix) {
    576         pw.print(prefix); pw.print("mZenMode=");
    577         pw.println(Global.zenModeToString(mZenMode));
    578         final int N = mConfigs.size();
    579         for (int i = 0; i < N; i++) {
    580             dump(pw, prefix, "mConfigs[u=" + mConfigs.keyAt(i) + "]", mConfigs.valueAt(i));
    581         }
    582         pw.print(prefix); pw.print("mUser="); pw.println(mUser);
    583         synchronized (mConfig) {
    584             dump(pw, prefix, "mConfig", mConfig);
    585         }
    586 
    587         pw.print(prefix); pw.print("mSuppressedEffects="); pw.println(mSuppressedEffects);
    588         mFiltering.dump(pw, prefix);
    589         mConditions.dump(pw, prefix);
    590     }
    591 
    592     private static void dump(PrintWriter pw, String prefix, String var, ZenModeConfig config) {
    593         pw.print(prefix); pw.print(var); pw.print('=');
    594         if (config == null) {
    595             pw.println(config);
    596             return;
    597         }
    598         pw.printf("allow(calls=%b,callsFrom=%s,repeatCallers=%b,messages=%b,messagesFrom=%s,"
    599                 + "events=%b,reminders=%b,whenScreenOff=%b,whenScreenOn=%b)\n",
    600                 config.allowCalls, ZenModeConfig.sourceToString(config.allowCallsFrom),
    601                 config.allowRepeatCallers, config.allowMessages,
    602                 ZenModeConfig.sourceToString(config.allowMessagesFrom),
    603                 config.allowEvents, config.allowReminders, config.allowWhenScreenOff,
    604                 config.allowWhenScreenOn);
    605         pw.print(prefix); pw.print("  manualRule="); pw.println(config.manualRule);
    606         if (config.automaticRules.isEmpty()) return;
    607         final int N = config.automaticRules.size();
    608         for (int i = 0; i < N; i++) {
    609             pw.print(prefix); pw.print(i == 0 ? "  automaticRules=" : "                 ");
    610             pw.println(config.automaticRules.valueAt(i));
    611         }
    612     }
    613 
    614     public void readXml(XmlPullParser parser, boolean forRestore)
    615             throws XmlPullParserException, IOException {
    616         final ZenModeConfig config = ZenModeConfig.readXml(parser);
    617         if (config != null) {
    618             if (forRestore) {
    619                 //TODO: http://b/22388012
    620                 if (config.user != UserHandle.USER_SYSTEM) {
    621                     return;
    622                 }
    623                 config.manualRule = null;  // don't restore the manual rule
    624                 long time = System.currentTimeMillis();
    625                 if (config.automaticRules != null) {
    626                     for (ZenRule automaticRule : config.automaticRules.values()) {
    627                         // don't restore transient state from restored automatic rules
    628                         automaticRule.snoozing = false;
    629                         automaticRule.condition = null;
    630                         automaticRule.creationTime = time;
    631                     }
    632                 }
    633             }
    634             if (DEBUG) Log.d(TAG, "readXml");
    635             synchronized (mConfig) {
    636                 setConfigLocked(config, "readXml");
    637             }
    638         }
    639     }
    640 
    641     public void writeXml(XmlSerializer out, boolean forBackup) throws IOException {
    642         final int N = mConfigs.size();
    643         for (int i = 0; i < N; i++) {
    644             //TODO: http://b/22388012
    645             if (forBackup && mConfigs.keyAt(i) != UserHandle.USER_SYSTEM) {
    646                 continue;
    647             }
    648             mConfigs.valueAt(i).writeXml(out);
    649         }
    650     }
    651 
    652     public Policy getNotificationPolicy() {
    653         return getNotificationPolicy(mConfig);
    654     }
    655 
    656     private static Policy getNotificationPolicy(ZenModeConfig config) {
    657         return config == null ? null : config.toNotificationPolicy();
    658     }
    659 
    660     public void setNotificationPolicy(Policy policy) {
    661         if (policy == null || mConfig == null) return;
    662         synchronized (mConfig) {
    663             final ZenModeConfig newConfig = mConfig.copy();
    664             newConfig.applyNotificationPolicy(policy);
    665             setConfigLocked(newConfig, "setNotificationPolicy");
    666         }
    667     }
    668 
    669     /**
    670      * Removes old rule instances whose owner is not installed.
    671      */
    672     private void cleanUpZenRules() {
    673         long currentTime = System.currentTimeMillis();
    674         synchronized (mConfig) {
    675             final ZenModeConfig newConfig = mConfig.copy();
    676             if (newConfig.automaticRules != null) {
    677                 for (int i = newConfig.automaticRules.size() - 1; i >= 0; i--) {
    678                     ZenRule rule = newConfig.automaticRules.get(newConfig.automaticRules.keyAt(i));
    679                     if (RULE_INSTANCE_GRACE_PERIOD < (currentTime - rule.creationTime)) {
    680                         try {
    681                             mPm.getPackageInfo(rule.component.getPackageName(),
    682                                     PackageManager.MATCH_ANY_USER);
    683                         } catch (PackageManager.NameNotFoundException e) {
    684                             newConfig.automaticRules.removeAt(i);
    685                         }
    686                     }
    687                 }
    688             }
    689             setConfigLocked(newConfig, "cleanUpZenRules");
    690         }
    691     }
    692 
    693     /**
    694      * @return a copy of the zen mode configuration
    695      */
    696     public ZenModeConfig getConfig() {
    697         synchronized (mConfig) {
    698             return mConfig.copy();
    699         }
    700     }
    701 
    702     public boolean setConfigLocked(ZenModeConfig config, String reason) {
    703         return setConfigLocked(config, reason, true /*setRingerMode*/);
    704     }
    705 
    706     public void setConfig(ZenModeConfig config, String reason) {
    707         synchronized (mConfig) {
    708             setConfigLocked(config, reason);
    709         }
    710     }
    711 
    712     private boolean setConfigLocked(ZenModeConfig config, String reason, boolean setRingerMode) {
    713         final long identity = Binder.clearCallingIdentity();
    714         try {
    715             if (config == null || !config.isValid()) {
    716                 Log.w(TAG, "Invalid config in setConfigLocked; " + config);
    717                 return false;
    718             }
    719             if (config.user != mUser) {
    720                 // simply store away for background users
    721                 mConfigs.put(config.user, config);
    722                 if (DEBUG) Log.d(TAG, "setConfigLocked: store config for user " + config.user);
    723                 return true;
    724             }
    725             mConditions.evaluateConfig(config, false /*processSubscriptions*/);  // may modify config
    726             mConfigs.put(config.user, config);
    727             if (DEBUG) Log.d(TAG, "setConfigLocked reason=" + reason, new Throwable());
    728             ZenLog.traceConfig(reason, mConfig, config);
    729             final boolean policyChanged = !Objects.equals(getNotificationPolicy(mConfig),
    730                     getNotificationPolicy(config));
    731             if (!config.equals(mConfig)) {
    732                 dispatchOnConfigChanged();
    733             }
    734             if (policyChanged) {
    735                 dispatchOnPolicyChanged();
    736             }
    737             mConfig = config;
    738             mHandler.postApplyConfig(config, reason, setRingerMode);
    739             return true;
    740         } catch (SecurityException e) {
    741             Log.wtf(TAG, "Invalid rule in config", e);
    742             return false;
    743         } finally {
    744             Binder.restoreCallingIdentity(identity);
    745         }
    746     }
    747 
    748     private void applyConfig(ZenModeConfig config, String reason, boolean setRingerMode) {
    749         final String val = Integer.toString(config.hashCode());
    750         Global.putString(mContext.getContentResolver(), Global.ZEN_MODE_CONFIG_ETAG, val);
    751         if (!evaluateZenMode(reason, setRingerMode)) {
    752             applyRestrictions();  // evaluateZenMode will also apply restrictions if changed
    753         }
    754         mConditions.evaluateConfig(config, true /*processSubscriptions*/);
    755     }
    756 
    757     private int getZenModeSetting() {
    758         return Global.getInt(mContext.getContentResolver(), Global.ZEN_MODE, Global.ZEN_MODE_OFF);
    759     }
    760 
    761     private void setZenModeSetting(int zen) {
    762         Global.putInt(mContext.getContentResolver(), Global.ZEN_MODE, zen);
    763     }
    764 
    765     private int getPreviousRingerModeSetting() {
    766         return Global.getInt(mContext.getContentResolver(),
    767                 Global.ZEN_MODE_RINGER_LEVEL, AudioManager.RINGER_MODE_NORMAL);
    768     }
    769 
    770     private void setPreviousRingerModeSetting(Integer previousRingerLevel) {
    771         Global.putString(
    772                 mContext.getContentResolver(), Global.ZEN_MODE_RINGER_LEVEL,
    773                 previousRingerLevel == null ? null : Integer.toString(previousRingerLevel));
    774     }
    775 
    776     private boolean evaluateZenMode(String reason, boolean setRingerMode) {
    777         if (DEBUG) Log.d(TAG, "evaluateZenMode");
    778         final int zenBefore = mZenMode;
    779         final int zen = computeZenMode();
    780         ZenLog.traceSetZenMode(zen, reason);
    781         mZenMode = zen;
    782         updateRingerModeAffectedStreams();
    783         setZenModeSetting(mZenMode);
    784         if (setRingerMode) {
    785             applyZenToRingerMode();
    786         }
    787         applyRestrictions();
    788         if (zen != zenBefore) {
    789             mHandler.postDispatchOnZenModeChanged();
    790         }
    791         return true;
    792     }
    793 
    794     private void updateRingerModeAffectedStreams() {
    795         if (mAudioManager != null) {
    796             mAudioManager.updateRingerModeAffectedStreamsInternal();
    797         }
    798     }
    799 
    800     private int computeZenMode() {
    801         synchronized (mConfig) {
    802             if (mConfig == null) return Global.ZEN_MODE_OFF;
    803             if (mConfig.manualRule != null) return mConfig.manualRule.zenMode;
    804             int zen = Global.ZEN_MODE_OFF;
    805             for (ZenRule automaticRule : mConfig.automaticRules.values()) {
    806                 if (automaticRule.isAutomaticActive()) {
    807                     if (zenSeverity(automaticRule.zenMode) > zenSeverity(zen)) {
    808                         zen = automaticRule.zenMode;
    809                     }
    810                 }
    811             }
    812             return zen;
    813         }
    814     }
    815 
    816     private void applyRestrictions() {
    817         final boolean zen = mZenMode != Global.ZEN_MODE_OFF;
    818 
    819         // notification restrictions
    820         final boolean muteNotifications =
    821                 (mSuppressedEffects & SUPPRESSED_EFFECT_NOTIFICATIONS) != 0;
    822         // call restrictions
    823         final boolean muteCalls = zen && !mConfig.allowCalls && !mConfig.allowRepeatCallers
    824                 || (mSuppressedEffects & SUPPRESSED_EFFECT_CALLS) != 0;
    825         // total silence restrictions
    826         final boolean muteEverything = mZenMode == Global.ZEN_MODE_NO_INTERRUPTIONS;
    827 
    828         for (int usage : AudioAttributes.SDK_USAGES) {
    829             final int suppressionBehavior = AudioAttributes.SUPPRESSIBLE_USAGES.get(usage);
    830             if (suppressionBehavior == AudioAttributes.SUPPRESSIBLE_NEVER) {
    831                 applyRestrictions(false /*mute*/, usage);
    832             } else if (suppressionBehavior == AudioAttributes.SUPPRESSIBLE_NOTIFICATION) {
    833                 applyRestrictions(muteNotifications || muteEverything, usage);
    834             } else if (suppressionBehavior == AudioAttributes.SUPPRESSIBLE_CALL) {
    835                 applyRestrictions(muteCalls || muteEverything, usage);
    836             } else {
    837                 applyRestrictions(muteEverything, usage);
    838             }
    839         }
    840     }
    841 
    842     private void applyRestrictions(boolean mute, int usage) {
    843         final String[] exceptionPackages = null; // none (for now)
    844         mAppOps.setRestriction(AppOpsManager.OP_VIBRATE, usage,
    845                 mute ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
    846                 exceptionPackages);
    847         mAppOps.setRestriction(AppOpsManager.OP_PLAY_AUDIO, usage,
    848                 mute ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
    849                 exceptionPackages);
    850     }
    851 
    852     private void applyZenToRingerMode() {
    853         if (mAudioManager == null) return;
    854         // force the ringer mode into compliance
    855         final int ringerModeInternal = mAudioManager.getRingerModeInternal();
    856         int newRingerModeInternal = ringerModeInternal;
    857         switch (mZenMode) {
    858             case Global.ZEN_MODE_NO_INTERRUPTIONS:
    859             case Global.ZEN_MODE_ALARMS:
    860                 if (ringerModeInternal != AudioManager.RINGER_MODE_SILENT) {
    861                     setPreviousRingerModeSetting(ringerModeInternal);
    862                     newRingerModeInternal = AudioManager.RINGER_MODE_SILENT;
    863                 }
    864                 break;
    865             case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS:
    866             case Global.ZEN_MODE_OFF:
    867                 if (ringerModeInternal == AudioManager.RINGER_MODE_SILENT) {
    868                     newRingerModeInternal = getPreviousRingerModeSetting();
    869                     setPreviousRingerModeSetting(null);
    870                 }
    871                 break;
    872         }
    873         if (newRingerModeInternal != -1) {
    874             mAudioManager.setRingerModeInternal(newRingerModeInternal, TAG);
    875         }
    876     }
    877 
    878     private void dispatchOnConfigChanged() {
    879         for (Callback callback : mCallbacks) {
    880             callback.onConfigChanged();
    881         }
    882     }
    883 
    884     private void dispatchOnPolicyChanged() {
    885         for (Callback callback : mCallbacks) {
    886             callback.onPolicyChanged();
    887         }
    888     }
    889 
    890     private void dispatchOnZenModeChanged() {
    891         for (Callback callback : mCallbacks) {
    892             callback.onZenModeChanged();
    893         }
    894     }
    895 
    896     private ZenModeConfig readDefaultConfig(Resources resources) {
    897         XmlResourceParser parser = null;
    898         try {
    899             parser = resources.getXml(R.xml.default_zen_mode_config);
    900             while (parser.next() != XmlPullParser.END_DOCUMENT) {
    901                 final ZenModeConfig config = ZenModeConfig.readXml(parser);
    902                 if (config != null) return config;
    903             }
    904         } catch (Exception e) {
    905             Log.w(TAG, "Error reading default zen mode config from resource", e);
    906         } finally {
    907             IoUtils.closeQuietly(parser);
    908         }
    909         return new ZenModeConfig();
    910     }
    911 
    912     private void appendDefaultScheduleRules(ZenModeConfig config) {
    913         if (config == null) return;
    914 
    915         final ScheduleInfo weeknights = new ScheduleInfo();
    916         weeknights.days = ZenModeConfig.WEEKNIGHT_DAYS;
    917         weeknights.startHour = 22;
    918         weeknights.endHour = 7;
    919         final ZenRule rule1 = new ZenRule();
    920         rule1.enabled = false;
    921         rule1.name = mDefaultRuleWeeknightsName;
    922         rule1.conditionId = ZenModeConfig.toScheduleConditionId(weeknights);
    923         rule1.zenMode = Global.ZEN_MODE_ALARMS;
    924         rule1.component = ScheduleConditionProvider.COMPONENT;
    925         rule1.id = SCHEDULED_DEFAULT_RULE_1;
    926         rule1.creationTime = System.currentTimeMillis();
    927         config.automaticRules.put(rule1.id, rule1);
    928 
    929         final ScheduleInfo weekends = new ScheduleInfo();
    930         weekends.days = ZenModeConfig.WEEKEND_DAYS;
    931         weekends.startHour = 23;
    932         weekends.startMinute = 30;
    933         weekends.endHour = 10;
    934         final ZenRule rule2 = new ZenRule();
    935         rule2.enabled = false;
    936         rule2.name = mDefaultRuleWeekendsName;
    937         rule2.conditionId = ZenModeConfig.toScheduleConditionId(weekends);
    938         rule2.zenMode = Global.ZEN_MODE_ALARMS;
    939         rule2.component = ScheduleConditionProvider.COMPONENT;
    940         rule2.id = SCHEDULED_DEFAULT_RULE_2;
    941         rule2.creationTime = System.currentTimeMillis();
    942         config.automaticRules.put(rule2.id, rule2);
    943     }
    944 
    945     private void appendDefaultEventRules(ZenModeConfig config) {
    946         if (config == null) return;
    947 
    948         final EventInfo events = new EventInfo();
    949         events.calendar = null; // any calendar
    950         events.reply = EventInfo.REPLY_YES_OR_MAYBE;
    951         final ZenRule rule = new ZenRule();
    952         rule.enabled = false;
    953         rule.name = mDefaultRuleEventsName;
    954         rule.conditionId = ZenModeConfig.toEventConditionId(events);
    955         rule.zenMode = Global.ZEN_MODE_ALARMS;
    956         rule.component = EventConditionProvider.COMPONENT;
    957         rule.id = EVENTS_DEFAULT_RULE;
    958         rule.creationTime = System.currentTimeMillis();
    959         config.automaticRules.put(rule.id, rule);
    960     }
    961 
    962     private static int zenSeverity(int zen) {
    963         switch (zen) {
    964             case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS: return 1;
    965             case Global.ZEN_MODE_ALARMS: return 2;
    966             case Global.ZEN_MODE_NO_INTERRUPTIONS: return 3;
    967             default: return 0;
    968         }
    969     }
    970 
    971     private final class RingerModeDelegate implements AudioManagerInternal.RingerModeDelegate {
    972         @Override
    973         public String toString() {
    974             return TAG;
    975         }
    976 
    977         @Override
    978         public int onSetRingerModeInternal(int ringerModeOld, int ringerModeNew, String caller,
    979                 int ringerModeExternal, VolumePolicy policy) {
    980             final boolean isChange = ringerModeOld != ringerModeNew;
    981 
    982             int ringerModeExternalOut = ringerModeNew;
    983 
    984             int newZen = -1;
    985             switch (ringerModeNew) {
    986                 case AudioManager.RINGER_MODE_SILENT:
    987                     if (isChange && policy.doNotDisturbWhenSilent) {
    988                         if (mZenMode != Global.ZEN_MODE_NO_INTERRUPTIONS
    989                                 && mZenMode != Global.ZEN_MODE_ALARMS) {
    990                             newZen = Global.ZEN_MODE_ALARMS;
    991                         }
    992                         setPreviousRingerModeSetting(ringerModeOld);
    993                     }
    994                     break;
    995                 case AudioManager.RINGER_MODE_VIBRATE:
    996                 case AudioManager.RINGER_MODE_NORMAL:
    997                     if (isChange && ringerModeOld == AudioManager.RINGER_MODE_SILENT
    998                             && (mZenMode == Global.ZEN_MODE_NO_INTERRUPTIONS
    999                                     || mZenMode == Global.ZEN_MODE_ALARMS)) {
   1000                         newZen = Global.ZEN_MODE_OFF;
   1001                     } else if (mZenMode != Global.ZEN_MODE_OFF) {
   1002                         ringerModeExternalOut = AudioManager.RINGER_MODE_SILENT;
   1003                     }
   1004                     break;
   1005             }
   1006             if (newZen != -1) {
   1007                 setManualZenMode(newZen, null, "ringerModeInternal", null,
   1008                         false /*setRingerMode*/);
   1009             }
   1010 
   1011             if (isChange || newZen != -1 || ringerModeExternal != ringerModeExternalOut) {
   1012                 ZenLog.traceSetRingerModeInternal(ringerModeOld, ringerModeNew, caller,
   1013                         ringerModeExternal, ringerModeExternalOut);
   1014             }
   1015             return ringerModeExternalOut;
   1016         }
   1017 
   1018         @Override
   1019         public int onSetRingerModeExternal(int ringerModeOld, int ringerModeNew, String caller,
   1020                 int ringerModeInternal, VolumePolicy policy) {
   1021             int ringerModeInternalOut = ringerModeNew;
   1022             final boolean isChange = ringerModeOld != ringerModeNew;
   1023             final boolean isVibrate = ringerModeInternal == AudioManager.RINGER_MODE_VIBRATE;
   1024 
   1025             int newZen = -1;
   1026             switch (ringerModeNew) {
   1027                 case AudioManager.RINGER_MODE_SILENT:
   1028                     if (isChange) {
   1029                         if (mZenMode == Global.ZEN_MODE_OFF) {
   1030                             newZen = Global.ZEN_MODE_ALARMS;
   1031                         }
   1032                         ringerModeInternalOut = isVibrate ? AudioManager.RINGER_MODE_VIBRATE
   1033                                 : AudioManager.RINGER_MODE_SILENT;
   1034                     } else {
   1035                         ringerModeInternalOut = ringerModeInternal;
   1036                     }
   1037                     break;
   1038                 case AudioManager.RINGER_MODE_VIBRATE:
   1039                 case AudioManager.RINGER_MODE_NORMAL:
   1040                     if (mZenMode != Global.ZEN_MODE_OFF) {
   1041                         newZen = Global.ZEN_MODE_OFF;
   1042                     }
   1043                     break;
   1044             }
   1045             if (newZen != -1) {
   1046                 setManualZenMode(newZen, null, "ringerModeExternal", caller,
   1047                         false /*setRingerMode*/);
   1048             }
   1049 
   1050             ZenLog.traceSetRingerModeExternal(ringerModeOld, ringerModeNew, caller,
   1051                     ringerModeInternal, ringerModeInternalOut);
   1052             return ringerModeInternalOut;
   1053         }
   1054 
   1055         @Override
   1056         public boolean canVolumeDownEnterSilent() {
   1057             return mZenMode == Global.ZEN_MODE_OFF;
   1058         }
   1059 
   1060         @Override
   1061         public int getRingerModeAffectedStreams(int streams) {
   1062             // ringtone, notification and system streams are always affected by ringer mode
   1063             streams |= (1 << AudioSystem.STREAM_RING) |
   1064                        (1 << AudioSystem.STREAM_NOTIFICATION) |
   1065                        (1 << AudioSystem.STREAM_SYSTEM);
   1066 
   1067             // alarm and music streams are only affected by ringer mode when in total silence
   1068             if (mZenMode == Global.ZEN_MODE_NO_INTERRUPTIONS) {
   1069                 streams |= (1 << AudioSystem.STREAM_ALARM) |
   1070                            (1 << AudioSystem.STREAM_MUSIC);
   1071             } else {
   1072                 streams &= ~((1 << AudioSystem.STREAM_ALARM) |
   1073                              (1 << AudioSystem.STREAM_MUSIC));
   1074             }
   1075             return streams;
   1076         }
   1077     }
   1078 
   1079     private final class SettingsObserver extends ContentObserver {
   1080         private final Uri ZEN_MODE = Global.getUriFor(Global.ZEN_MODE);
   1081 
   1082         public SettingsObserver(Handler handler) {
   1083             super(handler);
   1084         }
   1085 
   1086         public void observe() {
   1087             final ContentResolver resolver = mContext.getContentResolver();
   1088             resolver.registerContentObserver(ZEN_MODE, false /*notifyForDescendents*/, this);
   1089             update(null);
   1090         }
   1091 
   1092         @Override
   1093         public void onChange(boolean selfChange, Uri uri) {
   1094             update(uri);
   1095         }
   1096 
   1097         public void update(Uri uri) {
   1098             if (ZEN_MODE.equals(uri)) {
   1099                 if (mZenMode != getZenModeSetting()) {
   1100                     if (DEBUG) Log.d(TAG, "Fixing zen mode setting");
   1101                     setZenModeSetting(mZenMode);
   1102                 }
   1103             }
   1104         }
   1105     }
   1106 
   1107     private final class Metrics extends Callback {
   1108         private static final String COUNTER_PREFIX = "dnd_mode_";
   1109         private static final long MINIMUM_LOG_PERIOD_MS = 60 * 1000;
   1110 
   1111         private int mPreviousZenMode = -1;
   1112         private long mBeginningMs = 0L;
   1113 
   1114         @Override
   1115         void onZenModeChanged() {
   1116             emit();
   1117         }
   1118 
   1119         private void emit() {
   1120             mHandler.postMetricsTimer();
   1121             final long now = SystemClock.elapsedRealtime();
   1122             final long since = (now - mBeginningMs);
   1123             if (mPreviousZenMode != mZenMode || since > MINIMUM_LOG_PERIOD_MS) {
   1124                 if (mPreviousZenMode != -1) {
   1125                     MetricsLogger.count(mContext, COUNTER_PREFIX + mPreviousZenMode, (int) since);
   1126                 }
   1127                 mPreviousZenMode = mZenMode;
   1128                 mBeginningMs = now;
   1129             }
   1130         }
   1131     }
   1132 
   1133     private final class H extends Handler {
   1134         private static final int MSG_DISPATCH = 1;
   1135         private static final int MSG_METRICS = 2;
   1136         private static final int MSG_APPLY_CONFIG = 4;
   1137 
   1138         private final class ConfigMessageData {
   1139             public final ZenModeConfig config;
   1140             public final String reason;
   1141             public final boolean setRingerMode;
   1142 
   1143             ConfigMessageData(ZenModeConfig config, String reason, boolean setRingerMode) {
   1144                 this.config = config;
   1145                 this.reason = reason;
   1146                 this.setRingerMode = setRingerMode;
   1147             }
   1148         }
   1149 
   1150         private static final long METRICS_PERIOD_MS = 6 * 60 * 60 * 1000;
   1151 
   1152         private H(Looper looper) {
   1153             super(looper);
   1154         }
   1155 
   1156         private void postDispatchOnZenModeChanged() {
   1157             removeMessages(MSG_DISPATCH);
   1158             sendEmptyMessage(MSG_DISPATCH);
   1159         }
   1160 
   1161         private void postMetricsTimer() {
   1162             removeMessages(MSG_METRICS);
   1163             sendEmptyMessageDelayed(MSG_METRICS, METRICS_PERIOD_MS);
   1164         }
   1165 
   1166         private void postApplyConfig(ZenModeConfig config, String reason, boolean setRingerMode) {
   1167             sendMessage(obtainMessage(MSG_APPLY_CONFIG,
   1168                     new ConfigMessageData(config, reason, setRingerMode)));
   1169         }
   1170 
   1171         @Override
   1172         public void handleMessage(Message msg) {
   1173             switch (msg.what) {
   1174                 case MSG_DISPATCH:
   1175                     dispatchOnZenModeChanged();
   1176                     break;
   1177                 case MSG_METRICS:
   1178                     mMetrics.emit();
   1179                     break;
   1180                 case MSG_APPLY_CONFIG:
   1181                     ConfigMessageData applyConfigData = (ConfigMessageData) msg.obj;
   1182                     applyConfig(applyConfigData.config, applyConfigData.reason,
   1183                             applyConfigData.setRingerMode);
   1184             }
   1185         }
   1186     }
   1187 
   1188     public static class Callback {
   1189         void onConfigChanged() {}
   1190         void onZenModeChanged() {}
   1191         void onPolicyChanged() {}
   1192     }
   1193 
   1194 }
   1195