blob: 8c6afafd8ec87dc7fca6ea66e397589a2528d349 [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 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;
32import android.util.ArrayMap;
John Spurlock3b98b3f2014-05-01 09:08:48 -040033import android.util.ArraySet;
John Spurlock7340fc82014-04-24 18:50:12 -040034import android.util.Slog;
35
36import com.android.internal.R;
John Spurlock25e2d242014-06-27 13:58:23 -040037import com.android.server.notification.NotificationManagerService.DumpFilter;
John Spurlock7340fc82014-04-24 18:50:12 -040038
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 Spurlockb2278d62015-04-07 12:47:12 -040044 private final ArrayList<ConditionRecord> mRecords = new ArrayList<>();
45 private final ArrayMap<IBinder, IConditionListener> mListeners = new ArrayMap<>();
46 private final ArraySet<String> mSystemConditionProviderNames;
47 private final ArraySet<SystemConditionProviderService> mSystemConditionProviders
48 = new ArraySet<>();
John Spurlock7340fc82014-04-24 18:50:12 -040049
John Spurlockb2278d62015-04-07 12:47:12 -040050 private Callback mCallback;
John Spurlock856edeb2014-06-01 20:36:47 -040051
John Spurlockb2278d62015-04-07 12:47:12 -040052 public ConditionProviders(Context context, Handler handler, UserProfiles userProfiles) {
John Spurlocke77bb362014-04-26 10:24:59 -040053 super(context, handler, new Object(), userProfiles);
John Spurlockb2278d62015-04-07 12:47:12 -040054 mSystemConditionProviderNames = safeSet(PropConfig.getStringArray(mContext,
John Spurlock530052a2014-11-30 16:26:19 -050055 "system.condition.providers",
56 R.array.config_system_condition_providers));
John Spurlock530052a2014-11-30 16:26:19 -050057 }
58
John Spurlockb2278d62015-04-07 12:47:12 -040059 public void setCallback(Callback callback) {
60 mCallback = callback;
61 }
62
63 public boolean isSystemProviderEnabled(String path) {
64 return mSystemConditionProviderNames.contains(path);
65 }
66
67 public void addSystemProvider(SystemConditionProviderService service) {
68 mSystemConditionProviders.add(service);
69 service.attachBase(mContext);
70 registerService(service.asInterface(), service.getComponent(), UserHandle.USER_OWNER);
71 }
72
73 public Iterable<SystemConditionProviderService> getSystemProviders() {
74 return mSystemConditionProviders;
John Spurlock7340fc82014-04-24 18:50:12 -040075 }
76
77 @Override
78 protected Config getConfig() {
John Spurlockb2278d62015-04-07 12:47:12 -040079 final Config c = new Config();
John Spurlock7340fc82014-04-24 18:50:12 -040080 c.caption = "condition provider";
81 c.serviceInterface = ConditionProviderService.SERVICE_INTERFACE;
82 c.secureSettingName = Settings.Secure.ENABLED_CONDITION_PROVIDERS;
83 c.bindPermission = android.Manifest.permission.BIND_CONDITION_PROVIDER_SERVICE;
84 c.settingsAction = Settings.ACTION_CONDITION_PROVIDER_SETTINGS;
85 c.clientLabel = R.string.condition_provider_service_binding_label;
86 return c;
87 }
88
89 @Override
John Spurlock25e2d242014-06-27 13:58:23 -040090 public void dump(PrintWriter pw, DumpFilter filter) {
91 super.dump(pw, filter);
John Spurlocke77bb362014-04-26 10:24:59 -040092 synchronized(mMutex) {
John Spurlock3b98b3f2014-05-01 09:08:48 -040093 pw.print(" mRecords("); pw.print(mRecords.size()); pw.println("):");
94 for (int i = 0; i < mRecords.size(); i++) {
John Spurlock856edeb2014-06-01 20:36:47 -040095 final ConditionRecord r = mRecords.get(i);
John Spurlock25e2d242014-06-27 13:58:23 -040096 if (filter != null && !filter.matches(r.component)) continue;
John Spurlock856edeb2014-06-01 20:36:47 -040097 pw.print(" "); pw.println(r);
98 final String countdownDesc = CountdownConditionProvider.tryParseDescription(r.id);
99 if (countdownDesc != null) {
100 pw.print(" ("); pw.print(countdownDesc); pw.println(")");
101 }
John Spurlocke77bb362014-04-26 10:24:59 -0400102 }
103 }
John Spurlockb2278d62015-04-07 12:47:12 -0400104 if (filter == null) {
105 pw.print(" mListeners("); pw.print(mListeners.size()); pw.println("):");
106 for (int i = 0; i < mListeners.size(); i++) {
107 pw.print(" "); pw.println(mListeners.keyAt(i));
108 }
John Spurlock530052a2014-11-30 16:26:19 -0500109 }
John Spurlockb2278d62015-04-07 12:47:12 -0400110 pw.print(" mSystemConditionProviders: "); pw.println(mSystemConditionProviderNames);
111 for (int i = 0; i < mSystemConditionProviders.size(); i++) {
112 mSystemConditionProviders.valueAt(i).dump(pw, filter);
John Spurlock530052a2014-11-30 16:26:19 -0500113 }
John Spurlocke77bb362014-04-26 10:24:59 -0400114 }
115
116 @Override
John Spurlock7340fc82014-04-24 18:50:12 -0400117 protected IInterface asInterface(IBinder binder) {
118 return IConditionProvider.Stub.asInterface(binder);
119 }
120
121 @Override
John Spurlock856edeb2014-06-01 20:36:47 -0400122 public void onBootPhaseAppsCanStart() {
123 super.onBootPhaseAppsCanStart();
John Spurlock2f096ed2015-05-04 11:58:26 -0400124 for (int i = 0; i < mSystemConditionProviders.size(); i++) {
125 mSystemConditionProviders.valueAt(i).onBootComplete();
126 }
John Spurlockb2278d62015-04-07 12:47:12 -0400127 if (mCallback != null) {
128 mCallback.onBootComplete();
John Spurlock530052a2014-11-30 16:26:19 -0500129 }
John Spurlock37bc92c2014-11-03 11:01:51 -0500130 }
131
132 @Override
133 public void onUserSwitched() {
134 super.onUserSwitched();
John Spurlockb2278d62015-04-07 12:47:12 -0400135 if (mCallback != null) {
136 mCallback.onUserSwitched();
John Spurlock530052a2014-11-30 16:26:19 -0500137 }
John Spurlock856edeb2014-06-01 20:36:47 -0400138 }
139
140 @Override
John Spurlock3b98b3f2014-05-01 09:08:48 -0400141 protected void onServiceAdded(ManagedServiceInfo info) {
John Spurlock3b98b3f2014-05-01 09:08:48 -0400142 final IConditionProvider provider = provider(info);
John Spurlocke77bb362014-04-26 10:24:59 -0400143 try {
144 provider.onConnected();
145 } catch (RemoteException e) {
146 // we tried
147 }
John Spurlock39581cc2015-04-10 11:59:01 -0400148 if (mCallback != null) {
149 mCallback.onServiceAdded(info.component);
150 }
John Spurlocke77bb362014-04-26 10:24:59 -0400151 }
152
153 @Override
154 protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
155 if (removed == null) return;
John Spurlock3b98b3f2014-05-01 09:08:48 -0400156 for (int i = mRecords.size() - 1; i >= 0; i--) {
157 final ConditionRecord r = mRecords.get(i);
158 if (!r.component.equals(removed.component)) continue;
John Spurlock3b98b3f2014-05-01 09:08:48 -0400159 mRecords.remove(i);
John Spurlocke77bb362014-04-26 10:24:59 -0400160 }
161 }
162
163 public ManagedServiceInfo checkServiceToken(IConditionProvider provider) {
164 synchronized(mMutex) {
165 return checkServiceTokenLocked(provider);
166 }
167 }
168
John Spurlockb2278d62015-04-07 12:47:12 -0400169 public void requestConditions(IConditionListener callback, int relevance) {
John Spurlocke77bb362014-04-26 10:24:59 -0400170 synchronized(mMutex) {
John Spurlockb2278d62015-04-07 12:47:12 -0400171 if (DEBUG) Slog.d(TAG, "requestConditions callback=" + callback
John Spurlock3b98b3f2014-05-01 09:08:48 -0400172 + " relevance=" + Condition.relevanceToString(relevance));
John Spurlocke77bb362014-04-26 10:24:59 -0400173 if (callback == null) return;
John Spurlock3b98b3f2014-05-01 09:08:48 -0400174 relevance = relevance & (Condition.FLAG_RELEVANT_NOW | Condition.FLAG_RELEVANT_ALWAYS);
175 if (relevance != 0) {
John Spurlocke77bb362014-04-26 10:24:59 -0400176 mListeners.put(callback.asBinder(), callback);
John Spurlock3b98b3f2014-05-01 09:08:48 -0400177 requestConditionsLocked(relevance);
John Spurlocke77bb362014-04-26 10:24:59 -0400178 } else {
179 mListeners.remove(callback.asBinder());
180 if (mListeners.isEmpty()) {
181 requestConditionsLocked(0);
182 }
183 }
184 }
185 }
186
John Spurlock3b98b3f2014-05-01 09:08:48 -0400187 private Condition[] validateConditions(String pkg, Condition[] conditions) {
188 if (conditions == null || conditions.length == 0) return null;
189 final int N = conditions.length;
190 final ArrayMap<Uri, Condition> valid = new ArrayMap<Uri, Condition>(N);
191 for (int i = 0; i < N; i++) {
192 final Uri id = conditions[i].id;
193 if (!Condition.isValidId(id, pkg)) {
194 Slog.w(TAG, "Ignoring condition from " + pkg + " for invalid id: " + id);
195 continue;
196 }
197 if (valid.containsKey(id)) {
198 Slog.w(TAG, "Ignoring condition from " + pkg + " for duplicate id: " + id);
199 continue;
200 }
201 valid.put(id, conditions[i]);
202 }
203 if (valid.size() == 0) return null;
204 if (valid.size() == N) return conditions;
205 final Condition[] rt = new Condition[valid.size()];
206 for (int i = 0; i < rt.length; i++) {
207 rt[i] = valid.valueAt(i);
208 }
209 return rt;
210 }
211
John Spurlockb2278d62015-04-07 12:47:12 -0400212 private ConditionRecord getRecordLocked(Uri id, ComponentName component, boolean create) {
213 if (id == null || component == null) return null;
John Spurlock3b98b3f2014-05-01 09:08:48 -0400214 final int N = mRecords.size();
215 for (int i = 0; i < N; i++) {
216 final ConditionRecord r = mRecords.get(i);
217 if (r.id.equals(id) && r.component.equals(component)) {
218 return r;
219 }
220 }
John Spurlockb2278d62015-04-07 12:47:12 -0400221 if (create) {
222 final ConditionRecord r = new ConditionRecord(id, component);
223 mRecords.add(r);
224 return r;
225 }
226 return null;
John Spurlock3b98b3f2014-05-01 09:08:48 -0400227 }
228
John Spurlocke77bb362014-04-26 10:24:59 -0400229 public void notifyConditions(String pkg, ManagedServiceInfo info, Condition[] conditions) {
230 synchronized(mMutex) {
231 if (DEBUG) Slog.d(TAG, "notifyConditions pkg=" + pkg + " info=" + info + " conditions="
232 + (conditions == null ? null : Arrays.asList(conditions)));
John Spurlock3b98b3f2014-05-01 09:08:48 -0400233 conditions = validateConditions(pkg, conditions);
John Spurlocke77bb362014-04-26 10:24:59 -0400234 if (conditions == null || conditions.length == 0) return;
235 final int N = conditions.length;
John Spurlocke77bb362014-04-26 10:24:59 -0400236 for (IConditionListener listener : mListeners.values()) {
237 try {
238 listener.onConditionsReceived(conditions);
239 } catch (RemoteException e) {
240 Slog.w(TAG, "Error sending conditions to listener " + listener, e);
241 }
242 }
John Spurlock3b98b3f2014-05-01 09:08:48 -0400243 for (int i = 0; i < N; i++) {
244 final Condition c = conditions[i];
John Spurlockb2278d62015-04-07 12:47:12 -0400245 final ConditionRecord r = getRecordLocked(c.id, info.component, true /*create*/);
John Spurlock3b98b3f2014-05-01 09:08:48 -0400246 r.info = info;
247 r.condition = c;
John Spurlockb2278d62015-04-07 12:47:12 -0400248 if (mCallback != null) {
249 mCallback.onConditionChanged(c.id, c);
John Spurlocke77bb362014-04-26 10:24:59 -0400250 }
251 }
252 }
253 }
254
John Spurlock39581cc2015-04-10 11:59:01 -0400255 public IConditionProvider findConditionProvider(ComponentName component) {
256 if (component == null) return null;
257 for (ManagedServiceInfo service : mServices) {
258 if (component.equals(service.component)) {
259 return provider(service);
260 }
261 }
262 return null;
263 }
264
John Spurlockb2278d62015-04-07 12:47:12 -0400265 public void ensureRecordExists(ComponentName component, Uri conditionId,
266 IConditionProvider provider) {
John Spurlock530052a2014-11-30 16:26:19 -0500267 // constructed by convention, make sure the record exists...
John Spurlockb2278d62015-04-07 12:47:12 -0400268 final ConditionRecord r = getRecordLocked(conditionId, component, true /*create*/);
John Spurlock530052a2014-11-30 16:26:19 -0500269 if (r.info == null) {
270 // ... and is associated with the in-process service
271 r.info = checkServiceTokenLocked(provider);
272 }
273 }
274
John Spurlockb2278d62015-04-07 12:47:12 -0400275 public boolean subscribeIfNecessary(ComponentName component, Uri conditionId) {
276 synchronized (mMutex) {
277 final ConditionRecord r = getRecordLocked(conditionId, component, false /*create*/);
278 if (r == null) {
279 Slog.w(TAG, "Unable to subscribe to " + component + " " + conditionId);
280 return false;
John Spurlock856edeb2014-06-01 20:36:47 -0400281 }
John Spurlockb2278d62015-04-07 12:47:12 -0400282 if (r.subscribed) return true;
283 subscribeLocked(r);
284 return r.subscribed;
285 }
286 }
287
288 public void unsubscribeIfNecessary(ComponentName component, Uri conditionId) {
289 synchronized (mMutex) {
290 final ConditionRecord r = getRecordLocked(conditionId, component, false /*create*/);
291 if (r == null) {
292 Slog.w(TAG, "Unable to unsubscribe to " + component + " " + conditionId);
293 return;
John Spurlocke77bb362014-04-26 10:24:59 -0400294 }
John Spurlockb2278d62015-04-07 12:47:12 -0400295 if (!r.subscribed) return;
296 unsubscribeLocked(r);;
John Spurlocke77bb362014-04-26 10:24:59 -0400297 }
298 }
299
John Spurlock3b98b3f2014-05-01 09:08:48 -0400300 private void subscribeLocked(ConditionRecord r) {
301 if (DEBUG) Slog.d(TAG, "subscribeLocked " + r);
302 final IConditionProvider provider = provider(r);
John Spurlock6ae82a72014-07-16 16:23:01 -0400303 RemoteException re = null;
304 if (provider != null) {
305 try {
John Spurlockb2278d62015-04-07 12:47:12 -0400306 Slog.d(TAG, "Subscribing to " + r.id + " with " + r.component);
John Spurlock6ae82a72014-07-16 16:23:01 -0400307 provider.onSubscribe(r.id);
John Spurlockb2278d62015-04-07 12:47:12 -0400308 r.subscribed = true;
John Spurlock6ae82a72014-07-16 16:23:01 -0400309 } catch (RemoteException e) {
310 Slog.w(TAG, "Error subscribing to " + r, e);
311 re = e;
312 }
John Spurlocke77bb362014-04-26 10:24:59 -0400313 }
John Spurlock6ae82a72014-07-16 16:23:01 -0400314 ZenLog.traceSubscribe(r != null ? r.id : null, provider, re);
John Spurlock3b98b3f2014-05-01 09:08:48 -0400315 }
316
John Spurlock530052a2014-11-30 16:26:19 -0500317 @SafeVarargs
John Spurlock3b98b3f2014-05-01 09:08:48 -0400318 private static <T> ArraySet<T> safeSet(T... items) {
319 final ArraySet<T> rt = new ArraySet<T>();
320 if (items == null || items.length == 0) return rt;
321 final int N = items.length;
322 for (int i = 0; i < N; i++) {
323 final T item = items[i];
324 if (item != null) {
325 rt.add(item);
326 }
327 }
328 return rt;
329 }
330
John Spurlock3b98b3f2014-05-01 09:08:48 -0400331 private void unsubscribeLocked(ConditionRecord r) {
332 if (DEBUG) Slog.d(TAG, "unsubscribeLocked " + r);
333 final IConditionProvider provider = provider(r);
John Spurlock6ae82a72014-07-16 16:23:01 -0400334 RemoteException re = null;
335 if (provider != null) {
336 try {
337 provider.onUnsubscribe(r.id);
338 } catch (RemoteException e) {
339 Slog.w(TAG, "Error unsubscribing to " + r, e);
340 re = e;
341 }
John Spurlockb2278d62015-04-07 12:47:12 -0400342 r.subscribed = false;
John Spurlock3b98b3f2014-05-01 09:08:48 -0400343 }
John Spurlock6ae82a72014-07-16 16:23:01 -0400344 ZenLog.traceUnsubscribe(r != null ? r.id : null, provider, re);
John Spurlock3b98b3f2014-05-01 09:08:48 -0400345 }
346
347 private static IConditionProvider provider(ConditionRecord r) {
348 return r == null ? null : provider(r.info);
John Spurlocke77bb362014-04-26 10:24:59 -0400349 }
350
351 private static IConditionProvider provider(ManagedServiceInfo info) {
352 return info == null ? null : (IConditionProvider) info.service;
353 }
354
355 private void requestConditionsLocked(int flags) {
356 for (ManagedServiceInfo info : mServices) {
357 final IConditionProvider provider = provider(info);
358 if (provider == null) continue;
John Spurlock50806fc2014-07-15 10:22:02 -0400359 // clear all stored conditions from this provider that we no longer care about
360 for (int i = mRecords.size() - 1; i >= 0; i--) {
361 final ConditionRecord r = mRecords.get(i);
362 if (r.info != info) continue;
John Spurlockb2278d62015-04-07 12:47:12 -0400363 if (r.subscribed) continue;
John Spurlock50806fc2014-07-15 10:22:02 -0400364 mRecords.remove(i);
365 }
John Spurlocke77bb362014-04-26 10:24:59 -0400366 try {
367 provider.onRequestConditions(flags);
368 } catch (RemoteException e) {
369 Slog.w(TAG, "Error requesting conditions from " + info.component, e);
370 }
371 }
John Spurlock7340fc82014-04-24 18:50:12 -0400372 }
John Spurlock1c923a32014-04-27 16:42:29 -0400373
John Spurlock3b98b3f2014-05-01 09:08:48 -0400374 private static class ConditionRecord {
375 public final Uri id;
376 public final ComponentName component;
377 public Condition condition;
378 public ManagedServiceInfo info;
John Spurlockb2278d62015-04-07 12:47:12 -0400379 public boolean subscribed;
John Spurlock3b98b3f2014-05-01 09:08:48 -0400380
381 private ConditionRecord(Uri id, ComponentName component) {
382 this.id = id;
383 this.component = component;
384 }
385
386 @Override
387 public String toString() {
388 final StringBuilder sb = new StringBuilder("ConditionRecord[id=")
John Spurlockb2278d62015-04-07 12:47:12 -0400389 .append(id).append(",component=").append(component)
390 .append(",subscribed=").append(subscribed);
John Spurlock3b98b3f2014-05-01 09:08:48 -0400391 return sb.append(']').toString();
392 }
393 }
John Spurlockb2278d62015-04-07 12:47:12 -0400394
395 public interface Callback {
396 void onBootComplete();
John Spurlock39581cc2015-04-10 11:59:01 -0400397 void onServiceAdded(ComponentName component);
John Spurlockb2278d62015-04-07 12:47:12 -0400398 void onConditionChanged(Uri id, Condition condition);
399 void onUserSwitched();
400 }
401
John Spurlock7340fc82014-04-24 18:50:12 -0400402}