blob: a06daf6374962b179d00fb1a6934e7aad2f64aa5 [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 Spurlock856edeb2014-06-01 20:36:47 -040053 private final CountdownConditionProvider mCountdown = new CountdownConditionProvider();
54
55 private Uri mExitConditionId;
John Spurlock50806fc2014-07-15 10:22:02 -040056 private ComponentName mExitConditionComponent;
John Spurlocke77bb362014-04-26 10:24:59 -040057
John Spurlock7340fc82014-04-24 18:50:12 -040058 public ConditionProviders(Context context, Handler handler,
John Spurlocke77bb362014-04-26 10:24:59 -040059 UserProfiles userProfiles, ZenModeHelper zenModeHelper) {
60 super(context, handler, new Object(), userProfiles);
61 mZenModeHelper = zenModeHelper;
John Spurlock1c923a32014-04-27 16:42:29 -040062 mZenModeHelper.addCallback(new ZenModeHelperCallback());
John Spurlock3b98b3f2014-05-01 09:08:48 -040063 loadZenConfig();
John Spurlock7340fc82014-04-24 18:50:12 -040064 }
65
66 @Override
67 protected Config getConfig() {
68 Config c = new Config();
69 c.caption = "condition provider";
70 c.serviceInterface = ConditionProviderService.SERVICE_INTERFACE;
71 c.secureSettingName = Settings.Secure.ENABLED_CONDITION_PROVIDERS;
72 c.bindPermission = android.Manifest.permission.BIND_CONDITION_PROVIDER_SERVICE;
73 c.settingsAction = Settings.ACTION_CONDITION_PROVIDER_SETTINGS;
74 c.clientLabel = R.string.condition_provider_service_binding_label;
75 return c;
76 }
77
78 @Override
John Spurlock25e2d242014-06-27 13:58:23 -040079 public void dump(PrintWriter pw, DumpFilter filter) {
80 super.dump(pw, filter);
John Spurlocke77bb362014-04-26 10:24:59 -040081 synchronized(mMutex) {
John Spurlock25e2d242014-06-27 13:58:23 -040082 if (filter == null) {
83 pw.print(" mListeners("); pw.print(mListeners.size()); pw.println("):");
84 for (int i = 0; i < mListeners.size(); i++) {
85 pw.print(" "); pw.println(mListeners.keyAt(i));
86 }
John Spurlocke77bb362014-04-26 10:24:59 -040087 }
John Spurlock3b98b3f2014-05-01 09:08:48 -040088 pw.print(" mRecords("); pw.print(mRecords.size()); pw.println("):");
89 for (int i = 0; i < mRecords.size(); i++) {
John Spurlock856edeb2014-06-01 20:36:47 -040090 final ConditionRecord r = mRecords.get(i);
John Spurlock25e2d242014-06-27 13:58:23 -040091 if (filter != null && !filter.matches(r.component)) continue;
John Spurlock856edeb2014-06-01 20:36:47 -040092 pw.print(" "); pw.println(r);
93 final String countdownDesc = CountdownConditionProvider.tryParseDescription(r.id);
94 if (countdownDesc != null) {
95 pw.print(" ("); pw.print(countdownDesc); pw.println(")");
96 }
John Spurlocke77bb362014-04-26 10:24:59 -040097 }
98 }
John Spurlock50806fc2014-07-15 10:22:02 -040099 mCountdown.dump(pw, filter);
John Spurlocke77bb362014-04-26 10:24:59 -0400100 }
101
102 @Override
John Spurlock7340fc82014-04-24 18:50:12 -0400103 protected IInterface asInterface(IBinder binder) {
104 return IConditionProvider.Stub.asInterface(binder);
105 }
106
107 @Override
John Spurlock856edeb2014-06-01 20:36:47 -0400108 public void onBootPhaseAppsCanStart() {
109 super.onBootPhaseAppsCanStart();
110 mCountdown.attachBase(mContext);
111 registerService(mCountdown.asInterface(), CountdownConditionProvider.COMPONENT,
112 UserHandle.USER_OWNER);
113 }
114
115 @Override
John Spurlock3b98b3f2014-05-01 09:08:48 -0400116 protected void onServiceAdded(ManagedServiceInfo info) {
117 Slog.d(TAG, "onServiceAdded " + info);
118 final IConditionProvider provider = provider(info);
John Spurlocke77bb362014-04-26 10:24:59 -0400119 try {
120 provider.onConnected();
121 } catch (RemoteException e) {
122 // we tried
123 }
John Spurlock3b98b3f2014-05-01 09:08:48 -0400124 synchronized (mMutex) {
John Spurlock50806fc2014-07-15 10:22:02 -0400125 if (info.component.equals(mExitConditionComponent)) {
126 // ensure record exists, we'll wire it up and subscribe below
127 final ConditionRecord manualRecord =
128 getRecordLocked(mExitConditionId, mExitConditionComponent);
129 manualRecord.isManual = true;
130 }
John Spurlock3b98b3f2014-05-01 09:08:48 -0400131 final int N = mRecords.size();
132 for(int i = 0; i < N; i++) {
133 final ConditionRecord r = mRecords.get(i);
134 if (!r.component.equals(info.component)) continue;
135 r.info = info;
John Spurlock50806fc2014-07-15 10:22:02 -0400136 // if automatic or manual, auto-subscribe
137 if (r.isAutomatic || r.isManual) {
138 subscribeLocked(r);
John Spurlock3b98b3f2014-05-01 09:08:48 -0400139 }
140 }
141 }
John Spurlocke77bb362014-04-26 10:24:59 -0400142 }
143
144 @Override
145 protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
146 if (removed == null) return;
John Spurlock3b98b3f2014-05-01 09:08:48 -0400147 for (int i = mRecords.size() - 1; i >= 0; i--) {
148 final ConditionRecord r = mRecords.get(i);
149 if (!r.component.equals(removed.component)) continue;
150 if (r.isManual) {
151 // removing the current manual condition, exit zen
John Spurlocke86de4c2014-04-29 21:48:26 -0400152 mZenModeHelper.setZenMode(Global.ZEN_MODE_OFF);
153 }
John Spurlock3b98b3f2014-05-01 09:08:48 -0400154 if (r.isAutomatic) {
155 // removing an automatic condition, exit zen
156 mZenModeHelper.setZenMode(Global.ZEN_MODE_OFF);
John Spurlocke77bb362014-04-26 10:24:59 -0400157 }
John Spurlock3b98b3f2014-05-01 09:08:48 -0400158 mRecords.remove(i);
John Spurlocke77bb362014-04-26 10:24:59 -0400159 }
160 }
161
162 public ManagedServiceInfo checkServiceToken(IConditionProvider provider) {
163 synchronized(mMutex) {
164 return checkServiceTokenLocked(provider);
165 }
166 }
167
John Spurlock3b98b3f2014-05-01 09:08:48 -0400168 public void requestZenModeConditions(IConditionListener callback, int relevance) {
John Spurlocke77bb362014-04-26 10:24:59 -0400169 synchronized(mMutex) {
170 if (DEBUG) Slog.d(TAG, "requestZenModeConditions callback=" + callback
John Spurlock3b98b3f2014-05-01 09:08:48 -0400171 + " relevance=" + Condition.relevanceToString(relevance));
John Spurlocke77bb362014-04-26 10:24:59 -0400172 if (callback == null) return;
John Spurlock3b98b3f2014-05-01 09:08:48 -0400173 relevance = relevance & (Condition.FLAG_RELEVANT_NOW | Condition.FLAG_RELEVANT_ALWAYS);
174 if (relevance != 0) {
John Spurlocke77bb362014-04-26 10:24:59 -0400175 mListeners.put(callback.asBinder(), callback);
John Spurlock3b98b3f2014-05-01 09:08:48 -0400176 requestConditionsLocked(relevance);
John Spurlocke77bb362014-04-26 10:24:59 -0400177 } else {
178 mListeners.remove(callback.asBinder());
179 if (mListeners.isEmpty()) {
180 requestConditionsLocked(0);
181 }
182 }
183 }
184 }
185
John Spurlock3b98b3f2014-05-01 09:08:48 -0400186 private Condition[] validateConditions(String pkg, Condition[] conditions) {
187 if (conditions == null || conditions.length == 0) return null;
188 final int N = conditions.length;
189 final ArrayMap<Uri, Condition> valid = new ArrayMap<Uri, Condition>(N);
190 for (int i = 0; i < N; i++) {
191 final Uri id = conditions[i].id;
192 if (!Condition.isValidId(id, pkg)) {
193 Slog.w(TAG, "Ignoring condition from " + pkg + " for invalid id: " + id);
194 continue;
195 }
196 if (valid.containsKey(id)) {
197 Slog.w(TAG, "Ignoring condition from " + pkg + " for duplicate id: " + id);
198 continue;
199 }
200 valid.put(id, conditions[i]);
201 }
202 if (valid.size() == 0) return null;
203 if (valid.size() == N) return conditions;
204 final Condition[] rt = new Condition[valid.size()];
205 for (int i = 0; i < rt.length; i++) {
206 rt[i] = valid.valueAt(i);
207 }
208 return rt;
209 }
210
211 private ConditionRecord getRecordLocked(Uri id, ComponentName component) {
212 final int N = mRecords.size();
213 for (int i = 0; i < N; i++) {
214 final ConditionRecord r = mRecords.get(i);
215 if (r.id.equals(id) && r.component.equals(component)) {
216 return r;
217 }
218 }
219 final ConditionRecord r = new ConditionRecord(id, component);
220 mRecords.add(r);
221 return r;
222 }
223
John Spurlocke77bb362014-04-26 10:24:59 -0400224 public void notifyConditions(String pkg, ManagedServiceInfo info, Condition[] conditions) {
225 synchronized(mMutex) {
226 if (DEBUG) Slog.d(TAG, "notifyConditions pkg=" + pkg + " info=" + info + " conditions="
227 + (conditions == null ? null : Arrays.asList(conditions)));
John Spurlock3b98b3f2014-05-01 09:08:48 -0400228 conditions = validateConditions(pkg, conditions);
John Spurlocke77bb362014-04-26 10:24:59 -0400229 if (conditions == null || conditions.length == 0) return;
230 final int N = conditions.length;
John Spurlocke77bb362014-04-26 10:24:59 -0400231 for (IConditionListener listener : mListeners.values()) {
232 try {
233 listener.onConditionsReceived(conditions);
234 } catch (RemoteException e) {
235 Slog.w(TAG, "Error sending conditions to listener " + listener, e);
236 }
237 }
John Spurlock3b98b3f2014-05-01 09:08:48 -0400238 for (int i = 0; i < N; i++) {
239 final Condition c = conditions[i];
240 final ConditionRecord r = getRecordLocked(c.id, info.component);
241 r.info = info;
242 r.condition = c;
243 // if manual, exit zen if false (or failed)
244 if (r.isManual) {
245 if (c.state == Condition.STATE_FALSE || c.state == Condition.STATE_ERROR) {
246 final boolean failed = c.state == Condition.STATE_ERROR;
247 if (failed) {
248 Slog.w(TAG, "Exit zen: manual condition failed: " + c);
249 } else if (DEBUG) {
250 Slog.d(TAG, "Exit zen: manual condition false: " + c);
251 }
252 mZenModeHelper.setZenMode(Settings.Global.ZEN_MODE_OFF);
253 unsubscribeLocked(r);
254 r.isManual = false;
255 }
256 }
257 // if automatic, exit zen if false (or failed), enter zen if true
258 if (r.isAutomatic) {
259 if (c.state == Condition.STATE_FALSE || c.state == Condition.STATE_ERROR) {
260 final boolean failed = c.state == Condition.STATE_ERROR;
261 if (failed) {
262 Slog.w(TAG, "Exit zen: automatic condition failed: " + c);
263 } else if (DEBUG) {
264 Slog.d(TAG, "Exit zen: automatic condition false: " + c);
265 }
266 mZenModeHelper.setZenMode(Settings.Global.ZEN_MODE_OFF);
267 } else if (c.state == Condition.STATE_TRUE) {
268 Slog.d(TAG, "Enter zen: automatic condition true: " + c);
John Spurlockae641c92014-06-30 18:11:40 -0400269 mZenModeHelper.setZenMode(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS);
John Spurlocke77bb362014-04-26 10:24:59 -0400270 }
271 }
272 }
273 }
274 }
275
John Spurlock6ae82a72014-07-16 16:23:01 -0400276 public void setZenModeCondition(Uri conditionId, String reason) {
John Spurlock3b98b3f2014-05-01 09:08:48 -0400277 if (DEBUG) Slog.d(TAG, "setZenModeCondition " + conditionId);
John Spurlocke77bb362014-04-26 10:24:59 -0400278 synchronized(mMutex) {
John Spurlock50806fc2014-07-15 10:22:02 -0400279 ComponentName conditionComponent = null;
John Spurlock856edeb2014-06-01 20:36:47 -0400280 if (ZenModeConfig.isValidCountdownConditionId(conditionId)) {
281 // constructed by the client, make sure the record exists...
282 final ConditionRecord r = getRecordLocked(conditionId,
283 CountdownConditionProvider.COMPONENT);
284 if (r.info == null) {
285 // ... and is associated with the in-process service
286 r.info = checkServiceTokenLocked(mCountdown.asInterface());
287 }
288 }
John Spurlock3b98b3f2014-05-01 09:08:48 -0400289 final int N = mRecords.size();
290 for (int i = 0; i < N; i++) {
291 final ConditionRecord r = mRecords.get(i);
292 final boolean idEqual = r.id.equals(conditionId);
293 if (r.isManual && !idEqual) {
294 // was previous manual condition, unsubscribe
295 unsubscribeLocked(r);
296 r.isManual = false;
297 } else if (idEqual && !r.isManual) {
298 // is new manual condition, subscribe
299 subscribeLocked(r);
300 r.isManual = true;
John Spurlocke77bb362014-04-26 10:24:59 -0400301 }
John Spurlock50806fc2014-07-15 10:22:02 -0400302 if (idEqual) {
303 conditionComponent = r.component;
304 }
John Spurlocke77bb362014-04-26 10:24:59 -0400305 }
John Spurlock856edeb2014-06-01 20:36:47 -0400306 if (!Objects.equals(mExitConditionId, conditionId)) {
307 mExitConditionId = conditionId;
John Spurlock50806fc2014-07-15 10:22:02 -0400308 mExitConditionComponent = conditionComponent;
John Spurlock6ae82a72014-07-16 16:23:01 -0400309 ZenLog.traceExitCondition(mExitConditionId, mExitConditionComponent, reason);
John Spurlock856edeb2014-06-01 20:36:47 -0400310 saveZenConfigLocked();
311 }
John Spurlocke77bb362014-04-26 10:24:59 -0400312 }
313 }
314
John Spurlock3b98b3f2014-05-01 09:08:48 -0400315 private void subscribeLocked(ConditionRecord r) {
316 if (DEBUG) Slog.d(TAG, "subscribeLocked " + r);
317 final IConditionProvider provider = provider(r);
John Spurlock6ae82a72014-07-16 16:23:01 -0400318 RemoteException re = null;
319 if (provider != null) {
320 try {
321 provider.onSubscribe(r.id);
322 } catch (RemoteException e) {
323 Slog.w(TAG, "Error subscribing to " + r, e);
324 re = e;
325 }
John Spurlocke77bb362014-04-26 10:24:59 -0400326 }
John Spurlock6ae82a72014-07-16 16:23:01 -0400327 ZenLog.traceSubscribe(r != null ? r.id : null, provider, re);
John Spurlock3b98b3f2014-05-01 09:08:48 -0400328 }
329
330 private static <T> ArraySet<T> safeSet(T... items) {
331 final ArraySet<T> rt = new ArraySet<T>();
332 if (items == null || items.length == 0) return rt;
333 final int N = items.length;
334 for (int i = 0; i < N; i++) {
335 final T item = items[i];
336 if (item != null) {
337 rt.add(item);
338 }
339 }
340 return rt;
341 }
342
343 public void setAutomaticZenModeConditions(Uri[] conditionIds) {
344 setAutomaticZenModeConditions(conditionIds, true /*save*/);
345 }
346
347 private void setAutomaticZenModeConditions(Uri[] conditionIds, boolean save) {
348 if (DEBUG) Slog.d(TAG, "setAutomaticZenModeConditions "
349 + (conditionIds == null ? null : Arrays.asList(conditionIds)));
350 synchronized(mMutex) {
351 final ArraySet<Uri> newIds = safeSet(conditionIds);
352 final int N = mRecords.size();
353 boolean changed = false;
354 for (int i = 0; i < N; i++) {
355 final ConditionRecord r = mRecords.get(i);
356 final boolean automatic = newIds.contains(r.id);
357 if (!r.isAutomatic && automatic) {
358 // subscribe to new automatic
359 subscribeLocked(r);
360 r.isAutomatic = true;
361 changed = true;
362 } else if (r.isAutomatic && !automatic) {
363 // unsubscribe from old automatic
364 unsubscribeLocked(r);
365 r.isAutomatic = false;
366 changed = true;
367 }
368 }
369 if (save && changed) {
370 saveZenConfigLocked();
371 }
372 }
373 }
374
375 public Condition[] getAutomaticZenModeConditions() {
376 synchronized(mMutex) {
377 final int N = mRecords.size();
378 ArrayList<Condition> rt = null;
379 for (int i = 0; i < N; i++) {
380 final ConditionRecord r = mRecords.get(i);
381 if (r.isAutomatic && r.condition != null) {
382 if (rt == null) rt = new ArrayList<Condition>();
383 rt.add(r.condition);
384 }
385 }
386 return rt == null ? NO_CONDITIONS : rt.toArray(new Condition[rt.size()]);
387 }
388 }
389
390 private void unsubscribeLocked(ConditionRecord r) {
391 if (DEBUG) Slog.d(TAG, "unsubscribeLocked " + r);
392 final IConditionProvider provider = provider(r);
John Spurlock6ae82a72014-07-16 16:23:01 -0400393 RemoteException re = null;
394 if (provider != null) {
395 try {
396 provider.onUnsubscribe(r.id);
397 } catch (RemoteException e) {
398 Slog.w(TAG, "Error unsubscribing to " + r, e);
399 re = e;
400 }
John Spurlock3b98b3f2014-05-01 09:08:48 -0400401 }
John Spurlock6ae82a72014-07-16 16:23:01 -0400402 ZenLog.traceUnsubscribe(r != null ? r.id : null, provider, re);
John Spurlock3b98b3f2014-05-01 09:08:48 -0400403 }
404
405 private static IConditionProvider provider(ConditionRecord r) {
406 return r == null ? null : provider(r.info);
John Spurlocke77bb362014-04-26 10:24:59 -0400407 }
408
409 private static IConditionProvider provider(ManagedServiceInfo info) {
410 return info == null ? null : (IConditionProvider) info.service;
411 }
412
413 private void requestConditionsLocked(int flags) {
414 for (ManagedServiceInfo info : mServices) {
415 final IConditionProvider provider = provider(info);
416 if (provider == null) continue;
John Spurlock50806fc2014-07-15 10:22:02 -0400417 // clear all stored conditions from this provider that we no longer care about
418 for (int i = mRecords.size() - 1; i >= 0; i--) {
419 final ConditionRecord r = mRecords.get(i);
420 if (r.info != info) continue;
421 if (r.isManual || r.isAutomatic) continue;
422 mRecords.remove(i);
423 }
John Spurlocke77bb362014-04-26 10:24:59 -0400424 try {
425 provider.onRequestConditions(flags);
426 } catch (RemoteException e) {
427 Slog.w(TAG, "Error requesting conditions from " + info.component, e);
428 }
429 }
John Spurlock7340fc82014-04-24 18:50:12 -0400430 }
John Spurlock1c923a32014-04-27 16:42:29 -0400431
John Spurlock3b98b3f2014-05-01 09:08:48 -0400432 private void loadZenConfig() {
433 final ZenModeConfig config = mZenModeHelper.getConfig();
434 if (config == null) {
435 if (DEBUG) Slog.d(TAG, "loadZenConfig: no config");
436 return;
437 }
438 synchronized (mMutex) {
John Spurlock6ae82a72014-07-16 16:23:01 -0400439 final boolean changingExit = !Objects.equals(mExitConditionId, config.exitConditionId);
John Spurlock856edeb2014-06-01 20:36:47 -0400440 mExitConditionId = config.exitConditionId;
John Spurlock50806fc2014-07-15 10:22:02 -0400441 mExitConditionComponent = config.exitConditionComponent;
John Spurlock6ae82a72014-07-16 16:23:01 -0400442 if (changingExit) {
443 ZenLog.traceExitCondition(mExitConditionId, mExitConditionComponent, "config");
444 }
John Spurlock3b98b3f2014-05-01 09:08:48 -0400445 if (config.conditionComponents == null || config.conditionIds == null
446 || config.conditionComponents.length != config.conditionIds.length) {
447 if (DEBUG) Slog.d(TAG, "loadZenConfig: no conditions");
448 setAutomaticZenModeConditions(null, false /*save*/);
449 return;
450 }
451 final ArraySet<Uri> newIds = new ArraySet<Uri>();
452 final int N = config.conditionComponents.length;
453 for (int i = 0; i < N; i++) {
454 final ComponentName component = config.conditionComponents[i];
455 final Uri id = config.conditionIds[i];
456 if (component != null && id != null) {
457 getRecordLocked(id, component); // ensure record exists
458 newIds.add(id);
459 }
460 }
461 if (DEBUG) Slog.d(TAG, "loadZenConfig: N=" + N);
462 setAutomaticZenModeConditions(newIds.toArray(new Uri[newIds.size()]), false /*save*/);
463 }
464 }
465
466 private void saveZenConfigLocked() {
467 ZenModeConfig config = mZenModeHelper.getConfig();
468 if (config == null) return;
469 config = config.copy();
470 final ArrayList<ConditionRecord> automatic = new ArrayList<ConditionRecord>();
471 final int automaticN = mRecords.size();
472 for (int i = 0; i < automaticN; i++) {
473 final ConditionRecord r = mRecords.get(i);
474 if (r.isAutomatic) {
475 automatic.add(r);
476 }
477 }
478 if (automatic.isEmpty()) {
479 config.conditionComponents = null;
480 config.conditionIds = null;
481 } else {
482 final int N = automatic.size();
483 config.conditionComponents = new ComponentName[N];
484 config.conditionIds = new Uri[N];
485 for (int i = 0; i < N; i++) {
486 final ConditionRecord r = automatic.get(i);
487 config.conditionComponents[i] = r.component;
488 config.conditionIds[i] = r.id;
489 }
490 }
John Spurlock856edeb2014-06-01 20:36:47 -0400491 config.exitConditionId = mExitConditionId;
John Spurlock50806fc2014-07-15 10:22:02 -0400492 config.exitConditionComponent = mExitConditionComponent;
John Spurlock3b98b3f2014-05-01 09:08:48 -0400493 if (DEBUG) Slog.d(TAG, "Setting zen config to: " + config);
494 mZenModeHelper.setConfig(config);
495 }
496
John Spurlock1c923a32014-04-27 16:42:29 -0400497 private class ZenModeHelperCallback extends ZenModeHelper.Callback {
498 @Override
John Spurlock3b98b3f2014-05-01 09:08:48 -0400499 void onConfigChanged() {
500 loadZenConfig();
501 }
502
503 @Override
John Spurlock1c923a32014-04-27 16:42:29 -0400504 void onZenModeChanged() {
505 final int mode = mZenModeHelper.getZenMode();
506 if (mode == Global.ZEN_MODE_OFF) {
John Spurlock3b98b3f2014-05-01 09:08:48 -0400507 // ensure any manual condition is cleared
John Spurlock6ae82a72014-07-16 16:23:01 -0400508 setZenModeCondition(null, "zenOff");
John Spurlock1c923a32014-04-27 16:42:29 -0400509 }
510 }
511 }
John Spurlock3b98b3f2014-05-01 09:08:48 -0400512
513 private static class ConditionRecord {
514 public final Uri id;
515 public final ComponentName component;
516 public Condition condition;
517 public ManagedServiceInfo info;
518 public boolean isAutomatic;
519 public boolean isManual;
520
521 private ConditionRecord(Uri id, ComponentName component) {
522 this.id = id;
523 this.component = component;
524 }
525
526 @Override
527 public String toString() {
528 final StringBuilder sb = new StringBuilder("ConditionRecord[id=")
529 .append(id).append(",component=").append(component);
530 if (isAutomatic) sb.append(",automatic");
531 if (isManual) sb.append(",manual");
532 return sb.append(']').toString();
533 }
534 }
John Spurlock7340fc82014-04-24 18:50:12 -0400535}