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