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 (!android.app.Flags.modesUi() && config.manualRule != null 72 && config.manualRule.condition != null 73 && !config.manualRule.isTrueOrUnknown()) { 74 if (DEBUG) Log.d(TAG, "evaluateConfig: clearing manual rule"); 75 config.manualRule = null; 76 } 77 final ArraySet<Uri> current = new ArraySet<>(); 78 evaluateRule(config.manualRule, current, null, processSubscriptions, true); 79 for (ZenRule automaticRule : config.automaticRules.values()) { 80 if (automaticRule.component != null) { 81 evaluateRule(automaticRule, current, trigger, processSubscriptions, false); 82 updateSnoozing(automaticRule); 83 } 84 } 85 86 synchronized (mSubscriptions) { 87 final int N = mSubscriptions.size(); 88 for (int i = N - 1; i >= 0; i--) { 89 final Uri id = mSubscriptions.keyAt(i); 90 final ComponentName component = mSubscriptions.valueAt(i); 91 if (processSubscriptions) { 92 if (!current.contains(id)) { 93 mConditionProviders.unsubscribeIfNecessary(component, id); 94 mSubscriptions.removeAt(i); 95 } 96 } 97 } 98 } 99 } 100 101 @Override onBootComplete()102 public void onBootComplete() { 103 // noop 104 } 105 106 @Override onUserSwitched()107 public void onUserSwitched() { 108 // noop 109 } 110 111 @Override onServiceAdded(ComponentName component)112 public void onServiceAdded(ComponentName component) { 113 if (DEBUG) Log.d(TAG, "onServiceAdded " + component); 114 final int callingUid = Binder.getCallingUid(); 115 mHelper.setConfig(mHelper.getConfig(), component, 116 callingUid == Process.SYSTEM_UID ? ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI 117 : ZenModeConfig.UPDATE_ORIGIN_APP, 118 "zmc.onServiceAdded:" + component, callingUid); 119 } 120 121 @Override onConditionChanged(Uri id, Condition condition)122 public void onConditionChanged(Uri id, Condition condition) { 123 if (DEBUG) Log.d(TAG, "onConditionChanged " + id + " " + condition); 124 ZenModeConfig config = mHelper.getConfig(); 125 if (config == null) return; 126 final int callingUid = Binder.getCallingUid(); 127 mHelper.setAutomaticZenRuleState(id, condition, 128 callingUid == Process.SYSTEM_UID ? ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI 129 : ZenModeConfig.UPDATE_ORIGIN_APP, 130 callingUid); 131 } 132 133 // Only valid for CPS backed rules evaluateRule(ZenRule rule, ArraySet<Uri> current, ComponentName trigger, boolean processSubscriptions, boolean isManual)134 private void evaluateRule(ZenRule rule, ArraySet<Uri> current, ComponentName trigger, 135 boolean processSubscriptions, boolean isManual) { 136 if (rule == null || rule.conditionId == null) return; 137 if (rule.configurationActivity != null) return; 138 final Uri id = rule.conditionId; 139 boolean isSystemCondition = false; 140 for (SystemConditionProviderService sp : mConditionProviders.getSystemProviders()) { 141 if (sp.isValidConditionId(id)) { 142 mConditionProviders.ensureRecordExists(sp.getComponent(), id, sp.asInterface()); 143 rule.component = sp.getComponent(); 144 isSystemCondition = true; 145 } 146 } 147 // ensure that we have a record of the rule if it's backed by an currently alive CPS 148 if (!isSystemCondition) { 149 final IConditionProvider cp = mConditionProviders.findConditionProvider(rule.component); 150 if (DEBUG) Log.d(TAG, "Ensure external rule exists: " + (cp != null) + " for " + id); 151 if (cp != null) { 152 mConditionProviders.ensureRecordExists(rule.component, id, cp); 153 } 154 } 155 // empty rule? disable and bail early 156 if (rule.component == null && rule.enabler == null) { 157 if (!android.app.Flags.modesUi() || (android.app.Flags.modesUi() && !isManual)) { 158 Log.w(TAG, "No component found for automatic rule: " + rule.conditionId); 159 rule.enabled = false; 160 } 161 return; 162 } 163 if (current != null) { 164 current.add(id); 165 } 166 167 // If the rule is bound by a CPS and the CPS is alive, tell them about the rule 168 if (processSubscriptions && ((trigger != null && trigger.equals(rule.component)) 169 || isSystemCondition)) { 170 if (DEBUG) Log.d(TAG, "Subscribing to " + rule.component); 171 if (mConditionProviders.subscribeIfNecessary(rule.component, rule.conditionId)) { 172 synchronized (mSubscriptions) { 173 mSubscriptions.put(rule.conditionId, rule.component); 174 } 175 } else { 176 rule.condition = null; 177 if (DEBUG) Log.d(TAG, "zmc failed to subscribe"); 178 } 179 } 180 // backfill the rule state from CPS backed components if it's missing 181 if (rule.component != null && rule.condition == null) { 182 rule.condition = mConditionProviders.findCondition(rule.component, rule.conditionId); 183 if (rule.condition != null && DEBUG) Log.d(TAG, "Found existing condition for: " 184 + rule.conditionId); 185 } 186 } 187 updateSnoozing(ZenRule rule)188 private boolean updateSnoozing(ZenRule rule) { 189 if (rule != null && rule.snoozing && !rule.isTrueOrUnknown()) { 190 rule.snoozing = false; 191 if (DEBUG) Log.d(TAG, "Snoozing reset for " + rule.conditionId); 192 return true; 193 } 194 return false; 195 } 196 } 197