blob: d0745654b65ce02030acfd50d141fef3c55e5fa9 [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 Spurlock7340fc82014-04-24 18:50:12 -040026import android.provider.Settings;
John Spurlock1c923a32014-04-27 16:42:29 -040027import android.provider.Settings.Global;
John Spurlocke77bb362014-04-26 10:24:59 -040028import android.service.notification.Condition;
John Spurlock7340fc82014-04-24 18:50:12 -040029import android.service.notification.ConditionProviderService;
John Spurlocke77bb362014-04-26 10:24:59 -040030import android.service.notification.IConditionListener;
31import android.service.notification.IConditionProvider;
John Spurlock3b98b3f2014-05-01 09:08:48 -040032import android.service.notification.ZenModeConfig;
John Spurlocke77bb362014-04-26 10:24:59 -040033import android.util.ArrayMap;
John Spurlock3b98b3f2014-05-01 09:08:48 -040034import android.util.ArraySet;
John Spurlock7340fc82014-04-24 18:50:12 -040035import android.util.Slog;
36
37import com.android.internal.R;
38
John Spurlocke77bb362014-04-26 10:24:59 -040039import java.io.PrintWriter;
John Spurlock3b98b3f2014-05-01 09:08:48 -040040import java.util.ArrayList;
John Spurlocke77bb362014-04-26 10:24:59 -040041import java.util.Arrays;
42
John Spurlock7340fc82014-04-24 18:50:12 -040043public class ConditionProviders extends ManagedServices {
John Spurlock3b98b3f2014-05-01 09:08:48 -040044 private static final Condition[] NO_CONDITIONS = new Condition[0];
John Spurlock7340fc82014-04-24 18:50:12 -040045
John Spurlocke77bb362014-04-26 10:24:59 -040046 private final ZenModeHelper mZenModeHelper;
47 private final ArrayMap<IBinder, IConditionListener> mListeners
48 = new ArrayMap<IBinder, IConditionListener>();
John Spurlock3b98b3f2014-05-01 09:08:48 -040049 private final ArrayList<ConditionRecord> mRecords = new ArrayList<ConditionRecord>();
John Spurlocke77bb362014-04-26 10:24:59 -040050
John Spurlock7340fc82014-04-24 18:50:12 -040051 public ConditionProviders(Context context, Handler handler,
John Spurlocke77bb362014-04-26 10:24:59 -040052 UserProfiles userProfiles, ZenModeHelper zenModeHelper) {
53 super(context, handler, new Object(), userProfiles);
54 mZenModeHelper = zenModeHelper;
John Spurlock1c923a32014-04-27 16:42:29 -040055 mZenModeHelper.addCallback(new ZenModeHelperCallback());
John Spurlock3b98b3f2014-05-01 09:08:48 -040056 loadZenConfig();
John Spurlock7340fc82014-04-24 18:50:12 -040057 }
58
59 @Override
60 protected Config getConfig() {
61 Config c = new Config();
62 c.caption = "condition provider";
63 c.serviceInterface = ConditionProviderService.SERVICE_INTERFACE;
64 c.secureSettingName = Settings.Secure.ENABLED_CONDITION_PROVIDERS;
65 c.bindPermission = android.Manifest.permission.BIND_CONDITION_PROVIDER_SERVICE;
66 c.settingsAction = Settings.ACTION_CONDITION_PROVIDER_SETTINGS;
67 c.clientLabel = R.string.condition_provider_service_binding_label;
68 return c;
69 }
70
71 @Override
John Spurlocke77bb362014-04-26 10:24:59 -040072 public void dump(PrintWriter pw) {
73 super.dump(pw);
74 synchronized(mMutex) {
John Spurlocke77bb362014-04-26 10:24:59 -040075 pw.print(" mListeners("); pw.print(mListeners.size()); pw.println("):");
76 for (int i = 0; i < mListeners.size(); i++) {
77 pw.print(" "); pw.println(mListeners.keyAt(i));
78 }
John Spurlock3b98b3f2014-05-01 09:08:48 -040079 pw.print(" mRecords("); pw.print(mRecords.size()); pw.println("):");
80 for (int i = 0; i < mRecords.size(); i++) {
81 pw.print(" "); pw.println(mRecords.get(i));
John Spurlocke77bb362014-04-26 10:24:59 -040082 }
83 }
84 }
85
86 @Override
John Spurlock7340fc82014-04-24 18:50:12 -040087 protected IInterface asInterface(IBinder binder) {
88 return IConditionProvider.Stub.asInterface(binder);
89 }
90
91 @Override
John Spurlock3b98b3f2014-05-01 09:08:48 -040092 protected void onServiceAdded(ManagedServiceInfo info) {
93 Slog.d(TAG, "onServiceAdded " + info);
94 final IConditionProvider provider = provider(info);
John Spurlocke77bb362014-04-26 10:24:59 -040095 try {
96 provider.onConnected();
97 } catch (RemoteException e) {
98 // we tried
99 }
John Spurlock3b98b3f2014-05-01 09:08:48 -0400100 synchronized (mMutex) {
101 final int N = mRecords.size();
102 for(int i = 0; i < N; i++) {
103 final ConditionRecord r = mRecords.get(i);
104 if (!r.component.equals(info.component)) continue;
105 r.info = info;
106 // if automatic, auto-subscribe
107 if (r.isAutomatic) {
108 try {
109 final Uri id = r.id;
110 if (DEBUG) Slog.d(TAG, "Auto-subscribing to configured condition " + id);
111 provider.onSubscribe(id);
112 } catch (RemoteException e) {
113 // we tried
114 }
115 }
116 }
117 }
John Spurlocke77bb362014-04-26 10:24:59 -0400118 }
119
120 @Override
121 protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
122 if (removed == null) return;
John Spurlock3b98b3f2014-05-01 09:08:48 -0400123 for (int i = mRecords.size() - 1; i >= 0; i--) {
124 final ConditionRecord r = mRecords.get(i);
125 if (!r.component.equals(removed.component)) continue;
126 if (r.isManual) {
127 // removing the current manual condition, exit zen
John Spurlocke86de4c2014-04-29 21:48:26 -0400128 mZenModeHelper.setZenMode(Global.ZEN_MODE_OFF);
129 }
John Spurlock3b98b3f2014-05-01 09:08:48 -0400130 if (r.isAutomatic) {
131 // removing an automatic condition, exit zen
132 mZenModeHelper.setZenMode(Global.ZEN_MODE_OFF);
John Spurlocke77bb362014-04-26 10:24:59 -0400133 }
John Spurlock3b98b3f2014-05-01 09:08:48 -0400134 mRecords.remove(i);
John Spurlocke77bb362014-04-26 10:24:59 -0400135 }
136 }
137
138 public ManagedServiceInfo checkServiceToken(IConditionProvider provider) {
139 synchronized(mMutex) {
140 return checkServiceTokenLocked(provider);
141 }
142 }
143
John Spurlock3b98b3f2014-05-01 09:08:48 -0400144 public void requestZenModeConditions(IConditionListener callback, int relevance) {
John Spurlocke77bb362014-04-26 10:24:59 -0400145 synchronized(mMutex) {
146 if (DEBUG) Slog.d(TAG, "requestZenModeConditions callback=" + callback
John Spurlock3b98b3f2014-05-01 09:08:48 -0400147 + " relevance=" + Condition.relevanceToString(relevance));
John Spurlocke77bb362014-04-26 10:24:59 -0400148 if (callback == null) return;
John Spurlock3b98b3f2014-05-01 09:08:48 -0400149 relevance = relevance & (Condition.FLAG_RELEVANT_NOW | Condition.FLAG_RELEVANT_ALWAYS);
150 if (relevance != 0) {
John Spurlocke77bb362014-04-26 10:24:59 -0400151 mListeners.put(callback.asBinder(), callback);
John Spurlock3b98b3f2014-05-01 09:08:48 -0400152 requestConditionsLocked(relevance);
John Spurlocke77bb362014-04-26 10:24:59 -0400153 } else {
154 mListeners.remove(callback.asBinder());
155 if (mListeners.isEmpty()) {
156 requestConditionsLocked(0);
157 }
158 }
159 }
160 }
161
John Spurlock3b98b3f2014-05-01 09:08:48 -0400162 private Condition[] validateConditions(String pkg, Condition[] conditions) {
163 if (conditions == null || conditions.length == 0) return null;
164 final int N = conditions.length;
165 final ArrayMap<Uri, Condition> valid = new ArrayMap<Uri, Condition>(N);
166 for (int i = 0; i < N; i++) {
167 final Uri id = conditions[i].id;
168 if (!Condition.isValidId(id, pkg)) {
169 Slog.w(TAG, "Ignoring condition from " + pkg + " for invalid id: " + id);
170 continue;
171 }
172 if (valid.containsKey(id)) {
173 Slog.w(TAG, "Ignoring condition from " + pkg + " for duplicate id: " + id);
174 continue;
175 }
176 valid.put(id, conditions[i]);
177 }
178 if (valid.size() == 0) return null;
179 if (valid.size() == N) return conditions;
180 final Condition[] rt = new Condition[valid.size()];
181 for (int i = 0; i < rt.length; i++) {
182 rt[i] = valid.valueAt(i);
183 }
184 return rt;
185 }
186
187 private ConditionRecord getRecordLocked(Uri id, ComponentName component) {
188 final int N = mRecords.size();
189 for (int i = 0; i < N; i++) {
190 final ConditionRecord r = mRecords.get(i);
191 if (r.id.equals(id) && r.component.equals(component)) {
192 return r;
193 }
194 }
195 final ConditionRecord r = new ConditionRecord(id, component);
196 mRecords.add(r);
197 return r;
198 }
199
John Spurlocke77bb362014-04-26 10:24:59 -0400200 public void notifyConditions(String pkg, ManagedServiceInfo info, Condition[] conditions) {
201 synchronized(mMutex) {
202 if (DEBUG) Slog.d(TAG, "notifyConditions pkg=" + pkg + " info=" + info + " conditions="
203 + (conditions == null ? null : Arrays.asList(conditions)));
John Spurlock3b98b3f2014-05-01 09:08:48 -0400204 conditions = validateConditions(pkg, conditions);
John Spurlocke77bb362014-04-26 10:24:59 -0400205 if (conditions == null || conditions.length == 0) return;
206 final int N = conditions.length;
John Spurlocke77bb362014-04-26 10:24:59 -0400207 for (IConditionListener listener : mListeners.values()) {
208 try {
209 listener.onConditionsReceived(conditions);
210 } catch (RemoteException e) {
211 Slog.w(TAG, "Error sending conditions to listener " + listener, e);
212 }
213 }
John Spurlock3b98b3f2014-05-01 09:08:48 -0400214 for (int i = 0; i < N; i++) {
215 final Condition c = conditions[i];
216 final ConditionRecord r = getRecordLocked(c.id, info.component);
217 r.info = info;
218 r.condition = c;
219 // if manual, exit zen if false (or failed)
220 if (r.isManual) {
221 if (c.state == Condition.STATE_FALSE || c.state == Condition.STATE_ERROR) {
222 final boolean failed = c.state == Condition.STATE_ERROR;
223 if (failed) {
224 Slog.w(TAG, "Exit zen: manual condition failed: " + c);
225 } else if (DEBUG) {
226 Slog.d(TAG, "Exit zen: manual condition false: " + c);
227 }
228 mZenModeHelper.setZenMode(Settings.Global.ZEN_MODE_OFF);
229 unsubscribeLocked(r);
230 r.isManual = false;
231 }
232 }
233 // if automatic, exit zen if false (or failed), enter zen if true
234 if (r.isAutomatic) {
235 if (c.state == Condition.STATE_FALSE || c.state == Condition.STATE_ERROR) {
236 final boolean failed = c.state == Condition.STATE_ERROR;
237 if (failed) {
238 Slog.w(TAG, "Exit zen: automatic condition failed: " + c);
239 } else if (DEBUG) {
240 Slog.d(TAG, "Exit zen: automatic condition false: " + c);
241 }
242 mZenModeHelper.setZenMode(Settings.Global.ZEN_MODE_OFF);
243 } else if (c.state == Condition.STATE_TRUE) {
244 Slog.d(TAG, "Enter zen: automatic condition true: " + c);
245 mZenModeHelper.setZenMode(Settings.Global.ZEN_MODE_ON);
John Spurlocke77bb362014-04-26 10:24:59 -0400246 }
247 }
248 }
249 }
250 }
251
John Spurlocke77bb362014-04-26 10:24:59 -0400252 public void setZenModeCondition(Uri conditionId) {
John Spurlock3b98b3f2014-05-01 09:08:48 -0400253 if (DEBUG) Slog.d(TAG, "setZenModeCondition " + conditionId);
John Spurlocke77bb362014-04-26 10:24:59 -0400254 synchronized(mMutex) {
John Spurlock3b98b3f2014-05-01 09:08:48 -0400255 final int N = mRecords.size();
256 for (int i = 0; i < N; i++) {
257 final ConditionRecord r = mRecords.get(i);
258 final boolean idEqual = r.id.equals(conditionId);
259 if (r.isManual && !idEqual) {
260 // was previous manual condition, unsubscribe
261 unsubscribeLocked(r);
262 r.isManual = false;
263 } else if (idEqual && !r.isManual) {
264 // is new manual condition, subscribe
265 subscribeLocked(r);
266 r.isManual = true;
John Spurlocke77bb362014-04-26 10:24:59 -0400267 }
268 }
John Spurlocke77bb362014-04-26 10:24:59 -0400269 }
270 }
271
John Spurlock3b98b3f2014-05-01 09:08:48 -0400272 private void subscribeLocked(ConditionRecord r) {
273 if (DEBUG) Slog.d(TAG, "subscribeLocked " + r);
274 final IConditionProvider provider = provider(r);
275 if (provider == null) {
276 Slog.w(TAG, "subscribeLocked: no provider");
277 return;
John Spurlocke77bb362014-04-26 10:24:59 -0400278 }
John Spurlock3b98b3f2014-05-01 09:08:48 -0400279 try {
280 provider.onSubscribe(r.id);
281 } catch (RemoteException e) {
282 Slog.w(TAG, "Error subscribing to " + r, e);
283 }
284 }
285
286 private static <T> ArraySet<T> safeSet(T... items) {
287 final ArraySet<T> rt = new ArraySet<T>();
288 if (items == null || items.length == 0) return rt;
289 final int N = items.length;
290 for (int i = 0; i < N; i++) {
291 final T item = items[i];
292 if (item != null) {
293 rt.add(item);
294 }
295 }
296 return rt;
297 }
298
299 public void setAutomaticZenModeConditions(Uri[] conditionIds) {
300 setAutomaticZenModeConditions(conditionIds, true /*save*/);
301 }
302
303 private void setAutomaticZenModeConditions(Uri[] conditionIds, boolean save) {
304 if (DEBUG) Slog.d(TAG, "setAutomaticZenModeConditions "
305 + (conditionIds == null ? null : Arrays.asList(conditionIds)));
306 synchronized(mMutex) {
307 final ArraySet<Uri> newIds = safeSet(conditionIds);
308 final int N = mRecords.size();
309 boolean changed = false;
310 for (int i = 0; i < N; i++) {
311 final ConditionRecord r = mRecords.get(i);
312 final boolean automatic = newIds.contains(r.id);
313 if (!r.isAutomatic && automatic) {
314 // subscribe to new automatic
315 subscribeLocked(r);
316 r.isAutomatic = true;
317 changed = true;
318 } else if (r.isAutomatic && !automatic) {
319 // unsubscribe from old automatic
320 unsubscribeLocked(r);
321 r.isAutomatic = false;
322 changed = true;
323 }
324 }
325 if (save && changed) {
326 saveZenConfigLocked();
327 }
328 }
329 }
330
331 public Condition[] getAutomaticZenModeConditions() {
332 synchronized(mMutex) {
333 final int N = mRecords.size();
334 ArrayList<Condition> rt = null;
335 for (int i = 0; i < N; i++) {
336 final ConditionRecord r = mRecords.get(i);
337 if (r.isAutomatic && r.condition != null) {
338 if (rt == null) rt = new ArrayList<Condition>();
339 rt.add(r.condition);
340 }
341 }
342 return rt == null ? NO_CONDITIONS : rt.toArray(new Condition[rt.size()]);
343 }
344 }
345
346 private void unsubscribeLocked(ConditionRecord r) {
347 if (DEBUG) Slog.d(TAG, "unsubscribeLocked " + r);
348 final IConditionProvider provider = provider(r);
349 if (provider == null) {
350 Slog.w(TAG, "unsubscribeLocked: no provider");
351 return;
352 }
353 try {
354 provider.onUnsubscribe(r.id);
355 } catch (RemoteException e) {
356 Slog.w(TAG, "Error unsubscribing to " + r, e);
357 }
358 }
359
360 private static IConditionProvider provider(ConditionRecord r) {
361 return r == null ? null : provider(r.info);
John Spurlocke77bb362014-04-26 10:24:59 -0400362 }
363
364 private static IConditionProvider provider(ManagedServiceInfo info) {
365 return info == null ? null : (IConditionProvider) info.service;
366 }
367
368 private void requestConditionsLocked(int flags) {
369 for (ManagedServiceInfo info : mServices) {
370 final IConditionProvider provider = provider(info);
371 if (provider == null) continue;
372 try {
373 provider.onRequestConditions(flags);
374 } catch (RemoteException e) {
375 Slog.w(TAG, "Error requesting conditions from " + info.component, e);
376 }
377 }
John Spurlock7340fc82014-04-24 18:50:12 -0400378 }
John Spurlock1c923a32014-04-27 16:42:29 -0400379
John Spurlock3b98b3f2014-05-01 09:08:48 -0400380 private void loadZenConfig() {
381 final ZenModeConfig config = mZenModeHelper.getConfig();
382 if (config == null) {
383 if (DEBUG) Slog.d(TAG, "loadZenConfig: no config");
384 return;
385 }
386 synchronized (mMutex) {
387 if (config.conditionComponents == null || config.conditionIds == null
388 || config.conditionComponents.length != config.conditionIds.length) {
389 if (DEBUG) Slog.d(TAG, "loadZenConfig: no conditions");
390 setAutomaticZenModeConditions(null, false /*save*/);
391 return;
392 }
393 final ArraySet<Uri> newIds = new ArraySet<Uri>();
394 final int N = config.conditionComponents.length;
395 for (int i = 0; i < N; i++) {
396 final ComponentName component = config.conditionComponents[i];
397 final Uri id = config.conditionIds[i];
398 if (component != null && id != null) {
399 getRecordLocked(id, component); // ensure record exists
400 newIds.add(id);
401 }
402 }
403 if (DEBUG) Slog.d(TAG, "loadZenConfig: N=" + N);
404 setAutomaticZenModeConditions(newIds.toArray(new Uri[newIds.size()]), false /*save*/);
405 }
406 }
407
408 private void saveZenConfigLocked() {
409 ZenModeConfig config = mZenModeHelper.getConfig();
410 if (config == null) return;
411 config = config.copy();
412 final ArrayList<ConditionRecord> automatic = new ArrayList<ConditionRecord>();
413 final int automaticN = mRecords.size();
414 for (int i = 0; i < automaticN; i++) {
415 final ConditionRecord r = mRecords.get(i);
416 if (r.isAutomatic) {
417 automatic.add(r);
418 }
419 }
420 if (automatic.isEmpty()) {
421 config.conditionComponents = null;
422 config.conditionIds = null;
423 } else {
424 final int N = automatic.size();
425 config.conditionComponents = new ComponentName[N];
426 config.conditionIds = new Uri[N];
427 for (int i = 0; i < N; i++) {
428 final ConditionRecord r = automatic.get(i);
429 config.conditionComponents[i] = r.component;
430 config.conditionIds[i] = r.id;
431 }
432 }
433 if (DEBUG) Slog.d(TAG, "Setting zen config to: " + config);
434 mZenModeHelper.setConfig(config);
435 }
436
John Spurlock1c923a32014-04-27 16:42:29 -0400437 private class ZenModeHelperCallback extends ZenModeHelper.Callback {
438 @Override
John Spurlock3b98b3f2014-05-01 09:08:48 -0400439 void onConfigChanged() {
440 loadZenConfig();
441 }
442
443 @Override
John Spurlock1c923a32014-04-27 16:42:29 -0400444 void onZenModeChanged() {
445 final int mode = mZenModeHelper.getZenMode();
446 if (mode == Global.ZEN_MODE_OFF) {
John Spurlock3b98b3f2014-05-01 09:08:48 -0400447 // ensure any manual condition is cleared
448 setZenModeCondition(null);
John Spurlock1c923a32014-04-27 16:42:29 -0400449 }
450 }
451 }
John Spurlock3b98b3f2014-05-01 09:08:48 -0400452
453 private static class ConditionRecord {
454 public final Uri id;
455 public final ComponentName component;
456 public Condition condition;
457 public ManagedServiceInfo info;
458 public boolean isAutomatic;
459 public boolean isManual;
460
461 private ConditionRecord(Uri id, ComponentName component) {
462 this.id = id;
463 this.component = component;
464 }
465
466 @Override
467 public String toString() {
468 final StringBuilder sb = new StringBuilder("ConditionRecord[id=")
469 .append(id).append(",component=").append(component);
470 if (isAutomatic) sb.append(",automatic");
471 if (isManual) sb.append(",manual");
472 return sb.append(']').toString();
473 }
474 }
John Spurlock7340fc82014-04-24 18:50:12 -0400475}