blob: 62fe70cf176fec0778b16e787790364c9019ef41 [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
Julia Reynolds6e839b02016-04-13 10:01:17 -040019import android.annotation.NonNull;
John Spurlock3b98b3f2014-05-01 09:08:48 -040020import android.content.ComponentName;
Julia Reynoldsc279b992015-10-30 08:23:51 -040021import android.content.ContentResolver;
John Spurlock7340fc82014-04-24 18:50:12 -040022import android.content.Context;
John Spurlocke77bb362014-04-26 10:24:59 -040023import android.net.Uri;
John Spurlock7340fc82014-04-24 18:50:12 -040024import android.os.Handler;
25import android.os.IBinder;
26import android.os.IInterface;
John Spurlocke77bb362014-04-26 10:24:59 -040027import android.os.RemoteException;
John Spurlock856edeb2014-06-01 20:36:47 -040028import android.os.UserHandle;
John Spurlock7340fc82014-04-24 18:50:12 -040029import android.provider.Settings;
John Spurlocke77bb362014-04-26 10:24:59 -040030import android.service.notification.Condition;
John Spurlock7340fc82014-04-24 18:50:12 -040031import android.service.notification.ConditionProviderService;
John Spurlocke77bb362014-04-26 10:24:59 -040032import android.service.notification.IConditionProvider;
Julia Reynoldsc279b992015-10-30 08:23:51 -040033import android.text.TextUtils;
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;
44
John Spurlock7340fc82014-04-24 18:50:12 -040045public class ConditionProviders extends ManagedServices {
John Spurlockb2278d62015-04-07 12:47:12 -040046 private final ArrayList<ConditionRecord> mRecords = new ArrayList<>();
John Spurlockb2278d62015-04-07 12:47:12 -040047 private final ArraySet<String> mSystemConditionProviderNames;
48 private final ArraySet<SystemConditionProviderService> mSystemConditionProviders
49 = new ArraySet<>();
John Spurlock7340fc82014-04-24 18:50:12 -040050
John Spurlockb2278d62015-04-07 12:47:12 -040051 private Callback mCallback;
John Spurlock856edeb2014-06-01 20:36:47 -040052
John Spurlockb2278d62015-04-07 12:47:12 -040053 public ConditionProviders(Context context, Handler handler, UserProfiles userProfiles) {
John Spurlocke77bb362014-04-26 10:24:59 -040054 super(context, handler, new Object(), userProfiles);
John Spurlockb2278d62015-04-07 12:47:12 -040055 mSystemConditionProviderNames = safeSet(PropConfig.getStringArray(mContext,
John Spurlock530052a2014-11-30 16:26:19 -050056 "system.condition.providers",
57 R.array.config_system_condition_providers));
John Spurlock530052a2014-11-30 16:26:19 -050058 }
59
John Spurlockb2278d62015-04-07 12:47:12 -040060 public void setCallback(Callback callback) {
61 mCallback = callback;
62 }
63
64 public boolean isSystemProviderEnabled(String path) {
65 return mSystemConditionProviderNames.contains(path);
66 }
67
68 public void addSystemProvider(SystemConditionProviderService service) {
69 mSystemConditionProviders.add(service);
70 service.attachBase(mContext);
Xiaohui Chenddbe4ca2015-08-13 16:20:56 -070071 registerService(service.asInterface(), service.getComponent(), UserHandle.USER_SYSTEM);
John Spurlockb2278d62015-04-07 12:47:12 -040072 }
73
74 public Iterable<SystemConditionProviderService> getSystemProviders() {
75 return mSystemConditionProviders;
John Spurlock7340fc82014-04-24 18:50:12 -040076 }
77
78 @Override
79 protected Config getConfig() {
John Spurlockb2278d62015-04-07 12:47:12 -040080 final Config c = new Config();
John Spurlock7340fc82014-04-24 18:50:12 -040081 c.caption = "condition provider";
82 c.serviceInterface = ConditionProviderService.SERVICE_INTERFACE;
Julia Reynoldsc279b992015-10-30 08:23:51 -040083 c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES;
Julia Reynolds6e839b02016-04-13 10:01:17 -040084 c.secondarySettingName = Settings.Secure.ENABLED_NOTIFICATION_LISTENERS;
John Spurlock7340fc82014-04-24 18:50:12 -040085 c.bindPermission = android.Manifest.permission.BIND_CONDITION_PROVIDER_SERVICE;
86 c.settingsAction = Settings.ACTION_CONDITION_PROVIDER_SETTINGS;
87 c.clientLabel = R.string.condition_provider_service_binding_label;
88 return c;
89 }
90
91 @Override
John Spurlock25e2d242014-06-27 13:58:23 -040092 public void dump(PrintWriter pw, DumpFilter filter) {
93 super.dump(pw, filter);
John Spurlocke77bb362014-04-26 10:24:59 -040094 synchronized(mMutex) {
John Spurlock3b98b3f2014-05-01 09:08:48 -040095 pw.print(" mRecords("); pw.print(mRecords.size()); pw.println("):");
96 for (int i = 0; i < mRecords.size(); i++) {
John Spurlock856edeb2014-06-01 20:36:47 -040097 final ConditionRecord r = mRecords.get(i);
John Spurlock25e2d242014-06-27 13:58:23 -040098 if (filter != null && !filter.matches(r.component)) continue;
John Spurlock856edeb2014-06-01 20:36:47 -040099 pw.print(" "); pw.println(r);
100 final String countdownDesc = CountdownConditionProvider.tryParseDescription(r.id);
101 if (countdownDesc != null) {
102 pw.print(" ("); pw.print(countdownDesc); pw.println(")");
103 }
John Spurlocke77bb362014-04-26 10:24:59 -0400104 }
105 }
John Spurlockb2278d62015-04-07 12:47:12 -0400106 pw.print(" mSystemConditionProviders: "); pw.println(mSystemConditionProviderNames);
107 for (int i = 0; i < mSystemConditionProviders.size(); i++) {
108 mSystemConditionProviders.valueAt(i).dump(pw, filter);
John Spurlock530052a2014-11-30 16:26:19 -0500109 }
John Spurlocke77bb362014-04-26 10:24:59 -0400110 }
111
112 @Override
John Spurlock7340fc82014-04-24 18:50:12 -0400113 protected IInterface asInterface(IBinder binder) {
114 return IConditionProvider.Stub.asInterface(binder);
115 }
116
117 @Override
Chris Wren51017d02015-12-15 15:34:46 -0500118 protected boolean checkType(IInterface service) {
119 return service instanceof IConditionProvider;
120 }
121
122 @Override
John Spurlock856edeb2014-06-01 20:36:47 -0400123 public void onBootPhaseAppsCanStart() {
124 super.onBootPhaseAppsCanStart();
John Spurlock2f096ed2015-05-04 11:58:26 -0400125 for (int i = 0; i < mSystemConditionProviders.size(); i++) {
126 mSystemConditionProviders.valueAt(i).onBootComplete();
127 }
John Spurlockb2278d62015-04-07 12:47:12 -0400128 if (mCallback != null) {
129 mCallback.onBootComplete();
John Spurlock530052a2014-11-30 16:26:19 -0500130 }
John Spurlock37bc92c2014-11-03 11:01:51 -0500131 }
132
133 @Override
John Spurlock1b8b22b2015-05-20 09:47:13 -0400134 public void onUserSwitched(int user) {
135 super.onUserSwitched(user);
John Spurlockb2278d62015-04-07 12:47:12 -0400136 if (mCallback != null) {
137 mCallback.onUserSwitched();
John Spurlock530052a2014-11-30 16:26:19 -0500138 }
John Spurlock856edeb2014-06-01 20:36:47 -0400139 }
140
141 @Override
John Spurlock3b98b3f2014-05-01 09:08:48 -0400142 protected void onServiceAdded(ManagedServiceInfo info) {
John Spurlock3b98b3f2014-05-01 09:08:48 -0400143 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 Spurlock39581cc2015-04-10 11:59:01 -0400149 if (mCallback != null) {
150 mCallback.onServiceAdded(info.component);
151 }
John Spurlocke77bb362014-04-26 10:24:59 -0400152 }
153
154 @Override
155 protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
156 if (removed == null) return;
John Spurlock3b98b3f2014-05-01 09:08:48 -0400157 for (int i = mRecords.size() - 1; i >= 0; i--) {
158 final ConditionRecord r = mRecords.get(i);
159 if (!r.component.equals(removed.component)) continue;
John Spurlock3b98b3f2014-05-01 09:08:48 -0400160 mRecords.remove(i);
John Spurlocke77bb362014-04-26 10:24:59 -0400161 }
162 }
163
164 public ManagedServiceInfo checkServiceToken(IConditionProvider provider) {
165 synchronized(mMutex) {
166 return checkServiceTokenLocked(provider);
167 }
168 }
169
Julia Reynolds1d6d16d2016-03-07 13:51:02 -0500170 private Condition[] removeDuplicateConditions(String pkg, Condition[] conditions) {
John Spurlock3b98b3f2014-05-01 09:08:48 -0400171 if (conditions == null || conditions.length == 0) return null;
172 final int N = conditions.length;
173 final ArrayMap<Uri, Condition> valid = new ArrayMap<Uri, Condition>(N);
174 for (int i = 0; i < N; i++) {
175 final Uri id = conditions[i].id;
John Spurlock3b98b3f2014-05-01 09:08:48 -0400176 if (valid.containsKey(id)) {
177 Slog.w(TAG, "Ignoring condition from " + pkg + " for duplicate id: " + id);
178 continue;
179 }
180 valid.put(id, conditions[i]);
181 }
182 if (valid.size() == 0) return null;
183 if (valid.size() == N) return conditions;
184 final Condition[] rt = new Condition[valid.size()];
185 for (int i = 0; i < rt.length; i++) {
186 rt[i] = valid.valueAt(i);
187 }
188 return rt;
189 }
190
John Spurlockb2278d62015-04-07 12:47:12 -0400191 private ConditionRecord getRecordLocked(Uri id, ComponentName component, boolean create) {
192 if (id == null || component == null) return null;
John Spurlock3b98b3f2014-05-01 09:08:48 -0400193 final int N = mRecords.size();
194 for (int i = 0; i < N; i++) {
195 final ConditionRecord r = mRecords.get(i);
196 if (r.id.equals(id) && r.component.equals(component)) {
197 return r;
198 }
199 }
John Spurlockb2278d62015-04-07 12:47:12 -0400200 if (create) {
201 final ConditionRecord r = new ConditionRecord(id, component);
202 mRecords.add(r);
203 return r;
204 }
205 return null;
John Spurlock3b98b3f2014-05-01 09:08:48 -0400206 }
207
John Spurlocke77bb362014-04-26 10:24:59 -0400208 public void notifyConditions(String pkg, ManagedServiceInfo info, Condition[] conditions) {
209 synchronized(mMutex) {
210 if (DEBUG) Slog.d(TAG, "notifyConditions pkg=" + pkg + " info=" + info + " conditions="
211 + (conditions == null ? null : Arrays.asList(conditions)));
Julia Reynolds1d6d16d2016-03-07 13:51:02 -0500212 conditions = removeDuplicateConditions(pkg, conditions);
John Spurlocke77bb362014-04-26 10:24:59 -0400213 if (conditions == null || conditions.length == 0) return;
214 final int N = conditions.length;
John Spurlock3b98b3f2014-05-01 09:08:48 -0400215 for (int i = 0; i < N; i++) {
216 final Condition c = conditions[i];
John Spurlockb2278d62015-04-07 12:47:12 -0400217 final ConditionRecord r = getRecordLocked(c.id, info.component, true /*create*/);
John Spurlock3b98b3f2014-05-01 09:08:48 -0400218 r.info = info;
219 r.condition = c;
Julia Reynolds0f50e4e2016-02-11 08:54:24 -0500220 }
221 }
222 final int N = conditions.length;
223 for (int i = 0; i < N; i++) {
224 final Condition c = conditions[i];
225 if (mCallback != null) {
226 mCallback.onConditionChanged(c.id, c);
John Spurlocke77bb362014-04-26 10:24:59 -0400227 }
228 }
229 }
230
John Spurlock39581cc2015-04-10 11:59:01 -0400231 public IConditionProvider findConditionProvider(ComponentName component) {
232 if (component == null) return null;
233 for (ManagedServiceInfo service : mServices) {
234 if (component.equals(service.component)) {
235 return provider(service);
236 }
237 }
238 return null;
239 }
240
John Spurlock21258a32015-05-27 18:22:55 -0400241 public Condition findCondition(ComponentName component, Uri conditionId) {
242 if (component == null || conditionId == null) return null;
243 synchronized (mMutex) {
244 final ConditionRecord r = getRecordLocked(conditionId, component, false /*create*/);
245 return r != null ? r.condition : null;
246 }
247 }
248
John Spurlockb2278d62015-04-07 12:47:12 -0400249 public void ensureRecordExists(ComponentName component, Uri conditionId,
250 IConditionProvider provider) {
John Spurlock530052a2014-11-30 16:26:19 -0500251 // constructed by convention, make sure the record exists...
John Spurlockb2278d62015-04-07 12:47:12 -0400252 final ConditionRecord r = getRecordLocked(conditionId, component, true /*create*/);
John Spurlock530052a2014-11-30 16:26:19 -0500253 if (r.info == null) {
254 // ... and is associated with the in-process service
255 r.info = checkServiceTokenLocked(provider);
256 }
257 }
258
Julia Reynoldsc279b992015-10-30 08:23:51 -0400259 @Override
Julia Reynolds6e839b02016-04-13 10:01:17 -0400260 protected @NonNull ArraySet<ComponentName> loadComponentNamesFromSetting(String settingName,
Julia Reynoldsc279b992015-10-30 08:23:51 -0400261 int userId) {
262 final ContentResolver cr = mContext.getContentResolver();
263 String settingValue = Settings.Secure.getStringForUser(
264 cr,
265 settingName,
266 userId);
267 if (TextUtils.isEmpty(settingValue))
Julia Reynolds6e839b02016-04-13 10:01:17 -0400268 return new ArraySet<>();
Julia Reynoldsc279b992015-10-30 08:23:51 -0400269 String[] packages = settingValue.split(ENABLED_SERVICES_SEPARATOR);
270 ArraySet<ComponentName> result = new ArraySet<>(packages.length);
271 for (int i = 0; i < packages.length; i++) {
272 if (!TextUtils.isEmpty(packages[i])) {
Julia Reynolds6e839b02016-04-13 10:01:17 -0400273 final ComponentName component = ComponentName.unflattenFromString(packages[i]);
274 if (component != null) {
275 result.addAll(queryPackageForServices(component.getPackageName(), userId));
276 } else {
277 result.addAll(queryPackageForServices(packages[i], userId));
278 }
Julia Reynoldsc279b992015-10-30 08:23:51 -0400279 }
280 }
281 return result;
282 }
283
John Spurlockb2278d62015-04-07 12:47:12 -0400284 public boolean subscribeIfNecessary(ComponentName component, Uri conditionId) {
285 synchronized (mMutex) {
286 final ConditionRecord r = getRecordLocked(conditionId, component, false /*create*/);
287 if (r == null) {
288 Slog.w(TAG, "Unable to subscribe to " + component + " " + conditionId);
289 return false;
John Spurlock856edeb2014-06-01 20:36:47 -0400290 }
John Spurlockb2278d62015-04-07 12:47:12 -0400291 if (r.subscribed) return true;
292 subscribeLocked(r);
293 return r.subscribed;
294 }
295 }
296
297 public void unsubscribeIfNecessary(ComponentName component, Uri conditionId) {
298 synchronized (mMutex) {
299 final ConditionRecord r = getRecordLocked(conditionId, component, false /*create*/);
300 if (r == null) {
301 Slog.w(TAG, "Unable to unsubscribe to " + component + " " + conditionId);
302 return;
John Spurlocke77bb362014-04-26 10:24:59 -0400303 }
John Spurlockb2278d62015-04-07 12:47:12 -0400304 if (!r.subscribed) return;
305 unsubscribeLocked(r);;
John Spurlocke77bb362014-04-26 10:24:59 -0400306 }
307 }
308
John Spurlock3b98b3f2014-05-01 09:08:48 -0400309 private void subscribeLocked(ConditionRecord r) {
310 if (DEBUG) Slog.d(TAG, "subscribeLocked " + r);
311 final IConditionProvider provider = provider(r);
John Spurlock6ae82a72014-07-16 16:23:01 -0400312 RemoteException re = null;
313 if (provider != null) {
314 try {
John Spurlockb2278d62015-04-07 12:47:12 -0400315 Slog.d(TAG, "Subscribing to " + r.id + " with " + r.component);
John Spurlock6ae82a72014-07-16 16:23:01 -0400316 provider.onSubscribe(r.id);
John Spurlockb2278d62015-04-07 12:47:12 -0400317 r.subscribed = true;
John Spurlock6ae82a72014-07-16 16:23:01 -0400318 } catch (RemoteException e) {
319 Slog.w(TAG, "Error subscribing to " + r, e);
320 re = e;
321 }
John Spurlocke77bb362014-04-26 10:24:59 -0400322 }
John Spurlock6ae82a72014-07-16 16:23:01 -0400323 ZenLog.traceSubscribe(r != null ? r.id : null, provider, re);
John Spurlock3b98b3f2014-05-01 09:08:48 -0400324 }
325
John Spurlock530052a2014-11-30 16:26:19 -0500326 @SafeVarargs
John Spurlock3b98b3f2014-05-01 09:08:48 -0400327 private static <T> ArraySet<T> safeSet(T... items) {
328 final ArraySet<T> rt = new ArraySet<T>();
329 if (items == null || items.length == 0) return rt;
330 final int N = items.length;
331 for (int i = 0; i < N; i++) {
332 final T item = items[i];
333 if (item != null) {
334 rt.add(item);
335 }
336 }
337 return rt;
338 }
339
John Spurlock3b98b3f2014-05-01 09:08:48 -0400340 private void unsubscribeLocked(ConditionRecord r) {
341 if (DEBUG) Slog.d(TAG, "unsubscribeLocked " + r);
342 final IConditionProvider provider = provider(r);
John Spurlock6ae82a72014-07-16 16:23:01 -0400343 RemoteException re = null;
344 if (provider != null) {
345 try {
346 provider.onUnsubscribe(r.id);
347 } catch (RemoteException e) {
348 Slog.w(TAG, "Error unsubscribing to " + r, e);
349 re = e;
350 }
John Spurlockb2278d62015-04-07 12:47:12 -0400351 r.subscribed = false;
John Spurlock3b98b3f2014-05-01 09:08:48 -0400352 }
John Spurlock6ae82a72014-07-16 16:23:01 -0400353 ZenLog.traceUnsubscribe(r != null ? r.id : null, provider, re);
John Spurlock3b98b3f2014-05-01 09:08:48 -0400354 }
355
356 private static IConditionProvider provider(ConditionRecord r) {
357 return r == null ? null : provider(r.info);
John Spurlocke77bb362014-04-26 10:24:59 -0400358 }
359
360 private static IConditionProvider provider(ManagedServiceInfo info) {
361 return info == null ? null : (IConditionProvider) info.service;
362 }
363
John Spurlock3b98b3f2014-05-01 09:08:48 -0400364 private static class ConditionRecord {
365 public final Uri id;
366 public final ComponentName component;
367 public Condition condition;
368 public ManagedServiceInfo info;
John Spurlockb2278d62015-04-07 12:47:12 -0400369 public boolean subscribed;
John Spurlock3b98b3f2014-05-01 09:08:48 -0400370
371 private ConditionRecord(Uri id, ComponentName component) {
372 this.id = id;
373 this.component = component;
374 }
375
376 @Override
377 public String toString() {
378 final StringBuilder sb = new StringBuilder("ConditionRecord[id=")
John Spurlockb2278d62015-04-07 12:47:12 -0400379 .append(id).append(",component=").append(component)
380 .append(",subscribed=").append(subscribed);
John Spurlock3b98b3f2014-05-01 09:08:48 -0400381 return sb.append(']').toString();
382 }
383 }
John Spurlockb2278d62015-04-07 12:47:12 -0400384
385 public interface Callback {
386 void onBootComplete();
John Spurlock39581cc2015-04-10 11:59:01 -0400387 void onServiceAdded(ComponentName component);
John Spurlockb2278d62015-04-07 12:47:12 -0400388 void onConditionChanged(Uri id, Condition condition);
389 void onUserSwitched();
390 }
391
John Spurlock7340fc82014-04-24 18:50:12 -0400392}