blob: fc7b5a99962e80581073fe78410a661f6e3b8f60 [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();
John Spurlock4db0d982014-08-13 09:19:03 -040054 private final DowntimeConditionProvider mDowntime = new DowntimeConditionProvider();
John Spurlock37bc92c2014-11-03 11:01:51 -050055 private final NextAlarmConditionProvider mNextAlarm = new NextAlarmConditionProvider();
John Spurlock856edeb2014-06-01 20:36:47 -040056
John Spurlock4db0d982014-08-13 09:19:03 -040057 private Condition mExitCondition;
John Spurlock50806fc2014-07-15 10:22:02 -040058 private ComponentName mExitConditionComponent;
John Spurlocke77bb362014-04-26 10:24:59 -040059
John Spurlock7340fc82014-04-24 18:50:12 -040060 public ConditionProviders(Context context, Handler handler,
John Spurlocke77bb362014-04-26 10:24:59 -040061 UserProfiles userProfiles, ZenModeHelper zenModeHelper) {
62 super(context, handler, new Object(), userProfiles);
63 mZenModeHelper = zenModeHelper;
John Spurlock1c923a32014-04-27 16:42:29 -040064 mZenModeHelper.addCallback(new ZenModeHelperCallback());
John Spurlock3b98b3f2014-05-01 09:08:48 -040065 loadZenConfig();
John Spurlock7340fc82014-04-24 18:50:12 -040066 }
67
68 @Override
69 protected Config getConfig() {
70 Config c = new Config();
71 c.caption = "condition provider";
72 c.serviceInterface = ConditionProviderService.SERVICE_INTERFACE;
73 c.secureSettingName = Settings.Secure.ENABLED_CONDITION_PROVIDERS;
74 c.bindPermission = android.Manifest.permission.BIND_CONDITION_PROVIDER_SERVICE;
75 c.settingsAction = Settings.ACTION_CONDITION_PROVIDER_SETTINGS;
76 c.clientLabel = R.string.condition_provider_service_binding_label;
77 return c;
78 }
79
80 @Override
John Spurlock25e2d242014-06-27 13:58:23 -040081 public void dump(PrintWriter pw, DumpFilter filter) {
82 super.dump(pw, filter);
John Spurlocke77bb362014-04-26 10:24:59 -040083 synchronized(mMutex) {
John Spurlock25e2d242014-06-27 13:58:23 -040084 if (filter == null) {
85 pw.print(" mListeners("); pw.print(mListeners.size()); pw.println("):");
86 for (int i = 0; i < mListeners.size(); i++) {
87 pw.print(" "); pw.println(mListeners.keyAt(i));
88 }
John Spurlocke77bb362014-04-26 10:24:59 -040089 }
John Spurlock3b98b3f2014-05-01 09:08:48 -040090 pw.print(" mRecords("); pw.print(mRecords.size()); pw.println("):");
91 for (int i = 0; i < mRecords.size(); i++) {
John Spurlock856edeb2014-06-01 20:36:47 -040092 final ConditionRecord r = mRecords.get(i);
John Spurlock25e2d242014-06-27 13:58:23 -040093 if (filter != null && !filter.matches(r.component)) continue;
John Spurlock856edeb2014-06-01 20:36:47 -040094 pw.print(" "); pw.println(r);
95 final String countdownDesc = CountdownConditionProvider.tryParseDescription(r.id);
96 if (countdownDesc != null) {
97 pw.print(" ("); pw.print(countdownDesc); pw.println(")");
98 }
John Spurlocke77bb362014-04-26 10:24:59 -040099 }
100 }
John Spurlock50806fc2014-07-15 10:22:02 -0400101 mCountdown.dump(pw, filter);
John Spurlock4db0d982014-08-13 09:19:03 -0400102 mDowntime.dump(pw, filter);
John Spurlock37bc92c2014-11-03 11:01:51 -0500103 mNextAlarm.dump(pw, filter);
John Spurlocke77bb362014-04-26 10:24:59 -0400104 }
105
106 @Override
John Spurlock7340fc82014-04-24 18:50:12 -0400107 protected IInterface asInterface(IBinder binder) {
108 return IConditionProvider.Stub.asInterface(binder);
109 }
110
111 @Override
John Spurlock856edeb2014-06-01 20:36:47 -0400112 public void onBootPhaseAppsCanStart() {
113 super.onBootPhaseAppsCanStart();
114 mCountdown.attachBase(mContext);
115 registerService(mCountdown.asInterface(), CountdownConditionProvider.COMPONENT,
116 UserHandle.USER_OWNER);
John Spurlock4db0d982014-08-13 09:19:03 -0400117 mDowntime.attachBase(mContext);
118 registerService(mDowntime.asInterface(), DowntimeConditionProvider.COMPONENT,
119 UserHandle.USER_OWNER);
120 mDowntime.setCallback(new DowntimeCallback());
John Spurlock37bc92c2014-11-03 11:01:51 -0500121 mNextAlarm.attachBase(mContext);
122 registerService(mNextAlarm.asInterface(), NextAlarmConditionProvider.COMPONENT,
123 UserHandle.USER_OWNER);
124 mNextAlarm.setCallback(new NextAlarmConditionProvider.Callback() {
125 @Override
126 public boolean isInDowntime() {
127 return mDowntime.isInDowntime();
128 }
129 });
130 }
131
132 @Override
133 public void onUserSwitched() {
134 super.onUserSwitched();
135 if (mNextAlarm != null) {
136 mNextAlarm.onUserSwitched();
137 }
John Spurlock856edeb2014-06-01 20:36:47 -0400138 }
139
140 @Override
John Spurlock3b98b3f2014-05-01 09:08:48 -0400141 protected void onServiceAdded(ManagedServiceInfo info) {
142 Slog.d(TAG, "onServiceAdded " + info);
143 final IConditionProvider provider = provider(info);
John Spurlocke77bb362014-04-26 10:24:59 -0400144 try {
145 provider.onConnected();
146 } catch (RemoteException e) {
147 // we tried
148 }
John Spurlock3b98b3f2014-05-01 09:08:48 -0400149 synchronized (mMutex) {
John Spurlock50806fc2014-07-15 10:22:02 -0400150 if (info.component.equals(mExitConditionComponent)) {
151 // ensure record exists, we'll wire it up and subscribe below
152 final ConditionRecord manualRecord =
John Spurlock4db0d982014-08-13 09:19:03 -0400153 getRecordLocked(mExitCondition.id, mExitConditionComponent);
John Spurlock50806fc2014-07-15 10:22:02 -0400154 manualRecord.isManual = true;
155 }
John Spurlock3b98b3f2014-05-01 09:08:48 -0400156 final int N = mRecords.size();
157 for(int i = 0; i < N; i++) {
158 final ConditionRecord r = mRecords.get(i);
159 if (!r.component.equals(info.component)) continue;
160 r.info = info;
John Spurlock50806fc2014-07-15 10:22:02 -0400161 // if automatic or manual, auto-subscribe
162 if (r.isAutomatic || r.isManual) {
163 subscribeLocked(r);
John Spurlock3b98b3f2014-05-01 09:08:48 -0400164 }
165 }
166 }
John Spurlocke77bb362014-04-26 10:24:59 -0400167 }
168
169 @Override
170 protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
171 if (removed == null) return;
John Spurlock3b98b3f2014-05-01 09:08:48 -0400172 for (int i = mRecords.size() - 1; i >= 0; i--) {
173 final ConditionRecord r = mRecords.get(i);
174 if (!r.component.equals(removed.component)) continue;
175 if (r.isManual) {
176 // removing the current manual condition, exit zen
John Spurlock4db0d982014-08-13 09:19:03 -0400177 mZenModeHelper.setZenMode(Global.ZEN_MODE_OFF, "manualServiceRemoved");
John Spurlocke86de4c2014-04-29 21:48:26 -0400178 }
John Spurlock3b98b3f2014-05-01 09:08:48 -0400179 if (r.isAutomatic) {
180 // removing an automatic condition, exit zen
John Spurlock4db0d982014-08-13 09:19:03 -0400181 mZenModeHelper.setZenMode(Global.ZEN_MODE_OFF, "automaticServiceRemoved");
John Spurlocke77bb362014-04-26 10:24:59 -0400182 }
John Spurlock3b98b3f2014-05-01 09:08:48 -0400183 mRecords.remove(i);
John Spurlocke77bb362014-04-26 10:24:59 -0400184 }
185 }
186
187 public ManagedServiceInfo checkServiceToken(IConditionProvider provider) {
188 synchronized(mMutex) {
189 return checkServiceTokenLocked(provider);
190 }
191 }
192
John Spurlock3b98b3f2014-05-01 09:08:48 -0400193 public void requestZenModeConditions(IConditionListener callback, int relevance) {
John Spurlocke77bb362014-04-26 10:24:59 -0400194 synchronized(mMutex) {
195 if (DEBUG) Slog.d(TAG, "requestZenModeConditions callback=" + callback
John Spurlock3b98b3f2014-05-01 09:08:48 -0400196 + " relevance=" + Condition.relevanceToString(relevance));
John Spurlocke77bb362014-04-26 10:24:59 -0400197 if (callback == null) return;
John Spurlock3b98b3f2014-05-01 09:08:48 -0400198 relevance = relevance & (Condition.FLAG_RELEVANT_NOW | Condition.FLAG_RELEVANT_ALWAYS);
199 if (relevance != 0) {
John Spurlocke77bb362014-04-26 10:24:59 -0400200 mListeners.put(callback.asBinder(), callback);
John Spurlock3b98b3f2014-05-01 09:08:48 -0400201 requestConditionsLocked(relevance);
John Spurlocke77bb362014-04-26 10:24:59 -0400202 } else {
203 mListeners.remove(callback.asBinder());
204 if (mListeners.isEmpty()) {
205 requestConditionsLocked(0);
206 }
207 }
208 }
209 }
210
John Spurlock3b98b3f2014-05-01 09:08:48 -0400211 private Condition[] validateConditions(String pkg, Condition[] conditions) {
212 if (conditions == null || conditions.length == 0) return null;
213 final int N = conditions.length;
214 final ArrayMap<Uri, Condition> valid = new ArrayMap<Uri, Condition>(N);
215 for (int i = 0; i < N; i++) {
216 final Uri id = conditions[i].id;
217 if (!Condition.isValidId(id, pkg)) {
218 Slog.w(TAG, "Ignoring condition from " + pkg + " for invalid id: " + id);
219 continue;
220 }
221 if (valid.containsKey(id)) {
222 Slog.w(TAG, "Ignoring condition from " + pkg + " for duplicate id: " + id);
223 continue;
224 }
225 valid.put(id, conditions[i]);
226 }
227 if (valid.size() == 0) return null;
228 if (valid.size() == N) return conditions;
229 final Condition[] rt = new Condition[valid.size()];
230 for (int i = 0; i < rt.length; i++) {
231 rt[i] = valid.valueAt(i);
232 }
233 return rt;
234 }
235
236 private ConditionRecord getRecordLocked(Uri id, ComponentName component) {
237 final int N = mRecords.size();
238 for (int i = 0; i < N; i++) {
239 final ConditionRecord r = mRecords.get(i);
240 if (r.id.equals(id) && r.component.equals(component)) {
241 return r;
242 }
243 }
244 final ConditionRecord r = new ConditionRecord(id, component);
245 mRecords.add(r);
246 return r;
247 }
248
John Spurlocke77bb362014-04-26 10:24:59 -0400249 public void notifyConditions(String pkg, ManagedServiceInfo info, Condition[] conditions) {
250 synchronized(mMutex) {
251 if (DEBUG) Slog.d(TAG, "notifyConditions pkg=" + pkg + " info=" + info + " conditions="
252 + (conditions == null ? null : Arrays.asList(conditions)));
John Spurlock3b98b3f2014-05-01 09:08:48 -0400253 conditions = validateConditions(pkg, conditions);
John Spurlocke77bb362014-04-26 10:24:59 -0400254 if (conditions == null || conditions.length == 0) return;
255 final int N = conditions.length;
John Spurlocke77bb362014-04-26 10:24:59 -0400256 for (IConditionListener listener : mListeners.values()) {
257 try {
258 listener.onConditionsReceived(conditions);
259 } catch (RemoteException e) {
260 Slog.w(TAG, "Error sending conditions to listener " + listener, e);
261 }
262 }
John Spurlock3b98b3f2014-05-01 09:08:48 -0400263 for (int i = 0; i < N; i++) {
264 final Condition c = conditions[i];
265 final ConditionRecord r = getRecordLocked(c.id, info.component);
266 r.info = info;
267 r.condition = c;
268 // if manual, exit zen if false (or failed)
269 if (r.isManual) {
270 if (c.state == Condition.STATE_FALSE || c.state == Condition.STATE_ERROR) {
271 final boolean failed = c.state == Condition.STATE_ERROR;
272 if (failed) {
273 Slog.w(TAG, "Exit zen: manual condition failed: " + c);
274 } else if (DEBUG) {
275 Slog.d(TAG, "Exit zen: manual condition false: " + c);
276 }
John Spurlock4db0d982014-08-13 09:19:03 -0400277 mZenModeHelper.setZenMode(Settings.Global.ZEN_MODE_OFF,
278 "manualConditionExit");
John Spurlock3b98b3f2014-05-01 09:08:48 -0400279 unsubscribeLocked(r);
280 r.isManual = false;
281 }
282 }
283 // if automatic, exit zen if false (or failed), enter zen if true
284 if (r.isAutomatic) {
285 if (c.state == Condition.STATE_FALSE || c.state == Condition.STATE_ERROR) {
286 final boolean failed = c.state == Condition.STATE_ERROR;
287 if (failed) {
288 Slog.w(TAG, "Exit zen: automatic condition failed: " + c);
289 } else if (DEBUG) {
290 Slog.d(TAG, "Exit zen: automatic condition false: " + c);
291 }
John Spurlock4db0d982014-08-13 09:19:03 -0400292 mZenModeHelper.setZenMode(Settings.Global.ZEN_MODE_OFF,
293 "automaticConditionExit");
John Spurlock3b98b3f2014-05-01 09:08:48 -0400294 } else if (c.state == Condition.STATE_TRUE) {
295 Slog.d(TAG, "Enter zen: automatic condition true: " + c);
John Spurlock4db0d982014-08-13 09:19:03 -0400296 mZenModeHelper.setZenMode(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS,
297 "automaticConditionEnter");
John Spurlocke77bb362014-04-26 10:24:59 -0400298 }
299 }
300 }
301 }
302 }
303
John Spurlock4db0d982014-08-13 09:19:03 -0400304 public void setZenModeCondition(Condition condition, String reason) {
305 if (DEBUG) Slog.d(TAG, "setZenModeCondition " + condition);
John Spurlocke77bb362014-04-26 10:24:59 -0400306 synchronized(mMutex) {
John Spurlock50806fc2014-07-15 10:22:02 -0400307 ComponentName conditionComponent = null;
John Spurlock4db0d982014-08-13 09:19:03 -0400308 if (condition != null) {
309 if (ZenModeConfig.isValidCountdownConditionId(condition.id)) {
310 // constructed by the client, make sure the record exists...
311 final ConditionRecord r = getRecordLocked(condition.id,
312 CountdownConditionProvider.COMPONENT);
313 if (r.info == null) {
314 // ... and is associated with the in-process service
315 r.info = checkServiceTokenLocked(mCountdown.asInterface());
316 }
317 }
318 if (ZenModeConfig.isValidDowntimeConditionId(condition.id)) {
319 // constructed by the client, make sure the record exists...
320 final ConditionRecord r = getRecordLocked(condition.id,
321 DowntimeConditionProvider.COMPONENT);
322 if (r.info == null) {
323 // ... and is associated with the in-process service
324 r.info = checkServiceTokenLocked(mDowntime.asInterface());
325 }
John Spurlock856edeb2014-06-01 20:36:47 -0400326 }
327 }
John Spurlock3b98b3f2014-05-01 09:08:48 -0400328 final int N = mRecords.size();
329 for (int i = 0; i < N; i++) {
330 final ConditionRecord r = mRecords.get(i);
John Spurlock4db0d982014-08-13 09:19:03 -0400331 final boolean idEqual = condition != null && r.id.equals(condition.id);
John Spurlock3b98b3f2014-05-01 09:08:48 -0400332 if (r.isManual && !idEqual) {
333 // was previous manual condition, unsubscribe
334 unsubscribeLocked(r);
335 r.isManual = false;
336 } else if (idEqual && !r.isManual) {
337 // is new manual condition, subscribe
338 subscribeLocked(r);
339 r.isManual = true;
John Spurlocke77bb362014-04-26 10:24:59 -0400340 }
John Spurlock50806fc2014-07-15 10:22:02 -0400341 if (idEqual) {
342 conditionComponent = r.component;
343 }
John Spurlocke77bb362014-04-26 10:24:59 -0400344 }
John Spurlock4db0d982014-08-13 09:19:03 -0400345 if (!Objects.equals(mExitCondition, condition)) {
346 mExitCondition = condition;
John Spurlock50806fc2014-07-15 10:22:02 -0400347 mExitConditionComponent = conditionComponent;
John Spurlock4db0d982014-08-13 09:19:03 -0400348 ZenLog.traceExitCondition(mExitCondition, mExitConditionComponent, reason);
John Spurlock856edeb2014-06-01 20:36:47 -0400349 saveZenConfigLocked();
350 }
John Spurlocke77bb362014-04-26 10:24:59 -0400351 }
352 }
353
John Spurlock3b98b3f2014-05-01 09:08:48 -0400354 private void subscribeLocked(ConditionRecord r) {
355 if (DEBUG) Slog.d(TAG, "subscribeLocked " + r);
356 final IConditionProvider provider = provider(r);
John Spurlock6ae82a72014-07-16 16:23:01 -0400357 RemoteException re = null;
358 if (provider != null) {
359 try {
John Spurlock4db0d982014-08-13 09:19:03 -0400360 Slog.d(TAG, "Subscribing to " + r.id + " with " + provider);
John Spurlock6ae82a72014-07-16 16:23:01 -0400361 provider.onSubscribe(r.id);
362 } catch (RemoteException e) {
363 Slog.w(TAG, "Error subscribing to " + r, e);
364 re = e;
365 }
John Spurlocke77bb362014-04-26 10:24:59 -0400366 }
John Spurlock6ae82a72014-07-16 16:23:01 -0400367 ZenLog.traceSubscribe(r != null ? r.id : null, provider, re);
John Spurlock3b98b3f2014-05-01 09:08:48 -0400368 }
369
370 private static <T> ArraySet<T> safeSet(T... items) {
371 final ArraySet<T> rt = new ArraySet<T>();
372 if (items == null || items.length == 0) return rt;
373 final int N = items.length;
374 for (int i = 0; i < N; i++) {
375 final T item = items[i];
376 if (item != null) {
377 rt.add(item);
378 }
379 }
380 return rt;
381 }
382
383 public void setAutomaticZenModeConditions(Uri[] conditionIds) {
384 setAutomaticZenModeConditions(conditionIds, true /*save*/);
385 }
386
387 private void setAutomaticZenModeConditions(Uri[] conditionIds, boolean save) {
388 if (DEBUG) Slog.d(TAG, "setAutomaticZenModeConditions "
389 + (conditionIds == null ? null : Arrays.asList(conditionIds)));
390 synchronized(mMutex) {
391 final ArraySet<Uri> newIds = safeSet(conditionIds);
392 final int N = mRecords.size();
393 boolean changed = false;
394 for (int i = 0; i < N; i++) {
395 final ConditionRecord r = mRecords.get(i);
396 final boolean automatic = newIds.contains(r.id);
397 if (!r.isAutomatic && automatic) {
398 // subscribe to new automatic
399 subscribeLocked(r);
400 r.isAutomatic = true;
401 changed = true;
402 } else if (r.isAutomatic && !automatic) {
403 // unsubscribe from old automatic
404 unsubscribeLocked(r);
405 r.isAutomatic = false;
406 changed = true;
407 }
408 }
409 if (save && changed) {
410 saveZenConfigLocked();
411 }
412 }
413 }
414
415 public Condition[] getAutomaticZenModeConditions() {
416 synchronized(mMutex) {
417 final int N = mRecords.size();
418 ArrayList<Condition> rt = null;
419 for (int i = 0; i < N; i++) {
420 final ConditionRecord r = mRecords.get(i);
421 if (r.isAutomatic && r.condition != null) {
422 if (rt == null) rt = new ArrayList<Condition>();
423 rt.add(r.condition);
424 }
425 }
426 return rt == null ? NO_CONDITIONS : rt.toArray(new Condition[rt.size()]);
427 }
428 }
429
430 private void unsubscribeLocked(ConditionRecord r) {
431 if (DEBUG) Slog.d(TAG, "unsubscribeLocked " + r);
432 final IConditionProvider provider = provider(r);
John Spurlock6ae82a72014-07-16 16:23:01 -0400433 RemoteException re = null;
434 if (provider != null) {
435 try {
436 provider.onUnsubscribe(r.id);
437 } catch (RemoteException e) {
438 Slog.w(TAG, "Error unsubscribing to " + r, e);
439 re = e;
440 }
John Spurlock3b98b3f2014-05-01 09:08:48 -0400441 }
John Spurlock6ae82a72014-07-16 16:23:01 -0400442 ZenLog.traceUnsubscribe(r != null ? r.id : null, provider, re);
John Spurlock3b98b3f2014-05-01 09:08:48 -0400443 }
444
445 private static IConditionProvider provider(ConditionRecord r) {
446 return r == null ? null : provider(r.info);
John Spurlocke77bb362014-04-26 10:24:59 -0400447 }
448
449 private static IConditionProvider provider(ManagedServiceInfo info) {
450 return info == null ? null : (IConditionProvider) info.service;
451 }
452
453 private void requestConditionsLocked(int flags) {
454 for (ManagedServiceInfo info : mServices) {
455 final IConditionProvider provider = provider(info);
456 if (provider == null) continue;
John Spurlock50806fc2014-07-15 10:22:02 -0400457 // clear all stored conditions from this provider that we no longer care about
458 for (int i = mRecords.size() - 1; i >= 0; i--) {
459 final ConditionRecord r = mRecords.get(i);
460 if (r.info != info) continue;
461 if (r.isManual || r.isAutomatic) continue;
462 mRecords.remove(i);
463 }
John Spurlocke77bb362014-04-26 10:24:59 -0400464 try {
465 provider.onRequestConditions(flags);
466 } catch (RemoteException e) {
467 Slog.w(TAG, "Error requesting conditions from " + info.component, e);
468 }
469 }
John Spurlock7340fc82014-04-24 18:50:12 -0400470 }
John Spurlock1c923a32014-04-27 16:42:29 -0400471
John Spurlock3b98b3f2014-05-01 09:08:48 -0400472 private void loadZenConfig() {
473 final ZenModeConfig config = mZenModeHelper.getConfig();
474 if (config == null) {
475 if (DEBUG) Slog.d(TAG, "loadZenConfig: no config");
476 return;
477 }
478 synchronized (mMutex) {
John Spurlock4db0d982014-08-13 09:19:03 -0400479 final boolean changingExit = !Objects.equals(mExitCondition, config.exitCondition);
480 mExitCondition = config.exitCondition;
John Spurlock50806fc2014-07-15 10:22:02 -0400481 mExitConditionComponent = config.exitConditionComponent;
John Spurlock6ae82a72014-07-16 16:23:01 -0400482 if (changingExit) {
John Spurlock4db0d982014-08-13 09:19:03 -0400483 ZenLog.traceExitCondition(mExitCondition, mExitConditionComponent, "config");
John Spurlock6ae82a72014-07-16 16:23:01 -0400484 }
John Spurlock4db0d982014-08-13 09:19:03 -0400485 mDowntime.setConfig(config);
John Spurlock3b98b3f2014-05-01 09:08:48 -0400486 if (config.conditionComponents == null || config.conditionIds == null
487 || config.conditionComponents.length != config.conditionIds.length) {
488 if (DEBUG) Slog.d(TAG, "loadZenConfig: no conditions");
489 setAutomaticZenModeConditions(null, false /*save*/);
490 return;
491 }
492 final ArraySet<Uri> newIds = new ArraySet<Uri>();
493 final int N = config.conditionComponents.length;
494 for (int i = 0; i < N; i++) {
495 final ComponentName component = config.conditionComponents[i];
496 final Uri id = config.conditionIds[i];
497 if (component != null && id != null) {
498 getRecordLocked(id, component); // ensure record exists
499 newIds.add(id);
500 }
501 }
502 if (DEBUG) Slog.d(TAG, "loadZenConfig: N=" + N);
503 setAutomaticZenModeConditions(newIds.toArray(new Uri[newIds.size()]), false /*save*/);
504 }
505 }
506
507 private void saveZenConfigLocked() {
508 ZenModeConfig config = mZenModeHelper.getConfig();
509 if (config == null) return;
510 config = config.copy();
511 final ArrayList<ConditionRecord> automatic = new ArrayList<ConditionRecord>();
512 final int automaticN = mRecords.size();
513 for (int i = 0; i < automaticN; i++) {
514 final ConditionRecord r = mRecords.get(i);
515 if (r.isAutomatic) {
516 automatic.add(r);
517 }
518 }
519 if (automatic.isEmpty()) {
520 config.conditionComponents = null;
521 config.conditionIds = null;
522 } else {
523 final int N = automatic.size();
524 config.conditionComponents = new ComponentName[N];
525 config.conditionIds = new Uri[N];
526 for (int i = 0; i < N; i++) {
527 final ConditionRecord r = automatic.get(i);
528 config.conditionComponents[i] = r.component;
529 config.conditionIds[i] = r.id;
530 }
531 }
John Spurlock4db0d982014-08-13 09:19:03 -0400532 config.exitCondition = mExitCondition;
John Spurlock50806fc2014-07-15 10:22:02 -0400533 config.exitConditionComponent = mExitConditionComponent;
John Spurlock3b98b3f2014-05-01 09:08:48 -0400534 if (DEBUG) Slog.d(TAG, "Setting zen config to: " + config);
535 mZenModeHelper.setConfig(config);
536 }
537
John Spurlock1c923a32014-04-27 16:42:29 -0400538 private class ZenModeHelperCallback extends ZenModeHelper.Callback {
539 @Override
John Spurlock3b98b3f2014-05-01 09:08:48 -0400540 void onConfigChanged() {
541 loadZenConfig();
542 }
543
544 @Override
John Spurlock1c923a32014-04-27 16:42:29 -0400545 void onZenModeChanged() {
546 final int mode = mZenModeHelper.getZenMode();
547 if (mode == Global.ZEN_MODE_OFF) {
John Spurlock3b98b3f2014-05-01 09:08:48 -0400548 // ensure any manual condition is cleared
John Spurlock6ae82a72014-07-16 16:23:01 -0400549 setZenModeCondition(null, "zenOff");
John Spurlock1c923a32014-04-27 16:42:29 -0400550 }
551 }
552 }
John Spurlock3b98b3f2014-05-01 09:08:48 -0400553
John Spurlock4db0d982014-08-13 09:19:03 -0400554 private class DowntimeCallback implements DowntimeConditionProvider.Callback {
555 @Override
556 public void onDowntimeChanged(boolean inDowntime) {
557 final int mode = mZenModeHelper.getZenMode();
558 final ZenModeConfig config = mZenModeHelper.getConfig();
559 // enter downtime
560 if (inDowntime && mode == Global.ZEN_MODE_OFF && config != null) {
561 final Condition condition = mDowntime.createCondition(config.toDowntimeInfo(),
562 Condition.STATE_TRUE);
563 mZenModeHelper.setZenMode(Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, "downtimeEnter");
564 setZenModeCondition(condition, "downtime");
565 }
566 // exit downtime
Jason Monkacb8dfe2014-09-04 10:40:08 -0400567 if (!inDowntime && mDowntime.isDowntimeCondition(mExitCondition)
568 && (mode == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
569 || mode == Global.ZEN_MODE_NO_INTERRUPTIONS)) {
John Spurlock4db0d982014-08-13 09:19:03 -0400570 mZenModeHelper.setZenMode(Global.ZEN_MODE_OFF, "downtimeExit");
571 }
572 }
573 }
574
John Spurlock3b98b3f2014-05-01 09:08:48 -0400575 private static class ConditionRecord {
576 public final Uri id;
577 public final ComponentName component;
578 public Condition condition;
579 public ManagedServiceInfo info;
580 public boolean isAutomatic;
581 public boolean isManual;
582
583 private ConditionRecord(Uri id, ComponentName component) {
584 this.id = id;
585 this.component = component;
586 }
587
588 @Override
589 public String toString() {
590 final StringBuilder sb = new StringBuilder("ConditionRecord[id=")
591 .append(id).append(",component=").append(component);
592 if (isAutomatic) sb.append(",automatic");
593 if (isManual) sb.append(",manual");
594 return sb.append(']').toString();
595 }
596 }
John Spurlock7340fc82014-04-24 18:50:12 -0400597}