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