• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.os.UserHandle;
24 import android.service.notification.Condition;
25 import android.service.notification.IConditionProvider;
26 import android.service.notification.ZenModeConfig;
27 import android.service.notification.ZenModeConfig.ZenRule;
28 import android.util.ArrayMap;
29 import android.util.ArraySet;
30 import android.util.Log;
31 
32 import com.android.internal.annotations.VisibleForTesting;
33 
34 import java.io.PrintWriter;
35 
36 /**
37  * Helper class for managing active rules from
38  * {@link android.service.notification.ConditionProviderService CPSes}.
39  */
40 public class ZenModeConditions implements ConditionProviders.Callback {
41     private static final String TAG = ZenModeHelper.TAG;
42     private static final boolean DEBUG = ZenModeHelper.DEBUG;
43 
44     private final ZenModeHelper mHelper;
45     private final ConditionProviders mConditionProviders;
46 
47     @VisibleForTesting
48     protected final ArrayMap<Uri, ComponentName> mSubscriptions = new ArrayMap<>();
49 
ZenModeConditions(ZenModeHelper helper, ConditionProviders conditionProviders)50     public ZenModeConditions(ZenModeHelper helper, ConditionProviders conditionProviders) {
51         mHelper = helper;
52         mConditionProviders = conditionProviders;
53         if (mConditionProviders.isSystemProviderEnabled(ZenModeConfig.COUNTDOWN_PATH)) {
54             mConditionProviders.addSystemProvider(new CountdownConditionProvider());
55         }
56         if (mConditionProviders.isSystemProviderEnabled(ZenModeConfig.SCHEDULE_PATH)) {
57             mConditionProviders.addSystemProvider(new ScheduleConditionProvider());
58         }
59         if (mConditionProviders.isSystemProviderEnabled(ZenModeConfig.EVENT_PATH)) {
60             mConditionProviders.addSystemProvider(new EventConditionProvider());
61         }
62         if (mConditionProviders.isSystemProviderEnabled(ZenModeConfig.CUSTOM_MANUAL_PATH)) {
63             mConditionProviders.addSystemProvider(new CustomManualConditionProvider());
64         }
65         mConditionProviders.setCallback(this);
66     }
67 
dump(PrintWriter pw, String prefix)68     public void dump(PrintWriter pw, String prefix) {
69         pw.print(prefix); pw.print("mSubscriptions="); pw.println(mSubscriptions);
70     }
71 
evaluateConfig(ZenModeConfig config, ComponentName trigger, boolean processSubscriptions)72     public void evaluateConfig(ZenModeConfig config, ComponentName trigger,
73             boolean processSubscriptions) {
74         if (config == null) return;
75         if (!android.app.Flags.modesUi() && config.manualRule != null
76                 && config.manualRule.condition != null
77                 && !config.manualRule.isTrueOrUnknown()) {
78             if (DEBUG) Log.d(TAG, "evaluateConfig: clearing manual rule");
79             config.manualRule = null;
80         }
81         final ArraySet<Uri> current = new ArraySet<>();
82         evaluateRule(config.manualRule, current, null, processSubscriptions, true);
83         for (ZenRule automaticRule : config.automaticRules.values()) {
84             if (automaticRule.component != null) {
85                 evaluateRule(automaticRule, current, trigger, processSubscriptions, false);
86                 automaticRule.reconsiderConditionOverride();
87             }
88         }
89 
90         synchronized (mSubscriptions) {
91             final int N = mSubscriptions.size();
92             for (int i = N - 1; i >= 0; i--) {
93                 final Uri id = mSubscriptions.keyAt(i);
94                 final ComponentName component = mSubscriptions.valueAt(i);
95                 if (processSubscriptions) {
96                     if (!current.contains(id)) {
97                         mConditionProviders.unsubscribeIfNecessary(component, id);
98                         mSubscriptions.removeAt(i);
99                     }
100                 }
101             }
102         }
103     }
104 
105     @Override
onServiceAdded(ComponentName component)106     public void onServiceAdded(ComponentName component) {
107         if (DEBUG) Log.d(TAG, "onServiceAdded " + component);
108         final int callingUid = Binder.getCallingUid();
109         mHelper.setConfig(mHelper.getConfig(), component,
110                 callingUid == Process.SYSTEM_UID ? ZenModeConfig.ORIGIN_SYSTEM
111                         : ZenModeConfig.ORIGIN_APP,
112                 "zmc.onServiceAdded:" + component, callingUid);
113     }
114 
115     @Override
onConditionChanged(Uri id, Condition condition, int callingUid)116     public void onConditionChanged(Uri id, Condition condition, int callingUid) {
117         if (DEBUG) Log.d(TAG, "onConditionChanged " + id + " " + condition);
118         ZenModeConfig config = mHelper.getConfig();
119         if (config == null) return;
120         if (!Flags.fixCallingUidFromCps()) {
121             // Old behavior: overwrite with known-bad callingUid (always system_server).
122             callingUid = Binder.getCallingUid();
123         }
124 
125         // This change is known to be for UserHandle.CURRENT because ConditionProviders for
126         // background users are not bound.
127         mHelper.setAutomaticZenRuleStateFromConditionProvider(UserHandle.CURRENT, id, condition,
128                 callingUid == Process.SYSTEM_UID ? ZenModeConfig.ORIGIN_SYSTEM
129                         : ZenModeConfig.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 (!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 }
188