1/**
2 * Copyright (c) 2015, 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 android.content.ComponentName;
20import android.net.Uri;
21import android.service.notification.Condition;
22import android.service.notification.IConditionListener;
23import android.service.notification.IConditionProvider;
24import android.service.notification.ZenModeConfig;
25import android.service.notification.ZenModeConfig.ZenRule;
26import android.util.ArrayMap;
27import android.util.ArraySet;
28import android.util.Log;
29
30import java.io.PrintWriter;
31import java.util.Objects;
32
33public class ZenModeConditions implements ConditionProviders.Callback {
34    private static final String TAG = ZenModeHelper.TAG;
35    private static final boolean DEBUG = ZenModeHelper.DEBUG;
36
37    private final ZenModeHelper mHelper;
38    private final ConditionProviders mConditionProviders;
39    private final ArrayMap<Uri, ComponentName> mSubscriptions = new ArrayMap<>();
40
41    private boolean mFirstEvaluation = true;
42
43    public ZenModeConditions(ZenModeHelper helper, ConditionProviders conditionProviders) {
44        mHelper = helper;
45        mConditionProviders = conditionProviders;
46        if (mConditionProviders.isSystemProviderEnabled(ZenModeConfig.COUNTDOWN_PATH)) {
47            mConditionProviders.addSystemProvider(new CountdownConditionProvider());
48        }
49        if (mConditionProviders.isSystemProviderEnabled(ZenModeConfig.SCHEDULE_PATH)) {
50            mConditionProviders.addSystemProvider(new ScheduleConditionProvider());
51        }
52        if (mConditionProviders.isSystemProviderEnabled(ZenModeConfig.EVENT_PATH)) {
53            mConditionProviders.addSystemProvider(new EventConditionProvider());
54        }
55        mConditionProviders.setCallback(this);
56    }
57
58    public void dump(PrintWriter pw, String prefix) {
59        pw.print(prefix); pw.print("mSubscriptions="); pw.println(mSubscriptions);
60    }
61
62    public void evaluateConfig(ZenModeConfig config, boolean processSubscriptions) {
63        if (config == null) return;
64        if (config.manualRule != null && config.manualRule.condition != null
65                && !config.manualRule.isTrueOrUnknown()) {
66            if (DEBUG) Log.d(TAG, "evaluateConfig: clearing manual rule");
67            config.manualRule = null;
68        }
69        final ArraySet<Uri> current = new ArraySet<>();
70        evaluateRule(config.manualRule, current, processSubscriptions);
71        for (ZenRule automaticRule : config.automaticRules.values()) {
72            evaluateRule(automaticRule, current, processSubscriptions);
73            updateSnoozing(automaticRule);
74        }
75        final int N = mSubscriptions.size();
76        for (int i = N - 1; i >= 0; i--) {
77            final Uri id = mSubscriptions.keyAt(i);
78            final ComponentName component = mSubscriptions.valueAt(i);
79            if (processSubscriptions) {
80                if (!current.contains(id)) {
81                    mConditionProviders.unsubscribeIfNecessary(component, id);
82                    mSubscriptions.removeAt(i);
83                }
84            }
85        }
86        mFirstEvaluation = false;
87    }
88
89    @Override
90    public void onBootComplete() {
91        // noop
92    }
93
94    @Override
95    public void onUserSwitched() {
96        // noop
97    }
98
99    @Override
100    public void onServiceAdded(ComponentName component) {
101        if (DEBUG) Log.d(TAG, "onServiceAdded " + component);
102        mHelper.setConfigAsync(mHelper.getConfig(), "zmc.onServiceAdded");
103    }
104
105    @Override
106    public void onConditionChanged(Uri id, Condition condition) {
107        if (DEBUG) Log.d(TAG, "onConditionChanged " + id + " " + condition);
108        ZenModeConfig config = mHelper.getConfig();
109        if (config == null) return;
110        boolean updated = updateCondition(id, condition, config.manualRule);
111        for (ZenRule automaticRule : config.automaticRules.values()) {
112            updated |= updateCondition(id, condition, automaticRule);
113            updated |= updateSnoozing(automaticRule);
114        }
115        if (updated) {
116            mHelper.setConfigAsync(config, "conditionChanged");
117        }
118    }
119
120    private void evaluateRule(ZenRule rule, ArraySet<Uri> current, boolean processSubscriptions) {
121        if (rule == null || rule.conditionId == null) return;
122        final Uri id = rule.conditionId;
123        boolean isSystemCondition = false;
124        for (SystemConditionProviderService sp : mConditionProviders.getSystemProviders()) {
125            if (sp.isValidConditionId(id)) {
126                mConditionProviders.ensureRecordExists(sp.getComponent(), id, sp.asInterface());
127                rule.component = sp.getComponent();
128                isSystemCondition = true;
129            }
130        }
131        if (!isSystemCondition) {
132            final IConditionProvider cp = mConditionProviders.findConditionProvider(rule.component);
133            if (DEBUG) Log.d(TAG, "Ensure external rule exists: " + (cp != null) + " for " + id);
134            if (cp != null) {
135                mConditionProviders.ensureRecordExists(rule.component, id, cp);
136            }
137        }
138        if (rule.component == null) {
139            Log.w(TAG, "No component found for automatic rule: " + rule.conditionId);
140            rule.enabled = false;
141            return;
142        }
143        if (current != null) {
144            current.add(id);
145        }
146        if (processSubscriptions) {
147            if (mConditionProviders.subscribeIfNecessary(rule.component, rule.conditionId)) {
148                mSubscriptions.put(rule.conditionId, rule.component);
149            } else {
150                if (DEBUG) Log.d(TAG, "zmc failed to subscribe");
151            }
152        }
153        if (rule.condition == null) {
154            rule.condition = mConditionProviders.findCondition(rule.component, rule.conditionId);
155            if (rule.condition != null && DEBUG) Log.d(TAG, "Found existing condition for: "
156                    + rule.conditionId);
157        }
158    }
159
160    private boolean isAutomaticActive(ComponentName component) {
161        if (component == null) return false;
162        final ZenModeConfig config = mHelper.getConfig();
163        if (config == null) return false;
164        for (ZenRule rule : config.automaticRules.values()) {
165            if (component.equals(rule.component) && rule.isAutomaticActive()) {
166                return true;
167            }
168        }
169        return false;
170    }
171
172    private boolean updateSnoozing(ZenRule rule) {
173        if (rule != null && rule.snoozing && (mFirstEvaluation || !rule.isTrueOrUnknown())) {
174            rule.snoozing = false;
175            if (DEBUG) Log.d(TAG, "Snoozing reset for " + rule.conditionId);
176            return true;
177        }
178        return false;
179    }
180
181    private boolean updateCondition(Uri id, Condition condition, ZenRule rule) {
182        if (id == null || rule == null || rule.conditionId == null) return false;
183        if (!rule.conditionId.equals(id)) return false;
184        if (Objects.equals(condition, rule.condition)) return false;
185        rule.condition = condition;
186        return true;
187    }
188
189}
190