blob: 64d77c1b6ade187b6f8d05867832e7a206e533ec [file] [log] [blame]
John Spurlock7340fc82014-04-24 18:50:12 -04001/**
2 * Copyright (c) 2014, 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
17package com.android.server.notification;
18
John Spurlock3b98b3f2014-05-01 09:08:48 -040019import android.content.ComponentName;
John Spurlock7340fc82014-04-24 18:50:12 -040020import android.content.Context;
John Spurlocke77bb362014-04-26 10:24:59 -040021import android.net.Uri;
John Spurlock7340fc82014-04-24 18:50:12 -040022import android.os.Handler;
23import android.os.IBinder;
24import android.os.IInterface;
John Spurlocke77bb362014-04-26 10:24:59 -040025import android.os.RemoteException;
John Spurlock856edeb2014-06-01 20:36:47 -040026import android.os.UserHandle;
John Spurlock7340fc82014-04-24 18:50:12 -040027import android.provider.Settings;
John Spurlock1c923a32014-04-27 16:42:29 -040028import android.provider.Settings.Global;
John Spurlocke77bb362014-04-26 10:24:59 -040029import android.service.notification.Condition;
John Spurlock7340fc82014-04-24 18:50:12 -040030import android.service.notification.ConditionProviderService;
John Spurlocke77bb362014-04-26 10:24:59 -040031import android.service.notification.IConditionListener;
32import android.service.notification.IConditionProvider;
John Spurlock3b98b3f2014-05-01 09:08:48 -040033import android.service.notification.ZenModeConfig;
John Spurlocke77bb362014-04-26 10:24:59 -040034import android.util.ArrayMap;
John Spurlock3b98b3f2014-05-01 09:08:48 -040035import android.util.ArraySet;
John Spurlock7340fc82014-04-24 18:50:12 -040036import android.util.Slog;
37
38import com.android.internal.R;
John Spurlock25e2d242014-06-27 13:58:23 -040039import com.android.server.notification.NotificationManagerService.DumpFilter;
John Spurlock7340fc82014-04-24 18:50:12 -040040
John Spurlocke77bb362014-04-26 10:24:59 -040041import java.io.PrintWriter;
John Spurlock3b98b3f2014-05-01 09:08:48 -040042import java.util.ArrayList;
John Spurlocke77bb362014-04-26 10:24:59 -040043import java.util.Arrays;
John Spurlock856edeb2014-06-01 20:36:47 -040044import java.util.Objects;
John Spurlocke77bb362014-04-26 10:24:59 -040045
John Spurlock7340fc82014-04-24 18:50:12 -040046public class ConditionProviders extends ManagedServices {
John Spurlock3b98b3f2014-05-01 09:08:48 -040047 private static final Condition[] NO_CONDITIONS = new Condition[0];
John Spurlock7340fc82014-04-24 18:50:12 -040048
John Spurlocke77bb362014-04-26 10:24:59 -040049 private final ZenModeHelper mZenModeHelper;
50 private final ArrayMap<IBinder, IConditionListener> mListeners
51 = new ArrayMap<IBinder, IConditionListener>();
John Spurlock3b98b3f2014-05-01 09:08:48 -040052 private final ArrayList<ConditionRecord> mRecords = new ArrayList<ConditionRecord>();
John Spurlock530052a2014-11-30 16:26:19 -050053 private final ArraySet<String> mSystemConditionProviders;
54 private final CountdownConditionProvider mCountdown;
55 private final DowntimeConditionProvider mDowntime;
56 private final NextAlarmConditionProvider mNextAlarm;
John Spurlock6b0623a2014-11-10 14:05:13 -050057 private final NextAlarmTracker mNextAlarmTracker;
John Spurlock856edeb2014-06-01 20:36:47 -040058
John Spurlock4db0d982014-08-13 09:19:03 -040059 private Condition mExitCondition;
John Spurlock50806fc2014-07-15 10:22:02 -040060 private ComponentName mExitConditionComponent;
John Spurlocke77bb362014-04-26 10:24:59 -040061
John Spurlock7340fc82014-04-24 18:50:12 -040062 public ConditionProviders(Context context, Handler handler,
John Spurlocke77bb362014-04-26 10:24:59 -040063 UserProfiles userProfiles, ZenModeHelper zenModeHelper) {
64 super(context, handler, new Object(), userProfiles);
65 mZenModeHelper = zenModeHelper;
John Spurlock1c923a32014-04-27 16:42:29 -040066 mZenModeHelper.addCallback(new ZenModeHelperCallback());
John Spurlock530052a2014-11-30 16:26:19 -050067 mSystemConditionProviders = safeSet(PropConfig.getStringArray(mContext,
68 "system.condition.providers",
69 R.array.config_system_condition_providers));
70 final boolean countdown = mSystemConditionProviders.contains(ZenModeConfig.COUNTDOWN_PATH);
71 final boolean downtime = mSystemConditionProviders.contains(ZenModeConfig.DOWNTIME_PATH);
72 final boolean nextAlarm = mSystemConditionProviders.contains(ZenModeConfig.NEXT_ALARM_PATH);
73 mNextAlarmTracker = (downtime || nextAlarm) ? new NextAlarmTracker(mContext) : null;
74 mCountdown = countdown ? new CountdownConditionProvider() : null;
75 mDowntime = downtime ? new DowntimeConditionProvider(this, mNextAlarmTracker,
76 mZenModeHelper) : null;
77 mNextAlarm = nextAlarm ? new NextAlarmConditionProvider(mNextAlarmTracker) : null;
John Spurlock3b98b3f2014-05-01 09:08:48 -040078 loadZenConfig();
John Spurlock530052a2014-11-30 16:26:19 -050079 }
80
81 public boolean isSystemConditionProviderEnabled(String path) {
82 return mSystemConditionProviders.contains(path);
John Spurlock7340fc82014-04-24 18:50:12 -040083 }
84
85 @Override
86 protected Config getConfig() {
87 Config c = new Config();
88 c.caption = "condition provider";
89 c.serviceInterface = ConditionProviderService.SERVICE_INTERFACE;
90 c.secureSettingName = Settings.Secure.ENABLED_CONDITION_PROVIDERS;
91 c.bindPermission = android.Manifest.permission.BIND_CONDITION_PROVIDER_SERVICE;
92 c.settingsAction = Settings.ACTION_CONDITION_PROVIDER_SETTINGS;
93 c.clientLabel = R.string.condition_provider_service_binding_label;
94 return c;
95 }
96
97 @Override
John Spurlock25e2d242014-06-27 13:58:23 -040098 public void dump(PrintWriter pw, DumpFilter filter) {
99 super.dump(pw, filter);
John Spurlocke77bb362014-04-26 10:24:59 -0400100 synchronized(mMutex) {
John Spurlock25e2d242014-06-27 13:58:23 -0400101 if (filter == null) {
102 pw.print(" mListeners("); pw.print(mListeners.size()); pw.println("):");
103 for (int i = 0; i < mListeners.size(); i++) {
104 pw.print(" "); pw.println(mListeners.keyAt(i));
105 }
John Spurlocke77bb362014-04-26 10:24:59 -0400106 }
John Spurlock3b98b3f2014-05-01 09:08:48 -0400107 pw.print(" mRecords("); pw.print(mRecords.size()); pw.println("):");
108 for (int i = 0; i < mRecords.size(); i++) {
John Spurlock856edeb2014-06-01 20:36:47 -0400109 final ConditionRecord r = mRecords.get(i);
John Spurlock25e2d242014-06-27 13:58:23 -0400110 if (filter != null && !filter.matches(r.component)) continue;
John Spurlock856edeb2014-06-01 20:36:47 -0400111 pw.print(" "); pw.println(r);
112 final String countdownDesc = CountdownConditionProvider.tryParseDescription(r.id);
113 if (countdownDesc != null) {
114 pw.print(" ("); pw.print(countdownDesc); pw.println(")");
115 }
John Spurlocke77bb362014-04-26 10:24:59 -0400116 }
117 }
John Spurlock530052a2014-11-30 16:26:19 -0500118 pw.print(" mSystemConditionProviders: "); pw.println(mSystemConditionProviders);
119 if (mCountdown != null) {
120 mCountdown.dump(pw, filter);
121 }
122 if (mDowntime != null) {
123 mDowntime.dump(pw, filter);
124 }
125 if (mNextAlarm != null) {
126 mNextAlarm.dump(pw, filter);
127 }
128 if (mNextAlarmTracker != null) {
129 mNextAlarmTracker.dump(pw, filter);
130 }
John Spurlocke77bb362014-04-26 10:24:59 -0400131 }
132
133 @Override
John Spurlock7340fc82014-04-24 18:50:12 -0400134 protected IInterface asInterface(IBinder binder) {
135 return IConditionProvider.Stub.asInterface(binder);
136 }
137
138 @Override
John Spurlock856edeb2014-06-01 20:36:47 -0400139 public void onBootPhaseAppsCanStart() {
140 super.onBootPhaseAppsCanStart();
John Spurlock530052a2014-11-30 16:26:19 -0500141 if (mNextAlarmTracker != null) {
142 mNextAlarmTracker.init();
143 }
144 if (mCountdown != null) {
145 mCountdown.attachBase(mContext);
146 registerService(mCountdown.asInterface(), CountdownConditionProvider.COMPONENT,
147 UserHandle.USER_OWNER);
148 }
149 if (mDowntime != null) {
150 mDowntime.attachBase(mContext);
151 registerService(mDowntime.asInterface(), DowntimeConditionProvider.COMPONENT,
152 UserHandle.USER_OWNER);
153 }
154 if (mNextAlarm != null) {
155 mNextAlarm.attachBase(mContext);
156 registerService(mNextAlarm.asInterface(), NextAlarmConditionProvider.COMPONENT,
157 UserHandle.USER_OWNER);
158 }
John Spurlock37bc92c2014-11-03 11:01:51 -0500159 }
160
161 @Override
162 public void onUserSwitched() {
163 super.onUserSwitched();
John Spurlock530052a2014-11-30 16:26:19 -0500164 if (mNextAlarmTracker != null) {
165 mNextAlarmTracker.onUserSwitched();
166 }
John Spurlock856edeb2014-06-01 20:36:47 -0400167 }
168
169 @Override
John Spurlock3b98b3f2014-05-01 09:08:48 -0400170 protected void onServiceAdded(ManagedServiceInfo info) {
171 Slog.d(TAG, "onServiceAdded " + info);
172 final IConditionProvider provider = provider(info);
John Spurlocke77bb362014-04-26 10:24:59 -0400173 try {
174 provider.onConnected();
175 } catch (RemoteException e) {
176 // we tried
177 }
John Spurlock3b98b3f2014-05-01 09:08:48 -0400178 synchronized (mMutex) {
John Spurlock50806fc2014-07-15 10:22:02 -0400179 if (info.component.equals(mExitConditionComponent)) {
180 // ensure record exists, we'll wire it up and subscribe below
181 final ConditionRecord manualRecord =
John Spurlock4db0d982014-08-13 09:19:03 -0400182 getRecordLocked(mExitCondition.id, mExitConditionComponent);
John Spurlock50806fc2014-07-15 10:22:02 -0400183 manualRecord.isManual = true;
184 }
John Spurlock3b98b3f2014-05-01 09:08:48 -0400185 final int N = mRecords.size();
186 for(int i = 0; i < N; i++) {
187 final ConditionRecord r = mRecords.get(i);
188 if (!r.component.equals(info.component)) continue;
189 r.info = info;
John Spurlock50806fc2014-07-15 10:22:02 -0400190 // if automatic or manual, auto-subscribe
191 if (r.isAutomatic || r.isManual) {
192 subscribeLocked(r);
John Spurlock3b98b3f2014-05-01 09:08:48 -0400193 }
194 }
195 }
John Spurlocke77bb362014-04-26 10:24:59 -0400196 }
197
198 @Override
199 protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
200 if (removed == null) return;
John Spurlock3b98b3f2014-05-01 09:08:48 -0400201 for (int i = mRecords.size() - 1; i >= 0; i--) {
202 final ConditionRecord r = mRecords.get(i);
203 if (!r.component.equals(removed.component)) continue;
204 if (r.isManual) {
205 // removing the current manual condition, exit zen
John Spurlock530052a2014-11-30 16:26:19 -0500206 onManualConditionClearing();
John Spurlock4db0d982014-08-13 09:19:03 -0400207 mZenModeHelper.setZenMode(Global.ZEN_MODE_OFF, "manualServiceRemoved");
John Spurlocke86de4c2014-04-29 21:48:26 -0400208 }
John Spurlock3b98b3f2014-05-01 09:08:48 -0400209 if (r.isAutomatic) {
210 // removing an automatic condition, exit zen
John Spurlock4db0d982014-08-13 09:19:03 -0400211 mZenModeHelper.setZenMode(Global.ZEN_MODE_OFF, "automaticServiceRemoved");
John Spurlocke77bb362014-04-26 10:24:59 -0400212 }
John Spurlock3b98b3f2014-05-01 09:08:48 -0400213 mRecords.remove(i);
John Spurlocke77bb362014-04-26 10:24:59 -0400214 }
215 }
216
217 public ManagedServiceInfo checkServiceToken(IConditionProvider provider) {
218 synchronized(mMutex) {
219 return checkServiceTokenLocked(provider);
220 }
221 }
222
John Spurlock3b98b3f2014-05-01 09:08:48 -0400223 public void requestZenModeConditions(IConditionListener callback, int relevance) {
John Spurlocke77bb362014-04-26 10:24:59 -0400224 synchronized(mMutex) {
225 if (DEBUG) Slog.d(TAG, "requestZenModeConditions callback=" + callback
John Spurlock3b98b3f2014-05-01 09:08:48 -0400226 + " relevance=" + Condition.relevanceToString(relevance));
John Spurlocke77bb362014-04-26 10:24:59 -0400227 if (callback == null) return;
John Spurlock3b98b3f2014-05-01 09:08:48 -0400228 relevance = relevance & (Condition.FLAG_RELEVANT_NOW | Condition.FLAG_RELEVANT_ALWAYS);
229 if (relevance != 0) {
John Spurlocke77bb362014-04-26 10:24:59 -0400230 mListeners.put(callback.asBinder(), callback);
John Spurlock3b98b3f2014-05-01 09:08:48 -0400231 requestConditionsLocked(relevance);
John Spurlocke77bb362014-04-26 10:24:59 -0400232 } else {
233 mListeners.remove(callback.asBinder());
234 if (mListeners.isEmpty()) {
235 requestConditionsLocked(0);
236 }
237 }
238 }
239 }
240
John Spurlock3b98b3f2014-05-01 09:08:48 -0400241 private Condition[] validateConditions(String pkg, Condition[] conditions) {
242 if (conditions == null || conditions.length == 0) return null;
243 final int N = conditions.length;
244 final ArrayMap<Uri, Condition> valid = new ArrayMap<Uri, Condition>(N);
245 for (int i = 0; i < N; i++) {
246 final Uri id = conditions[i].id;
247 if (!Condition.isValidId(id, pkg)) {
248 Slog.w(TAG, "Ignoring condition from " + pkg + " for invalid id: " + id);
249 continue;
250 }
251 if (valid.containsKey(id)) {
252 Slog.w(TAG, "Ignoring condition from " + pkg + " for duplicate id: " + id);
253 continue;
254 }
255 valid.put(id, conditions[i]);
256 }
257 if (valid.size() == 0) return null;
258 if (valid.size() == N) return conditions;
259 final Condition[] rt = new Condition[valid.size()];
260 for (int i = 0; i < rt.length; i++) {
261 rt[i] = valid.valueAt(i);
262 }
263 return rt;
264 }
265
266 private ConditionRecord getRecordLocked(Uri id, ComponentName component) {
267 final int N = mRecords.size();
268 for (int i = 0; i < N; i++) {
269 final ConditionRecord r = mRecords.get(i);
270 if (r.id.equals(id) && r.component.equals(component)) {
271 return r;
272 }
273 }
274 final ConditionRecord r = new ConditionRecord(id, component);
275 mRecords.add(r);
276 return r;
277 }
278
John Spurlocke77bb362014-04-26 10:24:59 -0400279 public void notifyConditions(String pkg, ManagedServiceInfo info, Condition[] conditions) {
280 synchronized(mMutex) {
281 if (DEBUG) Slog.d(TAG, "notifyConditions pkg=" + pkg + " info=" + info + " conditions="
282 + (conditions == null ? null : Arrays.asList(conditions)));
John Spurlock3b98b3f2014-05-01 09:08:48 -0400283 conditions = validateConditions(pkg, conditions);
John Spurlocke77bb362014-04-26 10:24:59 -0400284 if (conditions == null || conditions.length == 0) return;
285 final int N = conditions.length;
John Spurlocke77bb362014-04-26 10:24:59 -0400286 for (IConditionListener listener : mListeners.values()) {
287 try {
288 listener.onConditionsReceived(conditions);
289 } catch (RemoteException e) {
290 Slog.w(TAG, "Error sending conditions to listener " + listener, e);
291 }
292 }
John Spurlock3b98b3f2014-05-01 09:08:48 -0400293 for (int i = 0; i < N; i++) {
294 final Condition c = conditions[i];
295 final ConditionRecord r = getRecordLocked(c.id, info.component);
John Spurlock25c34212014-11-12 09:16:49 -0500296 final Condition oldCondition = r.condition;
297 final boolean conditionUpdate = oldCondition != null && !oldCondition.equals(c);
John Spurlock3b98b3f2014-05-01 09:08:48 -0400298 r.info = info;
299 r.condition = c;
John Spurlock25c34212014-11-12 09:16:49 -0500300 // if manual, exit zen if false (or failed), update if true (and changed)
John Spurlock3b98b3f2014-05-01 09:08:48 -0400301 if (r.isManual) {
302 if (c.state == Condition.STATE_FALSE || c.state == Condition.STATE_ERROR) {
303 final boolean failed = c.state == Condition.STATE_ERROR;
304 if (failed) {
305 Slog.w(TAG, "Exit zen: manual condition failed: " + c);
306 } else if (DEBUG) {
307 Slog.d(TAG, "Exit zen: manual condition false: " + c);
308 }
John Spurlock530052a2014-11-30 16:26:19 -0500309 onManualConditionClearing();
John Spurlock4db0d982014-08-13 09:19:03 -0400310 mZenModeHelper.setZenMode(Settings.Global.ZEN_MODE_OFF,
311 "manualConditionExit");
John Spurlock3b98b3f2014-05-01 09:08:48 -0400312 unsubscribeLocked(r);
313 r.isManual = false;
John Spurlock25c34212014-11-12 09:16:49 -0500314 } else if (c.state == Condition.STATE_TRUE && conditionUpdate) {
315 if (DEBUG) Slog.d(TAG, "Current condition updated, still true. old="
316 + oldCondition + " new=" + c);
317 setZenModeCondition(c, "conditionUpdate");
John Spurlock3b98b3f2014-05-01 09:08:48 -0400318 }
319 }
320 // if automatic, exit zen if false (or failed), enter zen if true
321 if (r.isAutomatic) {
322 if (c.state == Condition.STATE_FALSE || c.state == Condition.STATE_ERROR) {
323 final boolean failed = c.state == Condition.STATE_ERROR;
324 if (failed) {
325 Slog.w(TAG, "Exit zen: automatic condition failed: " + c);
326 } else if (DEBUG) {
327 Slog.d(TAG, "Exit zen: automatic condition false: " + c);
328 }
John Spurlock4db0d982014-08-13 09:19:03 -0400329 mZenModeHelper.setZenMode(Settings.Global.ZEN_MODE_OFF,
330 "automaticConditionExit");
John Spurlock3b98b3f2014-05-01 09:08:48 -0400331 } else if (c.state == Condition.STATE_TRUE) {
332 Slog.d(TAG, "Enter zen: automatic condition true: " + c);
John Spurlock4db0d982014-08-13 09:19:03 -0400333 mZenModeHelper.setZenMode(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS,
334 "automaticConditionEnter");
John Spurlocke77bb362014-04-26 10:24:59 -0400335 }
336 }
337 }
338 }
339 }
340
John Spurlock530052a2014-11-30 16:26:19 -0500341 private void ensureRecordExists(Condition condition, IConditionProvider provider,
342 ComponentName component) {
343 // constructed by convention, make sure the record exists...
344 final ConditionRecord r = getRecordLocked(condition.id, component);
345 if (r.info == null) {
346 // ... and is associated with the in-process service
347 r.info = checkServiceTokenLocked(provider);
348 }
349 }
350
John Spurlock4db0d982014-08-13 09:19:03 -0400351 public void setZenModeCondition(Condition condition, String reason) {
John Spurlock530052a2014-11-30 16:26:19 -0500352 if (DEBUG) Slog.d(TAG, "setZenModeCondition " + condition + " reason=" + reason);
John Spurlocke77bb362014-04-26 10:24:59 -0400353 synchronized(mMutex) {
John Spurlock50806fc2014-07-15 10:22:02 -0400354 ComponentName conditionComponent = null;
John Spurlock4db0d982014-08-13 09:19:03 -0400355 if (condition != null) {
John Spurlock530052a2014-11-30 16:26:19 -0500356 if (mCountdown != null && ZenModeConfig.isValidCountdownConditionId(condition.id)) {
357 ensureRecordExists(condition, mCountdown.asInterface(),
John Spurlock4db0d982014-08-13 09:19:03 -0400358 CountdownConditionProvider.COMPONENT);
John Spurlock4db0d982014-08-13 09:19:03 -0400359 }
John Spurlock530052a2014-11-30 16:26:19 -0500360 if (mDowntime != null && ZenModeConfig.isValidDowntimeConditionId(condition.id)) {
361 ensureRecordExists(condition, mDowntime.asInterface(),
John Spurlock4db0d982014-08-13 09:19:03 -0400362 DowntimeConditionProvider.COMPONENT);
John Spurlock856edeb2014-06-01 20:36:47 -0400363 }
364 }
John Spurlock3b98b3f2014-05-01 09:08:48 -0400365 final int N = mRecords.size();
366 for (int i = 0; i < N; i++) {
367 final ConditionRecord r = mRecords.get(i);
John Spurlock4db0d982014-08-13 09:19:03 -0400368 final boolean idEqual = condition != null && r.id.equals(condition.id);
John Spurlock3b98b3f2014-05-01 09:08:48 -0400369 if (r.isManual && !idEqual) {
370 // was previous manual condition, unsubscribe
371 unsubscribeLocked(r);
372 r.isManual = false;
373 } else if (idEqual && !r.isManual) {
374 // is new manual condition, subscribe
375 subscribeLocked(r);
376 r.isManual = true;
John Spurlocke77bb362014-04-26 10:24:59 -0400377 }
John Spurlock50806fc2014-07-15 10:22:02 -0400378 if (idEqual) {
379 conditionComponent = r.component;
380 }
John Spurlocke77bb362014-04-26 10:24:59 -0400381 }
John Spurlock4db0d982014-08-13 09:19:03 -0400382 if (!Objects.equals(mExitCondition, condition)) {
383 mExitCondition = condition;
John Spurlock50806fc2014-07-15 10:22:02 -0400384 mExitConditionComponent = conditionComponent;
John Spurlock4db0d982014-08-13 09:19:03 -0400385 ZenLog.traceExitCondition(mExitCondition, mExitConditionComponent, reason);
John Spurlock856edeb2014-06-01 20:36:47 -0400386 saveZenConfigLocked();
387 }
John Spurlocke77bb362014-04-26 10:24:59 -0400388 }
389 }
390
John Spurlock3b98b3f2014-05-01 09:08:48 -0400391 private void subscribeLocked(ConditionRecord r) {
392 if (DEBUG) Slog.d(TAG, "subscribeLocked " + r);
393 final IConditionProvider provider = provider(r);
John Spurlock6ae82a72014-07-16 16:23:01 -0400394 RemoteException re = null;
395 if (provider != null) {
396 try {
John Spurlock4db0d982014-08-13 09:19:03 -0400397 Slog.d(TAG, "Subscribing to " + r.id + " with " + provider);
John Spurlock6ae82a72014-07-16 16:23:01 -0400398 provider.onSubscribe(r.id);
399 } catch (RemoteException e) {
400 Slog.w(TAG, "Error subscribing to " + r, e);
401 re = e;
402 }
John Spurlocke77bb362014-04-26 10:24:59 -0400403 }
John Spurlock6ae82a72014-07-16 16:23:01 -0400404 ZenLog.traceSubscribe(r != null ? r.id : null, provider, re);
John Spurlock3b98b3f2014-05-01 09:08:48 -0400405 }
406
John Spurlock530052a2014-11-30 16:26:19 -0500407 @SafeVarargs
John Spurlock3b98b3f2014-05-01 09:08:48 -0400408 private static <T> ArraySet<T> safeSet(T... items) {
409 final ArraySet<T> rt = new ArraySet<T>();
410 if (items == null || items.length == 0) return rt;
411 final int N = items.length;
412 for (int i = 0; i < N; i++) {
413 final T item = items[i];
414 if (item != null) {
415 rt.add(item);
416 }
417 }
418 return rt;
419 }
420
421 public void setAutomaticZenModeConditions(Uri[] conditionIds) {
422 setAutomaticZenModeConditions(conditionIds, true /*save*/);
423 }
424
425 private void setAutomaticZenModeConditions(Uri[] conditionIds, boolean save) {
426 if (DEBUG) Slog.d(TAG, "setAutomaticZenModeConditions "
427 + (conditionIds == null ? null : Arrays.asList(conditionIds)));
428 synchronized(mMutex) {
429 final ArraySet<Uri> newIds = safeSet(conditionIds);
430 final int N = mRecords.size();
431 boolean changed = false;
432 for (int i = 0; i < N; i++) {
433 final ConditionRecord r = mRecords.get(i);
434 final boolean automatic = newIds.contains(r.id);
435 if (!r.isAutomatic && automatic) {
436 // subscribe to new automatic
437 subscribeLocked(r);
438 r.isAutomatic = true;
439 changed = true;
440 } else if (r.isAutomatic && !automatic) {
441 // unsubscribe from old automatic
442 unsubscribeLocked(r);
443 r.isAutomatic = false;
444 changed = true;
445 }
446 }
447 if (save && changed) {
448 saveZenConfigLocked();
449 }
450 }
451 }
452
453 public Condition[] getAutomaticZenModeConditions() {
454 synchronized(mMutex) {
455 final int N = mRecords.size();
456 ArrayList<Condition> rt = null;
457 for (int i = 0; i < N; i++) {
458 final ConditionRecord r = mRecords.get(i);
459 if (r.isAutomatic && r.condition != null) {
460 if (rt == null) rt = new ArrayList<Condition>();
461 rt.add(r.condition);
462 }
463 }
464 return rt == null ? NO_CONDITIONS : rt.toArray(new Condition[rt.size()]);
465 }
466 }
467
468 private void unsubscribeLocked(ConditionRecord r) {
469 if (DEBUG) Slog.d(TAG, "unsubscribeLocked " + r);
470 final IConditionProvider provider = provider(r);
John Spurlock6ae82a72014-07-16 16:23:01 -0400471 RemoteException re = null;
472 if (provider != null) {
473 try {
474 provider.onUnsubscribe(r.id);
475 } catch (RemoteException e) {
476 Slog.w(TAG, "Error unsubscribing to " + r, e);
477 re = e;
478 }
John Spurlock3b98b3f2014-05-01 09:08:48 -0400479 }
John Spurlock6ae82a72014-07-16 16:23:01 -0400480 ZenLog.traceUnsubscribe(r != null ? r.id : null, provider, re);
John Spurlock3b98b3f2014-05-01 09:08:48 -0400481 }
482
483 private static IConditionProvider provider(ConditionRecord r) {
484 return r == null ? null : provider(r.info);
John Spurlocke77bb362014-04-26 10:24:59 -0400485 }
486
487 private static IConditionProvider provider(ManagedServiceInfo info) {
488 return info == null ? null : (IConditionProvider) info.service;
489 }
490
491 private void requestConditionsLocked(int flags) {
492 for (ManagedServiceInfo info : mServices) {
493 final IConditionProvider provider = provider(info);
494 if (provider == null) continue;
John Spurlock50806fc2014-07-15 10:22:02 -0400495 // clear all stored conditions from this provider that we no longer care about
496 for (int i = mRecords.size() - 1; i >= 0; i--) {
497 final ConditionRecord r = mRecords.get(i);
498 if (r.info != info) continue;
499 if (r.isManual || r.isAutomatic) continue;
500 mRecords.remove(i);
501 }
John Spurlocke77bb362014-04-26 10:24:59 -0400502 try {
503 provider.onRequestConditions(flags);
504 } catch (RemoteException e) {
505 Slog.w(TAG, "Error requesting conditions from " + info.component, e);
506 }
507 }
John Spurlock7340fc82014-04-24 18:50:12 -0400508 }
John Spurlock1c923a32014-04-27 16:42:29 -0400509
John Spurlock3b98b3f2014-05-01 09:08:48 -0400510 private void loadZenConfig() {
511 final ZenModeConfig config = mZenModeHelper.getConfig();
512 if (config == null) {
513 if (DEBUG) Slog.d(TAG, "loadZenConfig: no config");
514 return;
515 }
516 synchronized (mMutex) {
John Spurlock4db0d982014-08-13 09:19:03 -0400517 final boolean changingExit = !Objects.equals(mExitCondition, config.exitCondition);
518 mExitCondition = config.exitCondition;
John Spurlock50806fc2014-07-15 10:22:02 -0400519 mExitConditionComponent = config.exitConditionComponent;
John Spurlock6ae82a72014-07-16 16:23:01 -0400520 if (changingExit) {
John Spurlock4db0d982014-08-13 09:19:03 -0400521 ZenLog.traceExitCondition(mExitCondition, mExitConditionComponent, "config");
John Spurlock6ae82a72014-07-16 16:23:01 -0400522 }
John Spurlock530052a2014-11-30 16:26:19 -0500523 if (mDowntime != null) {
524 mDowntime.setConfig(config);
525 }
John Spurlock3b98b3f2014-05-01 09:08:48 -0400526 if (config.conditionComponents == null || config.conditionIds == null
527 || config.conditionComponents.length != config.conditionIds.length) {
528 if (DEBUG) Slog.d(TAG, "loadZenConfig: no conditions");
529 setAutomaticZenModeConditions(null, false /*save*/);
530 return;
531 }
532 final ArraySet<Uri> newIds = new ArraySet<Uri>();
533 final int N = config.conditionComponents.length;
534 for (int i = 0; i < N; i++) {
535 final ComponentName component = config.conditionComponents[i];
536 final Uri id = config.conditionIds[i];
537 if (component != null && id != null) {
538 getRecordLocked(id, component); // ensure record exists
539 newIds.add(id);
540 }
541 }
542 if (DEBUG) Slog.d(TAG, "loadZenConfig: N=" + N);
543 setAutomaticZenModeConditions(newIds.toArray(new Uri[newIds.size()]), false /*save*/);
544 }
545 }
546
547 private void saveZenConfigLocked() {
548 ZenModeConfig config = mZenModeHelper.getConfig();
549 if (config == null) return;
550 config = config.copy();
551 final ArrayList<ConditionRecord> automatic = new ArrayList<ConditionRecord>();
552 final int automaticN = mRecords.size();
553 for (int i = 0; i < automaticN; i++) {
554 final ConditionRecord r = mRecords.get(i);
555 if (r.isAutomatic) {
556 automatic.add(r);
557 }
558 }
559 if (automatic.isEmpty()) {
560 config.conditionComponents = null;
561 config.conditionIds = null;
562 } else {
563 final int N = automatic.size();
564 config.conditionComponents = new ComponentName[N];
565 config.conditionIds = new Uri[N];
566 for (int i = 0; i < N; i++) {
567 final ConditionRecord r = automatic.get(i);
568 config.conditionComponents[i] = r.component;
569 config.conditionIds[i] = r.id;
570 }
571 }
John Spurlock4db0d982014-08-13 09:19:03 -0400572 config.exitCondition = mExitCondition;
John Spurlock50806fc2014-07-15 10:22:02 -0400573 config.exitConditionComponent = mExitConditionComponent;
John Spurlock3b98b3f2014-05-01 09:08:48 -0400574 if (DEBUG) Slog.d(TAG, "Setting zen config to: " + config);
575 mZenModeHelper.setConfig(config);
576 }
577
John Spurlock530052a2014-11-30 16:26:19 -0500578 private void onManualConditionClearing() {
579 if (mDowntime != null) {
580 mDowntime.onManualConditionClearing();
581 }
582 }
583
John Spurlock1c923a32014-04-27 16:42:29 -0400584 private class ZenModeHelperCallback extends ZenModeHelper.Callback {
585 @Override
John Spurlock3b98b3f2014-05-01 09:08:48 -0400586 void onConfigChanged() {
587 loadZenConfig();
588 }
589
590 @Override
John Spurlock1c923a32014-04-27 16:42:29 -0400591 void onZenModeChanged() {
592 final int mode = mZenModeHelper.getZenMode();
593 if (mode == Global.ZEN_MODE_OFF) {
John Spurlock3b98b3f2014-05-01 09:08:48 -0400594 // ensure any manual condition is cleared
John Spurlock6ae82a72014-07-16 16:23:01 -0400595 setZenModeCondition(null, "zenOff");
John Spurlock1c923a32014-04-27 16:42:29 -0400596 }
597 }
598 }
John Spurlock3b98b3f2014-05-01 09:08:48 -0400599
600 private static class ConditionRecord {
601 public final Uri id;
602 public final ComponentName component;
603 public Condition condition;
604 public ManagedServiceInfo info;
605 public boolean isAutomatic;
606 public boolean isManual;
607
608 private ConditionRecord(Uri id, ComponentName component) {
609 this.id = id;
610 this.component = component;
611 }
612
613 @Override
614 public String toString() {
615 final StringBuilder sb = new StringBuilder("ConditionRecord[id=")
616 .append(id).append(",component=").append(component);
617 if (isAutomatic) sb.append(",automatic");
618 if (isManual) sb.append(",manual");
619 return sb.append(']').toString();
620 }
621 }
John Spurlock7340fc82014-04-24 18:50:12 -0400622}