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 17 package com.android.server.notification; 18 19 import android.content.ComponentName; 20 import android.net.Uri; 21 import android.service.notification.Condition; 22 import android.service.notification.IConditionListener; 23 import android.service.notification.IConditionProvider; 24 import android.service.notification.ZenModeConfig; 25 import android.service.notification.ZenModeConfig.ZenRule; 26 import android.util.ArrayMap; 27 import android.util.ArraySet; 28 import android.util.Log; 29 30 import java.io.PrintWriter; 31 import java.util.Objects; 32 33 public 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 rule.condition = null; 151 if (DEBUG) Log.d(TAG, "zmc failed to subscribe"); 152 } 153 } 154 if (rule.condition == null) { 155 rule.condition = mConditionProviders.findCondition(rule.component, rule.conditionId); 156 if (rule.condition != null && DEBUG) Log.d(TAG, "Found existing condition for: " 157 + rule.conditionId); 158 } 159 } 160 161 private boolean isAutomaticActive(ComponentName component) { 162 if (component == null) return false; 163 final ZenModeConfig config = mHelper.getConfig(); 164 if (config == null) return false; 165 for (ZenRule rule : config.automaticRules.values()) { 166 if (component.equals(rule.component) && rule.isAutomaticActive()) { 167 return true; 168 } 169 } 170 return false; 171 } 172 173 private boolean updateSnoozing(ZenRule rule) { 174 if (rule != null && rule.snoozing && (mFirstEvaluation || !rule.isTrueOrUnknown())) { 175 rule.snoozing = false; 176 if (DEBUG) Log.d(TAG, "Snoozing reset for " + rule.conditionId); 177 return true; 178 } 179 return false; 180 } 181 182 private boolean updateCondition(Uri id, Condition condition, ZenRule rule) { 183 if (id == null || rule == null || rule.conditionId == null) return false; 184 if (!rule.conditionId.equals(id)) return false; 185 if (Objects.equals(condition, rule.condition)) return false; 186 rule.condition = condition; 187 return true; 188 } 189 190 } 191