| /** |
| * Copyright (c) 2015, The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.server.notification; |
| |
| import android.content.ComponentName; |
| import android.net.Uri; |
| import android.service.notification.Condition; |
| import android.service.notification.IConditionProvider; |
| import android.service.notification.ZenModeConfig; |
| import android.service.notification.ZenModeConfig.ZenRule; |
| import android.util.ArrayMap; |
| import android.util.ArraySet; |
| import android.util.Log; |
| |
| import com.android.internal.annotations.VisibleForTesting; |
| |
| import java.io.PrintWriter; |
| |
| /** |
| * Helper class for managing active rules from |
| * {@link android.service.notification.ConditionProviderService CPSes}. |
| */ |
| public class ZenModeConditions implements ConditionProviders.Callback { |
| private static final String TAG = ZenModeHelper.TAG; |
| private static final boolean DEBUG = ZenModeHelper.DEBUG; |
| |
| private final ZenModeHelper mHelper; |
| private final ConditionProviders mConditionProviders; |
| |
| @VisibleForTesting |
| protected final ArrayMap<Uri, ComponentName> mSubscriptions = new ArrayMap<>(); |
| |
| public ZenModeConditions(ZenModeHelper helper, ConditionProviders conditionProviders) { |
| mHelper = helper; |
| mConditionProviders = conditionProviders; |
| if (mConditionProviders.isSystemProviderEnabled(ZenModeConfig.COUNTDOWN_PATH)) { |
| mConditionProviders.addSystemProvider(new CountdownConditionProvider()); |
| } |
| if (mConditionProviders.isSystemProviderEnabled(ZenModeConfig.SCHEDULE_PATH)) { |
| mConditionProviders.addSystemProvider(new ScheduleConditionProvider()); |
| } |
| if (mConditionProviders.isSystemProviderEnabled(ZenModeConfig.EVENT_PATH)) { |
| mConditionProviders.addSystemProvider(new EventConditionProvider()); |
| } |
| mConditionProviders.setCallback(this); |
| } |
| |
| public void dump(PrintWriter pw, String prefix) { |
| pw.print(prefix); pw.print("mSubscriptions="); pw.println(mSubscriptions); |
| } |
| |
| public void evaluateConfig(ZenModeConfig config, ComponentName trigger, |
| boolean processSubscriptions) { |
| if (config == null) return; |
| if (config.manualRule != null && config.manualRule.condition != null |
| && !config.manualRule.isTrueOrUnknown()) { |
| if (DEBUG) Log.d(TAG, "evaluateConfig: clearing manual rule"); |
| config.manualRule = null; |
| } |
| final ArraySet<Uri> current = new ArraySet<>(); |
| evaluateRule(config.manualRule, current, null, processSubscriptions); |
| for (ZenRule automaticRule : config.automaticRules.values()) { |
| if (automaticRule.component != null) { |
| evaluateRule(automaticRule, current, trigger, processSubscriptions); |
| updateSnoozing(automaticRule); |
| } |
| } |
| |
| synchronized (mSubscriptions) { |
| final int N = mSubscriptions.size(); |
| for (int i = N - 1; i >= 0; i--) { |
| final Uri id = mSubscriptions.keyAt(i); |
| final ComponentName component = mSubscriptions.valueAt(i); |
| if (processSubscriptions) { |
| if (!current.contains(id)) { |
| mConditionProviders.unsubscribeIfNecessary(component, id); |
| mSubscriptions.removeAt(i); |
| } |
| } |
| } |
| } |
| } |
| |
| @Override |
| public void onBootComplete() { |
| // noop |
| } |
| |
| @Override |
| public void onUserSwitched() { |
| // noop |
| } |
| |
| @Override |
| public void onServiceAdded(ComponentName component) { |
| if (DEBUG) Log.d(TAG, "onServiceAdded " + component); |
| mHelper.setConfig(mHelper.getConfig(), component, "zmc.onServiceAdded"); |
| } |
| |
| @Override |
| public void onConditionChanged(Uri id, Condition condition) { |
| if (DEBUG) Log.d(TAG, "onConditionChanged " + id + " " + condition); |
| ZenModeConfig config = mHelper.getConfig(); |
| if (config == null) return; |
| mHelper.setAutomaticZenRuleState(id, condition); |
| } |
| |
| // Only valid for CPS backed rules |
| private void evaluateRule(ZenRule rule, ArraySet<Uri> current, ComponentName trigger, |
| boolean processSubscriptions) { |
| if (rule == null || rule.conditionId == null) return; |
| if (rule.configurationActivity != null) return; |
| final Uri id = rule.conditionId; |
| boolean isSystemCondition = false; |
| for (SystemConditionProviderService sp : mConditionProviders.getSystemProviders()) { |
| if (sp.isValidConditionId(id)) { |
| mConditionProviders.ensureRecordExists(sp.getComponent(), id, sp.asInterface()); |
| rule.component = sp.getComponent(); |
| isSystemCondition = true; |
| } |
| } |
| // ensure that we have a record of the rule if it's backed by an currently alive CPS |
| if (!isSystemCondition) { |
| final IConditionProvider cp = mConditionProviders.findConditionProvider(rule.component); |
| if (DEBUG) Log.d(TAG, "Ensure external rule exists: " + (cp != null) + " for " + id); |
| if (cp != null) { |
| mConditionProviders.ensureRecordExists(rule.component, id, cp); |
| } |
| } |
| // empty rule? disable and bail early |
| if (rule.component == null && rule.enabler == null) { |
| Log.w(TAG, "No component found for automatic rule: " + rule.conditionId); |
| rule.enabled = false; |
| return; |
| } |
| if (current != null) { |
| current.add(id); |
| } |
| |
| // If the rule is bound by a CPS and the CPS is alive, tell them about the rule |
| if (processSubscriptions && ((trigger != null && trigger.equals(rule.component)) |
| || isSystemCondition)) { |
| if (DEBUG) Log.d(TAG, "Subscribing to " + rule.component); |
| if (mConditionProviders.subscribeIfNecessary(rule.component, rule.conditionId)) { |
| synchronized (mSubscriptions) { |
| mSubscriptions.put(rule.conditionId, rule.component); |
| } |
| } else { |
| rule.condition = null; |
| if (DEBUG) Log.d(TAG, "zmc failed to subscribe"); |
| } |
| } |
| // backfill the rule state from CPS backed components if it's missing |
| if (rule.component != null && rule.condition == null) { |
| rule.condition = mConditionProviders.findCondition(rule.component, rule.conditionId); |
| if (rule.condition != null && DEBUG) Log.d(TAG, "Found existing condition for: " |
| + rule.conditionId); |
| } |
| } |
| |
| private boolean updateSnoozing(ZenRule rule) { |
| if (rule != null && rule.snoozing && !rule.isTrueOrUnknown()) { |
| rule.snoozing = false; |
| if (DEBUG) Log.d(TAG, "Snoozing reset for " + rule.conditionId); |
| return true; |
| } |
| return false; |
| } |
| } |