blob: 2fab2887c28ef821a278a0cafbba2e2d1f116229 [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;
Julia Reynolds6434eb22016-08-08 17:19:26 -040020import android.app.INotificationManager;
21import android.app.NotificationManager;
John Spurlock3b98b3f2014-05-01 09:08:48 -040022import android.content.ComponentName;
Julia Reynoldsc279b992015-10-30 08:23:51 -040023import android.content.ContentResolver;
John Spurlock7340fc82014-04-24 18:50:12 -040024import android.content.Context;
John Spurlocke77bb362014-04-26 10:24:59 -040025import android.net.Uri;
John Spurlock7340fc82014-04-24 18:50:12 -040026import android.os.Handler;
27import android.os.IBinder;
28import android.os.IInterface;
John Spurlocke77bb362014-04-26 10:24:59 -040029import android.os.RemoteException;
John Spurlock856edeb2014-06-01 20:36:47 -040030import android.os.UserHandle;
John Spurlock7340fc82014-04-24 18:50:12 -040031import android.provider.Settings;
John Spurlocke77bb362014-04-26 10:24:59 -040032import android.service.notification.Condition;
John Spurlock7340fc82014-04-24 18:50:12 -040033import android.service.notification.ConditionProviderService;
John Spurlocke77bb362014-04-26 10:24:59 -040034import android.service.notification.IConditionProvider;
Julia Reynoldsc279b992015-10-30 08:23:51 -040035import android.text.TextUtils;
John Spurlocke77bb362014-04-26 10:24:59 -040036import android.util.ArrayMap;
John Spurlock3b98b3f2014-05-01 09:08:48 -040037import android.util.ArraySet;
John Spurlock7340fc82014-04-24 18:50:12 -040038import android.util.Slog;
39
40import com.android.internal.R;
John Spurlock25e2d242014-06-27 13:58:23 -040041import com.android.server.notification.NotificationManagerService.DumpFilter;
John Spurlock7340fc82014-04-24 18:50:12 -040042
John Spurlocke77bb362014-04-26 10:24:59 -040043import java.io.PrintWriter;
John Spurlock3b98b3f2014-05-01 09:08:48 -040044import java.util.ArrayList;
John Spurlocke77bb362014-04-26 10:24:59 -040045import java.util.Arrays;
46
John Spurlock7340fc82014-04-24 18:50:12 -040047public class ConditionProviders extends ManagedServices {
John Spurlockb2278d62015-04-07 12:47:12 -040048 private final ArrayList<ConditionRecord> mRecords = new ArrayList<>();
John Spurlockb2278d62015-04-07 12:47:12 -040049 private final ArraySet<String> mSystemConditionProviderNames;
50 private final ArraySet<SystemConditionProviderService> mSystemConditionProviders
51 = new ArraySet<>();
John Spurlock7340fc82014-04-24 18:50:12 -040052
John Spurlockb2278d62015-04-07 12:47:12 -040053 private Callback mCallback;
John Spurlock856edeb2014-06-01 20:36:47 -040054
John Spurlockb2278d62015-04-07 12:47:12 -040055 public ConditionProviders(Context context, Handler handler, UserProfiles userProfiles) {
John Spurlocke77bb362014-04-26 10:24:59 -040056 super(context, handler, new Object(), userProfiles);
John Spurlockb2278d62015-04-07 12:47:12 -040057 mSystemConditionProviderNames = safeSet(PropConfig.getStringArray(mContext,
John Spurlock530052a2014-11-30 16:26:19 -050058 "system.condition.providers",
59 R.array.config_system_condition_providers));
John Spurlock530052a2014-11-30 16:26:19 -050060 }
61
John Spurlockb2278d62015-04-07 12:47:12 -040062 public void setCallback(Callback callback) {
63 mCallback = callback;
64 }
65
66 public boolean isSystemProviderEnabled(String path) {
67 return mSystemConditionProviderNames.contains(path);
68 }
69
70 public void addSystemProvider(SystemConditionProviderService service) {
71 mSystemConditionProviders.add(service);
72 service.attachBase(mContext);
Xiaohui Chenddbe4ca2015-08-13 16:20:56 -070073 registerService(service.asInterface(), service.getComponent(), UserHandle.USER_SYSTEM);
John Spurlockb2278d62015-04-07 12:47:12 -040074 }
75
76 public Iterable<SystemConditionProviderService> getSystemProviders() {
77 return mSystemConditionProviders;
John Spurlock7340fc82014-04-24 18:50:12 -040078 }
79
80 @Override
81 protected Config getConfig() {
John Spurlockb2278d62015-04-07 12:47:12 -040082 final Config c = new Config();
John Spurlock7340fc82014-04-24 18:50:12 -040083 c.caption = "condition provider";
84 c.serviceInterface = ConditionProviderService.SERVICE_INTERFACE;
Julia Reynoldsc279b992015-10-30 08:23:51 -040085 c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES;
Julia Reynolds6e839b02016-04-13 10:01:17 -040086 c.secondarySettingName = Settings.Secure.ENABLED_NOTIFICATION_LISTENERS;
John Spurlock7340fc82014-04-24 18:50:12 -040087 c.bindPermission = android.Manifest.permission.BIND_CONDITION_PROVIDER_SERVICE;
88 c.settingsAction = Settings.ACTION_CONDITION_PROVIDER_SETTINGS;
89 c.clientLabel = R.string.condition_provider_service_binding_label;
90 return c;
91 }
92
93 @Override
John Spurlock25e2d242014-06-27 13:58:23 -040094 public void dump(PrintWriter pw, DumpFilter filter) {
95 super.dump(pw, filter);
John Spurlocke77bb362014-04-26 10:24:59 -040096 synchronized(mMutex) {
John Spurlock3b98b3f2014-05-01 09:08:48 -040097 pw.print(" mRecords("); pw.print(mRecords.size()); pw.println("):");
98 for (int i = 0; i < mRecords.size(); i++) {
John Spurlock856edeb2014-06-01 20:36:47 -040099 final ConditionRecord r = mRecords.get(i);
John Spurlock25e2d242014-06-27 13:58:23 -0400100 if (filter != null && !filter.matches(r.component)) continue;
John Spurlock856edeb2014-06-01 20:36:47 -0400101 pw.print(" "); pw.println(r);
102 final String countdownDesc = CountdownConditionProvider.tryParseDescription(r.id);
103 if (countdownDesc != null) {
104 pw.print(" ("); pw.print(countdownDesc); pw.println(")");
105 }
John Spurlocke77bb362014-04-26 10:24:59 -0400106 }
107 }
John Spurlockb2278d62015-04-07 12:47:12 -0400108 pw.print(" mSystemConditionProviders: "); pw.println(mSystemConditionProviderNames);
109 for (int i = 0; i < mSystemConditionProviders.size(); i++) {
110 mSystemConditionProviders.valueAt(i).dump(pw, filter);
John Spurlock530052a2014-11-30 16:26:19 -0500111 }
John Spurlocke77bb362014-04-26 10:24:59 -0400112 }
113
114 @Override
John Spurlock7340fc82014-04-24 18:50:12 -0400115 protected IInterface asInterface(IBinder binder) {
116 return IConditionProvider.Stub.asInterface(binder);
117 }
118
119 @Override
Chris Wren51017d02015-12-15 15:34:46 -0500120 protected boolean checkType(IInterface service) {
121 return service instanceof IConditionProvider;
122 }
123
124 @Override
John Spurlock856edeb2014-06-01 20:36:47 -0400125 public void onBootPhaseAppsCanStart() {
126 super.onBootPhaseAppsCanStart();
John Spurlock2f096ed2015-05-04 11:58:26 -0400127 for (int i = 0; i < mSystemConditionProviders.size(); i++) {
128 mSystemConditionProviders.valueAt(i).onBootComplete();
129 }
John Spurlockb2278d62015-04-07 12:47:12 -0400130 if (mCallback != null) {
131 mCallback.onBootComplete();
John Spurlock530052a2014-11-30 16:26:19 -0500132 }
John Spurlock37bc92c2014-11-03 11:01:51 -0500133 }
134
135 @Override
John Spurlock1b8b22b2015-05-20 09:47:13 -0400136 public void onUserSwitched(int user) {
137 super.onUserSwitched(user);
John Spurlockb2278d62015-04-07 12:47:12 -0400138 if (mCallback != null) {
139 mCallback.onUserSwitched();
John Spurlock530052a2014-11-30 16:26:19 -0500140 }
John Spurlock856edeb2014-06-01 20:36:47 -0400141 }
142
143 @Override
John Spurlock3b98b3f2014-05-01 09:08:48 -0400144 protected void onServiceAdded(ManagedServiceInfo info) {
John Spurlock3b98b3f2014-05-01 09:08:48 -0400145 final IConditionProvider provider = provider(info);
John Spurlocke77bb362014-04-26 10:24:59 -0400146 try {
147 provider.onConnected();
148 } catch (RemoteException e) {
149 // we tried
150 }
John Spurlock39581cc2015-04-10 11:59:01 -0400151 if (mCallback != null) {
152 mCallback.onServiceAdded(info.component);
153 }
John Spurlocke77bb362014-04-26 10:24:59 -0400154 }
155
156 @Override
157 protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
158 if (removed == null) return;
John Spurlock3b98b3f2014-05-01 09:08:48 -0400159 for (int i = mRecords.size() - 1; i >= 0; i--) {
160 final ConditionRecord r = mRecords.get(i);
161 if (!r.component.equals(removed.component)) continue;
John Spurlock3b98b3f2014-05-01 09:08:48 -0400162 mRecords.remove(i);
John Spurlocke77bb362014-04-26 10:24:59 -0400163 }
164 }
165
Julia Reynolds6434eb22016-08-08 17:19:26 -0400166 @Override
167 public void onPackagesChanged(boolean removingPackage, String[] pkgList) {
168 if (removingPackage) {
169 INotificationManager inm = NotificationManager.getService();
170
171 if (pkgList != null && (pkgList.length > 0)) {
172 for (String pkgName : pkgList) {
173 try {
174 inm.removeAutomaticZenRules(pkgName);
175 inm.setNotificationPolicyAccessGranted(pkgName, false);
176 } catch (Exception e) {
177 Slog.e(TAG, "Failed to clean up rules for " + pkgName, e);
178 }
179 }
180 }
181 }
182 super.onPackagesChanged(removingPackage, pkgList);
183 }
184
John Spurlocke77bb362014-04-26 10:24:59 -0400185 public ManagedServiceInfo checkServiceToken(IConditionProvider provider) {
186 synchronized(mMutex) {
187 return checkServiceTokenLocked(provider);
188 }
189 }
190
Julia Reynolds1d6d16d2016-03-07 13:51:02 -0500191 private Condition[] removeDuplicateConditions(String pkg, Condition[] conditions) {
John Spurlock3b98b3f2014-05-01 09:08:48 -0400192 if (conditions == null || conditions.length == 0) return null;
193 final int N = conditions.length;
194 final ArrayMap<Uri, Condition> valid = new ArrayMap<Uri, Condition>(N);
195 for (int i = 0; i < N; i++) {
196 final Uri id = conditions[i].id;
John Spurlock3b98b3f2014-05-01 09:08:48 -0400197 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)));
Julia Reynolds1d6d16d2016-03-07 13:51:02 -0500233 conditions = removeDuplicateConditions(pkg, conditions);
John Spurlocke77bb362014-04-26 10:24:59 -0400234 if (conditions == null || conditions.length == 0) return;
235 final int N = conditions.length;
John Spurlock3b98b3f2014-05-01 09:08:48 -0400236 for (int i = 0; i < N; i++) {
237 final Condition c = conditions[i];
John Spurlockb2278d62015-04-07 12:47:12 -0400238 final ConditionRecord r = getRecordLocked(c.id, info.component, true /*create*/);
John Spurlock3b98b3f2014-05-01 09:08:48 -0400239 r.info = info;
240 r.condition = c;
Julia Reynolds0f50e4e2016-02-11 08:54:24 -0500241 }
242 }
243 final int N = conditions.length;
244 for (int i = 0; i < N; i++) {
245 final Condition c = conditions[i];
246 if (mCallback != null) {
247 mCallback.onConditionChanged(c.id, c);
John Spurlocke77bb362014-04-26 10:24:59 -0400248 }
249 }
250 }
251
John Spurlock39581cc2015-04-10 11:59:01 -0400252 public IConditionProvider findConditionProvider(ComponentName component) {
253 if (component == null) return null;
254 for (ManagedServiceInfo service : mServices) {
255 if (component.equals(service.component)) {
256 return provider(service);
257 }
258 }
259 return null;
260 }
261
John Spurlock21258a32015-05-27 18:22:55 -0400262 public Condition findCondition(ComponentName component, Uri conditionId) {
263 if (component == null || conditionId == null) return null;
264 synchronized (mMutex) {
265 final ConditionRecord r = getRecordLocked(conditionId, component, false /*create*/);
266 return r != null ? r.condition : null;
267 }
268 }
269
John Spurlockb2278d62015-04-07 12:47:12 -0400270 public void ensureRecordExists(ComponentName component, Uri conditionId,
271 IConditionProvider provider) {
John Spurlock530052a2014-11-30 16:26:19 -0500272 // constructed by convention, make sure the record exists...
John Spurlockb2278d62015-04-07 12:47:12 -0400273 final ConditionRecord r = getRecordLocked(conditionId, component, true /*create*/);
John Spurlock530052a2014-11-30 16:26:19 -0500274 if (r.info == null) {
275 // ... and is associated with the in-process service
276 r.info = checkServiceTokenLocked(provider);
277 }
278 }
279
Julia Reynoldsc279b992015-10-30 08:23:51 -0400280 @Override
Julia Reynolds6e839b02016-04-13 10:01:17 -0400281 protected @NonNull ArraySet<ComponentName> loadComponentNamesFromSetting(String settingName,
Julia Reynoldsc279b992015-10-30 08:23:51 -0400282 int userId) {
283 final ContentResolver cr = mContext.getContentResolver();
284 String settingValue = Settings.Secure.getStringForUser(
285 cr,
286 settingName,
287 userId);
288 if (TextUtils.isEmpty(settingValue))
Julia Reynolds6e839b02016-04-13 10:01:17 -0400289 return new ArraySet<>();
Julia Reynoldsc279b992015-10-30 08:23:51 -0400290 String[] packages = settingValue.split(ENABLED_SERVICES_SEPARATOR);
291 ArraySet<ComponentName> result = new ArraySet<>(packages.length);
292 for (int i = 0; i < packages.length; i++) {
293 if (!TextUtils.isEmpty(packages[i])) {
Julia Reynolds6e839b02016-04-13 10:01:17 -0400294 final ComponentName component = ComponentName.unflattenFromString(packages[i]);
295 if (component != null) {
296 result.addAll(queryPackageForServices(component.getPackageName(), userId));
297 } else {
298 result.addAll(queryPackageForServices(packages[i], userId));
299 }
Julia Reynoldsc279b992015-10-30 08:23:51 -0400300 }
301 }
302 return result;
303 }
304
John Spurlockb2278d62015-04-07 12:47:12 -0400305 public boolean subscribeIfNecessary(ComponentName component, Uri conditionId) {
306 synchronized (mMutex) {
307 final ConditionRecord r = getRecordLocked(conditionId, component, false /*create*/);
308 if (r == null) {
309 Slog.w(TAG, "Unable to subscribe to " + component + " " + conditionId);
310 return false;
John Spurlock856edeb2014-06-01 20:36:47 -0400311 }
John Spurlockb2278d62015-04-07 12:47:12 -0400312 if (r.subscribed) return true;
313 subscribeLocked(r);
314 return r.subscribed;
315 }
316 }
317
318 public void unsubscribeIfNecessary(ComponentName component, Uri conditionId) {
319 synchronized (mMutex) {
320 final ConditionRecord r = getRecordLocked(conditionId, component, false /*create*/);
321 if (r == null) {
322 Slog.w(TAG, "Unable to unsubscribe to " + component + " " + conditionId);
323 return;
John Spurlocke77bb362014-04-26 10:24:59 -0400324 }
John Spurlockb2278d62015-04-07 12:47:12 -0400325 if (!r.subscribed) return;
326 unsubscribeLocked(r);;
John Spurlocke77bb362014-04-26 10:24:59 -0400327 }
328 }
329
John Spurlock3b98b3f2014-05-01 09:08:48 -0400330 private void subscribeLocked(ConditionRecord r) {
331 if (DEBUG) Slog.d(TAG, "subscribeLocked " + r);
332 final IConditionProvider provider = provider(r);
John Spurlock6ae82a72014-07-16 16:23:01 -0400333 RemoteException re = null;
334 if (provider != null) {
335 try {
John Spurlockb2278d62015-04-07 12:47:12 -0400336 Slog.d(TAG, "Subscribing to " + r.id + " with " + r.component);
John Spurlock6ae82a72014-07-16 16:23:01 -0400337 provider.onSubscribe(r.id);
John Spurlockb2278d62015-04-07 12:47:12 -0400338 r.subscribed = true;
John Spurlock6ae82a72014-07-16 16:23:01 -0400339 } catch (RemoteException e) {
340 Slog.w(TAG, "Error subscribing to " + r, e);
341 re = e;
342 }
John Spurlocke77bb362014-04-26 10:24:59 -0400343 }
John Spurlock6ae82a72014-07-16 16:23:01 -0400344 ZenLog.traceSubscribe(r != null ? r.id : null, provider, re);
John Spurlock3b98b3f2014-05-01 09:08:48 -0400345 }
346
John Spurlock530052a2014-11-30 16:26:19 -0500347 @SafeVarargs
John Spurlock3b98b3f2014-05-01 09:08:48 -0400348 private static <T> ArraySet<T> safeSet(T... items) {
349 final ArraySet<T> rt = new ArraySet<T>();
350 if (items == null || items.length == 0) return rt;
351 final int N = items.length;
352 for (int i = 0; i < N; i++) {
353 final T item = items[i];
354 if (item != null) {
355 rt.add(item);
356 }
357 }
358 return rt;
359 }
360
John Spurlock3b98b3f2014-05-01 09:08:48 -0400361 private void unsubscribeLocked(ConditionRecord r) {
362 if (DEBUG) Slog.d(TAG, "unsubscribeLocked " + r);
363 final IConditionProvider provider = provider(r);
John Spurlock6ae82a72014-07-16 16:23:01 -0400364 RemoteException re = null;
365 if (provider != null) {
366 try {
367 provider.onUnsubscribe(r.id);
368 } catch (RemoteException e) {
369 Slog.w(TAG, "Error unsubscribing to " + r, e);
370 re = e;
371 }
John Spurlockb2278d62015-04-07 12:47:12 -0400372 r.subscribed = false;
John Spurlock3b98b3f2014-05-01 09:08:48 -0400373 }
John Spurlock6ae82a72014-07-16 16:23:01 -0400374 ZenLog.traceUnsubscribe(r != null ? r.id : null, provider, re);
John Spurlock3b98b3f2014-05-01 09:08:48 -0400375 }
376
377 private static IConditionProvider provider(ConditionRecord r) {
378 return r == null ? null : provider(r.info);
John Spurlocke77bb362014-04-26 10:24:59 -0400379 }
380
381 private static IConditionProvider provider(ManagedServiceInfo info) {
382 return info == null ? null : (IConditionProvider) info.service;
383 }
384
John Spurlock3b98b3f2014-05-01 09:08:48 -0400385 private static class ConditionRecord {
386 public final Uri id;
387 public final ComponentName component;
388 public Condition condition;
389 public ManagedServiceInfo info;
John Spurlockb2278d62015-04-07 12:47:12 -0400390 public boolean subscribed;
John Spurlock3b98b3f2014-05-01 09:08:48 -0400391
392 private ConditionRecord(Uri id, ComponentName component) {
393 this.id = id;
394 this.component = component;
395 }
396
397 @Override
398 public String toString() {
399 final StringBuilder sb = new StringBuilder("ConditionRecord[id=")
John Spurlockb2278d62015-04-07 12:47:12 -0400400 .append(id).append(",component=").append(component)
401 .append(",subscribed=").append(subscribed);
John Spurlock3b98b3f2014-05-01 09:08:48 -0400402 return sb.append(']').toString();
403 }
404 }
John Spurlockb2278d62015-04-07 12:47:12 -0400405
406 public interface Callback {
407 void onBootComplete();
John Spurlock39581cc2015-04-10 11:59:01 -0400408 void onServiceAdded(ComponentName component);
John Spurlockb2278d62015-04-07 12:47:12 -0400409 void onConditionChanged(Uri id, Condition condition);
410 void onUserSwitched();
411 }
412
John Spurlock7340fc82014-04-24 18:50:12 -0400413}