blob: f8ac2e4807629ac5b587c49a41e7b5002e3bfa77 [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 Spurlocke77bb362014-04-26 10:24:59 -040056
John Spurlock7340fc82014-04-24 18:50:12 -040057 public ConditionProviders(Context context, Handler handler,
John Spurlocke77bb362014-04-26 10:24:59 -040058 UserProfiles userProfiles, ZenModeHelper zenModeHelper) {
59 super(context, handler, new Object(), userProfiles);
60 mZenModeHelper = zenModeHelper;
John Spurlock1c923a32014-04-27 16:42:29 -040061 mZenModeHelper.addCallback(new ZenModeHelperCallback());
John Spurlock3b98b3f2014-05-01 09:08:48 -040062 loadZenConfig();
John Spurlock7340fc82014-04-24 18:50:12 -040063 }
64
65 @Override
66 protected Config getConfig() {
67 Config c = new Config();
68 c.caption = "condition provider";
69 c.serviceInterface = ConditionProviderService.SERVICE_INTERFACE;
70 c.secureSettingName = Settings.Secure.ENABLED_CONDITION_PROVIDERS;
71 c.bindPermission = android.Manifest.permission.BIND_CONDITION_PROVIDER_SERVICE;
72 c.settingsAction = Settings.ACTION_CONDITION_PROVIDER_SETTINGS;
73 c.clientLabel = R.string.condition_provider_service_binding_label;
74 return c;
75 }
76
77 @Override
John Spurlock25e2d242014-06-27 13:58:23 -040078 public void dump(PrintWriter pw, DumpFilter filter) {
79 super.dump(pw, filter);
John Spurlocke77bb362014-04-26 10:24:59 -040080 synchronized(mMutex) {
John Spurlock25e2d242014-06-27 13:58:23 -040081 if (filter == null) {
82 pw.print(" mListeners("); pw.print(mListeners.size()); pw.println("):");
83 for (int i = 0; i < mListeners.size(); i++) {
84 pw.print(" "); pw.println(mListeners.keyAt(i));
85 }
John Spurlocke77bb362014-04-26 10:24:59 -040086 }
John Spurlock3b98b3f2014-05-01 09:08:48 -040087 pw.print(" mRecords("); pw.print(mRecords.size()); pw.println("):");
88 for (int i = 0; i < mRecords.size(); i++) {
John Spurlock856edeb2014-06-01 20:36:47 -040089 final ConditionRecord r = mRecords.get(i);
John Spurlock25e2d242014-06-27 13:58:23 -040090 if (filter != null && !filter.matches(r.component)) continue;
John Spurlock856edeb2014-06-01 20:36:47 -040091 pw.print(" "); pw.println(r);
92 final String countdownDesc = CountdownConditionProvider.tryParseDescription(r.id);
93 if (countdownDesc != null) {
94 pw.print(" ("); pw.print(countdownDesc); pw.println(")");
95 }
John Spurlocke77bb362014-04-26 10:24:59 -040096 }
97 }
98 }
99
100 @Override
John Spurlock7340fc82014-04-24 18:50:12 -0400101 protected IInterface asInterface(IBinder binder) {
102 return IConditionProvider.Stub.asInterface(binder);
103 }
104
105 @Override
John Spurlock856edeb2014-06-01 20:36:47 -0400106 public void onBootPhaseAppsCanStart() {
107 super.onBootPhaseAppsCanStart();
108 mCountdown.attachBase(mContext);
109 registerService(mCountdown.asInterface(), CountdownConditionProvider.COMPONENT,
110 UserHandle.USER_OWNER);
111 }
112
113 @Override
John Spurlock3b98b3f2014-05-01 09:08:48 -0400114 protected void onServiceAdded(ManagedServiceInfo info) {
115 Slog.d(TAG, "onServiceAdded " + info);
116 final IConditionProvider provider = provider(info);
John Spurlocke77bb362014-04-26 10:24:59 -0400117 try {
118 provider.onConnected();
119 } catch (RemoteException e) {
120 // we tried
121 }
John Spurlock3b98b3f2014-05-01 09:08:48 -0400122 synchronized (mMutex) {
123 final int N = mRecords.size();
124 for(int i = 0; i < N; i++) {
125 final ConditionRecord r = mRecords.get(i);
126 if (!r.component.equals(info.component)) continue;
127 r.info = info;
128 // if automatic, auto-subscribe
129 if (r.isAutomatic) {
130 try {
131 final Uri id = r.id;
132 if (DEBUG) Slog.d(TAG, "Auto-subscribing to configured condition " + id);
133 provider.onSubscribe(id);
134 } catch (RemoteException e) {
135 // we tried
136 }
137 }
138 }
139 }
John Spurlocke77bb362014-04-26 10:24:59 -0400140 }
141
142 @Override
143 protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
144 if (removed == null) return;
John Spurlock3b98b3f2014-05-01 09:08:48 -0400145 for (int i = mRecords.size() - 1; i >= 0; i--) {
146 final ConditionRecord r = mRecords.get(i);
147 if (!r.component.equals(removed.component)) continue;
148 if (r.isManual) {
149 // removing the current manual condition, exit zen
John Spurlocke86de4c2014-04-29 21:48:26 -0400150 mZenModeHelper.setZenMode(Global.ZEN_MODE_OFF);
151 }
John Spurlock3b98b3f2014-05-01 09:08:48 -0400152 if (r.isAutomatic) {
153 // removing an automatic condition, exit zen
154 mZenModeHelper.setZenMode(Global.ZEN_MODE_OFF);
John Spurlocke77bb362014-04-26 10:24:59 -0400155 }
John Spurlock3b98b3f2014-05-01 09:08:48 -0400156 mRecords.remove(i);
John Spurlocke77bb362014-04-26 10:24:59 -0400157 }
158 }
159
160 public ManagedServiceInfo checkServiceToken(IConditionProvider provider) {
161 synchronized(mMutex) {
162 return checkServiceTokenLocked(provider);
163 }
164 }
165
John Spurlock3b98b3f2014-05-01 09:08:48 -0400166 public void requestZenModeConditions(IConditionListener callback, int relevance) {
John Spurlocke77bb362014-04-26 10:24:59 -0400167 synchronized(mMutex) {
168 if (DEBUG) Slog.d(TAG, "requestZenModeConditions callback=" + callback
John Spurlock3b98b3f2014-05-01 09:08:48 -0400169 + " relevance=" + Condition.relevanceToString(relevance));
John Spurlocke77bb362014-04-26 10:24:59 -0400170 if (callback == null) return;
John Spurlock3b98b3f2014-05-01 09:08:48 -0400171 relevance = relevance & (Condition.FLAG_RELEVANT_NOW | Condition.FLAG_RELEVANT_ALWAYS);
172 if (relevance != 0) {
John Spurlocke77bb362014-04-26 10:24:59 -0400173 mListeners.put(callback.asBinder(), callback);
John Spurlock3b98b3f2014-05-01 09:08:48 -0400174 requestConditionsLocked(relevance);
John Spurlocke77bb362014-04-26 10:24:59 -0400175 } else {
176 mListeners.remove(callback.asBinder());
177 if (mListeners.isEmpty()) {
178 requestConditionsLocked(0);
179 }
180 }
181 }
182 }
183
John Spurlock3b98b3f2014-05-01 09:08:48 -0400184 private Condition[] validateConditions(String pkg, Condition[] conditions) {
185 if (conditions == null || conditions.length == 0) return null;
186 final int N = conditions.length;
187 final ArrayMap<Uri, Condition> valid = new ArrayMap<Uri, Condition>(N);
188 for (int i = 0; i < N; i++) {
189 final Uri id = conditions[i].id;
190 if (!Condition.isValidId(id, pkg)) {
191 Slog.w(TAG, "Ignoring condition from " + pkg + " for invalid id: " + id);
192 continue;
193 }
194 if (valid.containsKey(id)) {
195 Slog.w(TAG, "Ignoring condition from " + pkg + " for duplicate id: " + id);
196 continue;
197 }
198 valid.put(id, conditions[i]);
199 }
200 if (valid.size() == 0) return null;
201 if (valid.size() == N) return conditions;
202 final Condition[] rt = new Condition[valid.size()];
203 for (int i = 0; i < rt.length; i++) {
204 rt[i] = valid.valueAt(i);
205 }
206 return rt;
207 }
208
209 private ConditionRecord getRecordLocked(Uri id, ComponentName component) {
210 final int N = mRecords.size();
211 for (int i = 0; i < N; i++) {
212 final ConditionRecord r = mRecords.get(i);
213 if (r.id.equals(id) && r.component.equals(component)) {
214 return r;
215 }
216 }
217 final ConditionRecord r = new ConditionRecord(id, component);
218 mRecords.add(r);
219 return r;
220 }
221
John Spurlocke77bb362014-04-26 10:24:59 -0400222 public void notifyConditions(String pkg, ManagedServiceInfo info, Condition[] conditions) {
223 synchronized(mMutex) {
224 if (DEBUG) Slog.d(TAG, "notifyConditions pkg=" + pkg + " info=" + info + " conditions="
225 + (conditions == null ? null : Arrays.asList(conditions)));
John Spurlock3b98b3f2014-05-01 09:08:48 -0400226 conditions = validateConditions(pkg, conditions);
John Spurlocke77bb362014-04-26 10:24:59 -0400227 if (conditions == null || conditions.length == 0) return;
228 final int N = conditions.length;
John Spurlocke77bb362014-04-26 10:24:59 -0400229 for (IConditionListener listener : mListeners.values()) {
230 try {
231 listener.onConditionsReceived(conditions);
232 } catch (RemoteException e) {
233 Slog.w(TAG, "Error sending conditions to listener " + listener, e);
234 }
235 }
John Spurlock3b98b3f2014-05-01 09:08:48 -0400236 for (int i = 0; i < N; i++) {
237 final Condition c = conditions[i];
238 final ConditionRecord r = getRecordLocked(c.id, info.component);
239 r.info = info;
240 r.condition = c;
241 // if manual, exit zen if false (or failed)
242 if (r.isManual) {
243 if (c.state == Condition.STATE_FALSE || c.state == Condition.STATE_ERROR) {
244 final boolean failed = c.state == Condition.STATE_ERROR;
245 if (failed) {
246 Slog.w(TAG, "Exit zen: manual condition failed: " + c);
247 } else if (DEBUG) {
248 Slog.d(TAG, "Exit zen: manual condition false: " + c);
249 }
250 mZenModeHelper.setZenMode(Settings.Global.ZEN_MODE_OFF);
251 unsubscribeLocked(r);
252 r.isManual = false;
253 }
254 }
255 // if automatic, exit zen if false (or failed), enter zen if true
256 if (r.isAutomatic) {
257 if (c.state == Condition.STATE_FALSE || c.state == Condition.STATE_ERROR) {
258 final boolean failed = c.state == Condition.STATE_ERROR;
259 if (failed) {
260 Slog.w(TAG, "Exit zen: automatic condition failed: " + c);
261 } else if (DEBUG) {
262 Slog.d(TAG, "Exit zen: automatic condition false: " + c);
263 }
264 mZenModeHelper.setZenMode(Settings.Global.ZEN_MODE_OFF);
265 } else if (c.state == Condition.STATE_TRUE) {
266 Slog.d(TAG, "Enter zen: automatic condition true: " + c);
267 mZenModeHelper.setZenMode(Settings.Global.ZEN_MODE_ON);
John Spurlocke77bb362014-04-26 10:24:59 -0400268 }
269 }
270 }
271 }
272 }
273
John Spurlocke77bb362014-04-26 10:24:59 -0400274 public void setZenModeCondition(Uri conditionId) {
John Spurlock3b98b3f2014-05-01 09:08:48 -0400275 if (DEBUG) Slog.d(TAG, "setZenModeCondition " + conditionId);
John Spurlocke77bb362014-04-26 10:24:59 -0400276 synchronized(mMutex) {
John Spurlock856edeb2014-06-01 20:36:47 -0400277 if (ZenModeConfig.isValidCountdownConditionId(conditionId)) {
278 // constructed by the client, make sure the record exists...
279 final ConditionRecord r = getRecordLocked(conditionId,
280 CountdownConditionProvider.COMPONENT);
281 if (r.info == null) {
282 // ... and is associated with the in-process service
283 r.info = checkServiceTokenLocked(mCountdown.asInterface());
284 }
285 }
John Spurlock3b98b3f2014-05-01 09:08:48 -0400286 final int N = mRecords.size();
287 for (int i = 0; i < N; i++) {
288 final ConditionRecord r = mRecords.get(i);
289 final boolean idEqual = r.id.equals(conditionId);
290 if (r.isManual && !idEqual) {
291 // was previous manual condition, unsubscribe
292 unsubscribeLocked(r);
293 r.isManual = false;
294 } else if (idEqual && !r.isManual) {
295 // is new manual condition, subscribe
296 subscribeLocked(r);
297 r.isManual = true;
John Spurlocke77bb362014-04-26 10:24:59 -0400298 }
299 }
John Spurlock856edeb2014-06-01 20:36:47 -0400300 if (!Objects.equals(mExitConditionId, conditionId)) {
301 mExitConditionId = conditionId;
302 saveZenConfigLocked();
303 }
John Spurlocke77bb362014-04-26 10:24:59 -0400304 }
305 }
306
John Spurlock3b98b3f2014-05-01 09:08:48 -0400307 private void subscribeLocked(ConditionRecord r) {
308 if (DEBUG) Slog.d(TAG, "subscribeLocked " + r);
309 final IConditionProvider provider = provider(r);
310 if (provider == null) {
311 Slog.w(TAG, "subscribeLocked: no provider");
312 return;
John Spurlocke77bb362014-04-26 10:24:59 -0400313 }
John Spurlock3b98b3f2014-05-01 09:08:48 -0400314 try {
315 provider.onSubscribe(r.id);
316 } catch (RemoteException e) {
317 Slog.w(TAG, "Error subscribing to " + r, e);
318 }
319 }
320
321 private static <T> ArraySet<T> safeSet(T... items) {
322 final ArraySet<T> rt = new ArraySet<T>();
323 if (items == null || items.length == 0) return rt;
324 final int N = items.length;
325 for (int i = 0; i < N; i++) {
326 final T item = items[i];
327 if (item != null) {
328 rt.add(item);
329 }
330 }
331 return rt;
332 }
333
334 public void setAutomaticZenModeConditions(Uri[] conditionIds) {
335 setAutomaticZenModeConditions(conditionIds, true /*save*/);
336 }
337
338 private void setAutomaticZenModeConditions(Uri[] conditionIds, boolean save) {
339 if (DEBUG) Slog.d(TAG, "setAutomaticZenModeConditions "
340 + (conditionIds == null ? null : Arrays.asList(conditionIds)));
341 synchronized(mMutex) {
342 final ArraySet<Uri> newIds = safeSet(conditionIds);
343 final int N = mRecords.size();
344 boolean changed = false;
345 for (int i = 0; i < N; i++) {
346 final ConditionRecord r = mRecords.get(i);
347 final boolean automatic = newIds.contains(r.id);
348 if (!r.isAutomatic && automatic) {
349 // subscribe to new automatic
350 subscribeLocked(r);
351 r.isAutomatic = true;
352 changed = true;
353 } else if (r.isAutomatic && !automatic) {
354 // unsubscribe from old automatic
355 unsubscribeLocked(r);
356 r.isAutomatic = false;
357 changed = true;
358 }
359 }
360 if (save && changed) {
361 saveZenConfigLocked();
362 }
363 }
364 }
365
366 public Condition[] getAutomaticZenModeConditions() {
367 synchronized(mMutex) {
368 final int N = mRecords.size();
369 ArrayList<Condition> rt = null;
370 for (int i = 0; i < N; i++) {
371 final ConditionRecord r = mRecords.get(i);
372 if (r.isAutomatic && r.condition != null) {
373 if (rt == null) rt = new ArrayList<Condition>();
374 rt.add(r.condition);
375 }
376 }
377 return rt == null ? NO_CONDITIONS : rt.toArray(new Condition[rt.size()]);
378 }
379 }
380
381 private void unsubscribeLocked(ConditionRecord r) {
382 if (DEBUG) Slog.d(TAG, "unsubscribeLocked " + r);
383 final IConditionProvider provider = provider(r);
384 if (provider == null) {
385 Slog.w(TAG, "unsubscribeLocked: no provider");
386 return;
387 }
388 try {
389 provider.onUnsubscribe(r.id);
390 } catch (RemoteException e) {
391 Slog.w(TAG, "Error unsubscribing to " + r, e);
392 }
393 }
394
395 private static IConditionProvider provider(ConditionRecord r) {
396 return r == null ? null : provider(r.info);
John Spurlocke77bb362014-04-26 10:24:59 -0400397 }
398
399 private static IConditionProvider provider(ManagedServiceInfo info) {
400 return info == null ? null : (IConditionProvider) info.service;
401 }
402
403 private void requestConditionsLocked(int flags) {
404 for (ManagedServiceInfo info : mServices) {
405 final IConditionProvider provider = provider(info);
406 if (provider == null) continue;
407 try {
408 provider.onRequestConditions(flags);
409 } catch (RemoteException e) {
410 Slog.w(TAG, "Error requesting conditions from " + info.component, e);
411 }
412 }
John Spurlock7340fc82014-04-24 18:50:12 -0400413 }
John Spurlock1c923a32014-04-27 16:42:29 -0400414
John Spurlock3b98b3f2014-05-01 09:08:48 -0400415 private void loadZenConfig() {
416 final ZenModeConfig config = mZenModeHelper.getConfig();
417 if (config == null) {
418 if (DEBUG) Slog.d(TAG, "loadZenConfig: no config");
419 return;
420 }
421 synchronized (mMutex) {
John Spurlock856edeb2014-06-01 20:36:47 -0400422 mExitConditionId = config.exitConditionId;
John Spurlock3b98b3f2014-05-01 09:08:48 -0400423 if (config.conditionComponents == null || config.conditionIds == null
424 || config.conditionComponents.length != config.conditionIds.length) {
425 if (DEBUG) Slog.d(TAG, "loadZenConfig: no conditions");
426 setAutomaticZenModeConditions(null, false /*save*/);
427 return;
428 }
429 final ArraySet<Uri> newIds = new ArraySet<Uri>();
430 final int N = config.conditionComponents.length;
431 for (int i = 0; i < N; i++) {
432 final ComponentName component = config.conditionComponents[i];
433 final Uri id = config.conditionIds[i];
434 if (component != null && id != null) {
435 getRecordLocked(id, component); // ensure record exists
436 newIds.add(id);
437 }
438 }
439 if (DEBUG) Slog.d(TAG, "loadZenConfig: N=" + N);
440 setAutomaticZenModeConditions(newIds.toArray(new Uri[newIds.size()]), false /*save*/);
441 }
442 }
443
444 private void saveZenConfigLocked() {
445 ZenModeConfig config = mZenModeHelper.getConfig();
446 if (config == null) return;
447 config = config.copy();
448 final ArrayList<ConditionRecord> automatic = new ArrayList<ConditionRecord>();
449 final int automaticN = mRecords.size();
450 for (int i = 0; i < automaticN; i++) {
451 final ConditionRecord r = mRecords.get(i);
452 if (r.isAutomatic) {
453 automatic.add(r);
454 }
455 }
456 if (automatic.isEmpty()) {
457 config.conditionComponents = null;
458 config.conditionIds = null;
459 } else {
460 final int N = automatic.size();
461 config.conditionComponents = new ComponentName[N];
462 config.conditionIds = new Uri[N];
463 for (int i = 0; i < N; i++) {
464 final ConditionRecord r = automatic.get(i);
465 config.conditionComponents[i] = r.component;
466 config.conditionIds[i] = r.id;
467 }
468 }
John Spurlock856edeb2014-06-01 20:36:47 -0400469 config.exitConditionId = mExitConditionId;
John Spurlock3b98b3f2014-05-01 09:08:48 -0400470 if (DEBUG) Slog.d(TAG, "Setting zen config to: " + config);
471 mZenModeHelper.setConfig(config);
472 }
473
John Spurlock1c923a32014-04-27 16:42:29 -0400474 private class ZenModeHelperCallback extends ZenModeHelper.Callback {
475 @Override
John Spurlock3b98b3f2014-05-01 09:08:48 -0400476 void onConfigChanged() {
477 loadZenConfig();
478 }
479
480 @Override
John Spurlock1c923a32014-04-27 16:42:29 -0400481 void onZenModeChanged() {
482 final int mode = mZenModeHelper.getZenMode();
483 if (mode == Global.ZEN_MODE_OFF) {
John Spurlock3b98b3f2014-05-01 09:08:48 -0400484 // ensure any manual condition is cleared
485 setZenModeCondition(null);
John Spurlock1c923a32014-04-27 16:42:29 -0400486 }
487 }
488 }
John Spurlock3b98b3f2014-05-01 09:08:48 -0400489
490 private static class ConditionRecord {
491 public final Uri id;
492 public final ComponentName component;
493 public Condition condition;
494 public ManagedServiceInfo info;
495 public boolean isAutomatic;
496 public boolean isManual;
497
498 private ConditionRecord(Uri id, ComponentName component) {
499 this.id = id;
500 this.component = component;
501 }
502
503 @Override
504 public String toString() {
505 final StringBuilder sb = new StringBuilder("ConditionRecord[id=")
506 .append(id).append(",component=").append(component);
507 if (isAutomatic) sb.append(",automatic");
508 if (isManual) sb.append(",manual");
509 return sb.append(']').toString();
510 }
511 }
John Spurlock7340fc82014-04-24 18:50:12 -0400512}