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