ConditionProviders.java revision 3b98b3f1f85aff0c84ebef4dd497c146d1b4d248
1/** 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 19import android.content.ComponentName; 20import android.content.Context; 21import android.net.Uri; 22import android.os.Handler; 23import android.os.IBinder; 24import android.os.IInterface; 25import android.os.RemoteException; 26import android.provider.Settings; 27import android.provider.Settings.Global; 28import android.service.notification.Condition; 29import android.service.notification.ConditionProviderService; 30import android.service.notification.IConditionListener; 31import android.service.notification.IConditionProvider; 32import android.service.notification.ZenModeConfig; 33import android.util.ArrayMap; 34import android.util.ArraySet; 35import android.util.Slog; 36 37import com.android.internal.R; 38 39import java.io.PrintWriter; 40import java.util.ArrayList; 41import java.util.Arrays; 42 43public class ConditionProviders extends ManagedServices { 44 private static final Condition[] NO_CONDITIONS = new Condition[0]; 45 46 private final ZenModeHelper mZenModeHelper; 47 private final ArrayMap<IBinder, IConditionListener> mListeners 48 = new ArrayMap<IBinder, IConditionListener>(); 49 private final ArrayList<ConditionRecord> mRecords = new ArrayList<ConditionRecord>(); 50 51 public ConditionProviders(Context context, Handler handler, 52 UserProfiles userProfiles, ZenModeHelper zenModeHelper) { 53 super(context, handler, new Object(), userProfiles); 54 mZenModeHelper = zenModeHelper; 55 mZenModeHelper.addCallback(new ZenModeHelperCallback()); 56 loadZenConfig(); 57 } 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 72 public void dump(PrintWriter pw) { 73 super.dump(pw); 74 synchronized(mMutex) { 75 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 } 79 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)); 82 } 83 } 84 } 85 86 @Override 87 protected IInterface asInterface(IBinder binder) { 88 return IConditionProvider.Stub.asInterface(binder); 89 } 90 91 @Override 92 protected void onServiceAdded(ManagedServiceInfo info) { 93 Slog.d(TAG, "onServiceAdded " + info); 94 final IConditionProvider provider = provider(info); 95 try { 96 provider.onConnected(); 97 } catch (RemoteException e) { 98 // we tried 99 } 100 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 } 118 } 119 120 @Override 121 protected void onServiceRemovedLocked(ManagedServiceInfo removed) { 122 if (removed == null) return; 123 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 128 mZenModeHelper.setZenMode(Global.ZEN_MODE_OFF); 129 } 130 if (r.isAutomatic) { 131 // removing an automatic condition, exit zen 132 mZenModeHelper.setZenMode(Global.ZEN_MODE_OFF); 133 } 134 mRecords.remove(i); 135 } 136 } 137 138 public ManagedServiceInfo checkServiceToken(IConditionProvider provider) { 139 synchronized(mMutex) { 140 return checkServiceTokenLocked(provider); 141 } 142 } 143 144 public void requestZenModeConditions(IConditionListener callback, int relevance) { 145 synchronized(mMutex) { 146 if (DEBUG) Slog.d(TAG, "requestZenModeConditions callback=" + callback 147 + " relevance=" + Condition.relevanceToString(relevance)); 148 if (callback == null) return; 149 relevance = relevance & (Condition.FLAG_RELEVANT_NOW | Condition.FLAG_RELEVANT_ALWAYS); 150 if (relevance != 0) { 151 mListeners.put(callback.asBinder(), callback); 152 requestConditionsLocked(relevance); 153 } else { 154 mListeners.remove(callback.asBinder()); 155 if (mListeners.isEmpty()) { 156 requestConditionsLocked(0); 157 } 158 } 159 } 160 } 161 162 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 200 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))); 204 conditions = validateConditions(pkg, conditions); 205 if (conditions == null || conditions.length == 0) return; 206 final int N = conditions.length; 207 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 } 214 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); 246 } 247 } 248 } 249 } 250 } 251 252 public void setZenModeCondition(Uri conditionId) { 253 if (DEBUG) Slog.d(TAG, "setZenModeCondition " + conditionId); 254 synchronized(mMutex) { 255 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; 267 } 268 } 269 } 270 } 271 272 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; 278 } 279 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); 362 } 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 } 378 } 379 380 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 437 private class ZenModeHelperCallback extends ZenModeHelper.Callback { 438 @Override 439 void onConfigChanged() { 440 loadZenConfig(); 441 } 442 443 @Override 444 void onZenModeChanged() { 445 final int mode = mZenModeHelper.getZenMode(); 446 if (mode == Global.ZEN_MODE_OFF) { 447 // ensure any manual condition is cleared 448 setZenModeCondition(null); 449 } 450 } 451 } 452 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 } 475} 476