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