blob: 64477a52a9d9edbcee0d73c86427cd68a8904975 [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 Reynolds6434eb22016-08-08 17:19:26 -040019import android.app.INotificationManager;
20import android.app.NotificationManager;
John Spurlock3b98b3f2014-05-01 09:08:48 -040021import android.content.ComponentName;
John Spurlock7340fc82014-04-24 18:50:12 -040022import android.content.Context;
Julia Reynoldsb852e562017-06-06 16:14:18 -040023import android.content.pm.IPackageManager;
John Spurlocke77bb362014-04-26 10:24:59 -040024import android.net.Uri;
John Spurlock7340fc82014-04-24 18:50:12 -040025import 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;
33import android.util.ArrayMap;
John Spurlock3b98b3f2014-05-01 09:08:48 -040034import android.util.ArraySet;
John Spurlock7340fc82014-04-24 18:50:12 -040035import android.util.Slog;
36
37import com.android.internal.R;
Julia Reynoldsd1bf5f02017-07-11 10:39:58 -040038import com.android.internal.annotations.VisibleForTesting;
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 {
Julia Reynoldsb852e562017-06-06 16:14:18 -040046
Julia Reynoldsd1bf5f02017-07-11 10:39:58 -040047 @VisibleForTesting
48 static final String TAG_ENABLED_DND_APPS = "dnd_apps";
Julia Reynoldsb852e562017-06-06 16:14:18 -040049
John Spurlockb2278d62015-04-07 12:47:12 -040050 private final ArrayList<ConditionRecord> mRecords = new ArrayList<>();
John Spurlockb2278d62015-04-07 12:47:12 -040051 private final ArraySet<String> mSystemConditionProviderNames;
52 private final ArraySet<SystemConditionProviderService> mSystemConditionProviders
53 = new ArraySet<>();
John Spurlock7340fc82014-04-24 18:50:12 -040054
John Spurlockb2278d62015-04-07 12:47:12 -040055 private Callback mCallback;
John Spurlock856edeb2014-06-01 20:36:47 -040056
Julia Reynoldsb852e562017-06-06 16:14:18 -040057 public ConditionProviders(Context context, UserProfiles userProfiles, IPackageManager pm) {
58 super(context, new Object(), userProfiles, pm);
John Spurlockb2278d62015-04-07 12:47:12 -040059 mSystemConditionProviderNames = safeSet(PropConfig.getStringArray(mContext,
John Spurlock530052a2014-11-30 16:26:19 -050060 "system.condition.providers",
61 R.array.config_system_condition_providers));
Julia Reynoldsb852e562017-06-06 16:14:18 -040062 mApprovalLevel = APPROVAL_BY_PACKAGE;
John Spurlock530052a2014-11-30 16:26:19 -050063 }
64
John Spurlockb2278d62015-04-07 12:47:12 -040065 public void setCallback(Callback callback) {
66 mCallback = callback;
67 }
68
69 public boolean isSystemProviderEnabled(String path) {
70 return mSystemConditionProviderNames.contains(path);
71 }
72
73 public void addSystemProvider(SystemConditionProviderService service) {
74 mSystemConditionProviders.add(service);
75 service.attachBase(mContext);
Xiaohui Chenddbe4ca2015-08-13 16:20:56 -070076 registerService(service.asInterface(), service.getComponent(), UserHandle.USER_SYSTEM);
John Spurlockb2278d62015-04-07 12:47:12 -040077 }
78
79 public Iterable<SystemConditionProviderService> getSystemProviders() {
80 return mSystemConditionProviders;
John Spurlock7340fc82014-04-24 18:50:12 -040081 }
82
83 @Override
84 protected Config getConfig() {
John Spurlockb2278d62015-04-07 12:47:12 -040085 final Config c = new Config();
John Spurlock7340fc82014-04-24 18:50:12 -040086 c.caption = "condition provider";
87 c.serviceInterface = ConditionProviderService.SERVICE_INTERFACE;
Julia Reynoldsc279b992015-10-30 08:23:51 -040088 c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES;
Julia Reynoldsd1bf5f02017-07-11 10:39:58 -040089 c.xmlTag = TAG_ENABLED_DND_APPS;
Julia Reynolds6e839b02016-04-13 10:01:17 -040090 c.secondarySettingName = Settings.Secure.ENABLED_NOTIFICATION_LISTENERS;
John Spurlock7340fc82014-04-24 18:50:12 -040091 c.bindPermission = android.Manifest.permission.BIND_CONDITION_PROVIDER_SERVICE;
92 c.settingsAction = Settings.ACTION_CONDITION_PROVIDER_SETTINGS;
93 c.clientLabel = R.string.condition_provider_service_binding_label;
94 return c;
95 }
96
97 @Override
John Spurlock25e2d242014-06-27 13:58:23 -040098 public void dump(PrintWriter pw, DumpFilter filter) {
99 super.dump(pw, filter);
John Spurlocke77bb362014-04-26 10:24:59 -0400100 synchronized(mMutex) {
John Spurlock3b98b3f2014-05-01 09:08:48 -0400101 pw.print(" mRecords("); pw.print(mRecords.size()); pw.println("):");
102 for (int i = 0; i < mRecords.size(); i++) {
John Spurlock856edeb2014-06-01 20:36:47 -0400103 final ConditionRecord r = mRecords.get(i);
John Spurlock25e2d242014-06-27 13:58:23 -0400104 if (filter != null && !filter.matches(r.component)) continue;
John Spurlock856edeb2014-06-01 20:36:47 -0400105 pw.print(" "); pw.println(r);
106 final String countdownDesc = CountdownConditionProvider.tryParseDescription(r.id);
107 if (countdownDesc != null) {
108 pw.print(" ("); pw.print(countdownDesc); pw.println(")");
109 }
John Spurlocke77bb362014-04-26 10:24:59 -0400110 }
111 }
John Spurlockb2278d62015-04-07 12:47:12 -0400112 pw.print(" mSystemConditionProviders: "); pw.println(mSystemConditionProviderNames);
113 for (int i = 0; i < mSystemConditionProviders.size(); i++) {
114 mSystemConditionProviders.valueAt(i).dump(pw, filter);
John Spurlock530052a2014-11-30 16:26:19 -0500115 }
John Spurlocke77bb362014-04-26 10:24:59 -0400116 }
117
118 @Override
John Spurlock7340fc82014-04-24 18:50:12 -0400119 protected IInterface asInterface(IBinder binder) {
120 return IConditionProvider.Stub.asInterface(binder);
121 }
122
123 @Override
Chris Wren51017d02015-12-15 15:34:46 -0500124 protected boolean checkType(IInterface service) {
125 return service instanceof IConditionProvider;
126 }
127
128 @Override
John Spurlock856edeb2014-06-01 20:36:47 -0400129 public void onBootPhaseAppsCanStart() {
130 super.onBootPhaseAppsCanStart();
John Spurlock2f096ed2015-05-04 11:58:26 -0400131 for (int i = 0; i < mSystemConditionProviders.size(); i++) {
132 mSystemConditionProviders.valueAt(i).onBootComplete();
133 }
John Spurlockb2278d62015-04-07 12:47:12 -0400134 if (mCallback != null) {
135 mCallback.onBootComplete();
John Spurlock530052a2014-11-30 16:26:19 -0500136 }
John Spurlock37bc92c2014-11-03 11:01:51 -0500137 }
138
139 @Override
John Spurlock1b8b22b2015-05-20 09:47:13 -0400140 public void onUserSwitched(int user) {
141 super.onUserSwitched(user);
John Spurlockb2278d62015-04-07 12:47:12 -0400142 if (mCallback != null) {
143 mCallback.onUserSwitched();
John Spurlock530052a2014-11-30 16:26:19 -0500144 }
John Spurlock856edeb2014-06-01 20:36:47 -0400145 }
146
147 @Override
John Spurlock3b98b3f2014-05-01 09:08:48 -0400148 protected void onServiceAdded(ManagedServiceInfo info) {
John Spurlock3b98b3f2014-05-01 09:08:48 -0400149 final IConditionProvider provider = provider(info);
John Spurlocke77bb362014-04-26 10:24:59 -0400150 try {
151 provider.onConnected();
152 } catch (RemoteException e) {
Julia Reynolds8f056002018-07-13 15:12:29 -0400153 Slog.e(TAG, "can't connect to service " + info, e);
John Spurlocke77bb362014-04-26 10:24:59 -0400154 // we tried
155 }
John Spurlock39581cc2015-04-10 11:59:01 -0400156 if (mCallback != null) {
157 mCallback.onServiceAdded(info.component);
158 }
John Spurlocke77bb362014-04-26 10:24:59 -0400159 }
160
161 @Override
162 protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
163 if (removed == null) return;
John Spurlock3b98b3f2014-05-01 09:08:48 -0400164 for (int i = mRecords.size() - 1; i >= 0; i--) {
165 final ConditionRecord r = mRecords.get(i);
166 if (!r.component.equals(removed.component)) continue;
John Spurlock3b98b3f2014-05-01 09:08:48 -0400167 mRecords.remove(i);
John Spurlocke77bb362014-04-26 10:24:59 -0400168 }
169 }
170
Julia Reynolds6434eb22016-08-08 17:19:26 -0400171 @Override
Julia Reynoldsb852e562017-06-06 16:14:18 -0400172 public void onPackagesChanged(boolean removingPackage, String[] pkgList, int[] uid) {
Julia Reynolds6434eb22016-08-08 17:19:26 -0400173 if (removingPackage) {
174 INotificationManager inm = NotificationManager.getService();
175
176 if (pkgList != null && (pkgList.length > 0)) {
177 for (String pkgName : pkgList) {
178 try {
179 inm.removeAutomaticZenRules(pkgName);
180 inm.setNotificationPolicyAccessGranted(pkgName, false);
181 } catch (Exception e) {
182 Slog.e(TAG, "Failed to clean up rules for " + pkgName, e);
183 }
184 }
185 }
186 }
Julia Reynoldsb852e562017-06-06 16:14:18 -0400187 super.onPackagesChanged(removingPackage, pkgList, uid);
Julia Reynolds6434eb22016-08-08 17:19:26 -0400188 }
189
Julia Reynoldse5ce071bf2017-09-15 14:26:32 -0400190 @Override
191 protected boolean isValidEntry(String packageOrComponent, int userId) {
192 return true;
193 }
194
Julia Reynoldsd0ceefa2019-03-03 16:10:52 -0500195 @Override
196 protected String getRequiredPermission() {
197 return null;
198 }
199
John Spurlocke77bb362014-04-26 10:24:59 -0400200 public ManagedServiceInfo checkServiceToken(IConditionProvider provider) {
201 synchronized(mMutex) {
202 return checkServiceTokenLocked(provider);
203 }
204 }
205
Julia Reynolds1d6d16d2016-03-07 13:51:02 -0500206 private Condition[] removeDuplicateConditions(String pkg, Condition[] conditions) {
John Spurlock3b98b3f2014-05-01 09:08:48 -0400207 if (conditions == null || conditions.length == 0) return null;
208 final int N = conditions.length;
209 final ArrayMap<Uri, Condition> valid = new ArrayMap<Uri, Condition>(N);
210 for (int i = 0; i < N; i++) {
211 final Uri id = conditions[i].id;
John Spurlock3b98b3f2014-05-01 09:08:48 -0400212 if (valid.containsKey(id)) {
213 Slog.w(TAG, "Ignoring condition from " + pkg + " for duplicate id: " + id);
214 continue;
215 }
216 valid.put(id, conditions[i]);
217 }
218 if (valid.size() == 0) return null;
219 if (valid.size() == N) return conditions;
220 final Condition[] rt = new Condition[valid.size()];
221 for (int i = 0; i < rt.length; i++) {
222 rt[i] = valid.valueAt(i);
223 }
224 return rt;
225 }
226
John Spurlockb2278d62015-04-07 12:47:12 -0400227 private ConditionRecord getRecordLocked(Uri id, ComponentName component, boolean create) {
228 if (id == null || component == null) return null;
John Spurlock3b98b3f2014-05-01 09:08:48 -0400229 final int N = mRecords.size();
230 for (int i = 0; i < N; i++) {
231 final ConditionRecord r = mRecords.get(i);
232 if (r.id.equals(id) && r.component.equals(component)) {
233 return r;
234 }
235 }
John Spurlockb2278d62015-04-07 12:47:12 -0400236 if (create) {
237 final ConditionRecord r = new ConditionRecord(id, component);
238 mRecords.add(r);
239 return r;
240 }
241 return null;
John Spurlock3b98b3f2014-05-01 09:08:48 -0400242 }
243
John Spurlocke77bb362014-04-26 10:24:59 -0400244 public void notifyConditions(String pkg, ManagedServiceInfo info, Condition[] conditions) {
245 synchronized(mMutex) {
246 if (DEBUG) Slog.d(TAG, "notifyConditions pkg=" + pkg + " info=" + info + " conditions="
247 + (conditions == null ? null : Arrays.asList(conditions)));
Julia Reynolds1d6d16d2016-03-07 13:51:02 -0500248 conditions = removeDuplicateConditions(pkg, conditions);
John Spurlocke77bb362014-04-26 10:24:59 -0400249 if (conditions == null || conditions.length == 0) return;
250 final int N = conditions.length;
John Spurlock3b98b3f2014-05-01 09:08:48 -0400251 for (int i = 0; i < N; i++) {
252 final Condition c = conditions[i];
John Spurlockb2278d62015-04-07 12:47:12 -0400253 final ConditionRecord r = getRecordLocked(c.id, info.component, true /*create*/);
John Spurlock3b98b3f2014-05-01 09:08:48 -0400254 r.info = info;
255 r.condition = c;
Julia Reynolds0f50e4e2016-02-11 08:54:24 -0500256 }
257 }
258 final int N = conditions.length;
259 for (int i = 0; i < N; i++) {
260 final Condition c = conditions[i];
261 if (mCallback != null) {
262 mCallback.onConditionChanged(c.id, c);
John Spurlocke77bb362014-04-26 10:24:59 -0400263 }
264 }
265 }
266
John Spurlock39581cc2015-04-10 11:59:01 -0400267 public IConditionProvider findConditionProvider(ComponentName component) {
268 if (component == null) return null;
Julia Reynolds00314d92017-04-14 10:01:24 -0400269 for (ManagedServiceInfo service : getServices()) {
John Spurlock39581cc2015-04-10 11:59:01 -0400270 if (component.equals(service.component)) {
271 return provider(service);
272 }
273 }
274 return null;
275 }
276
John Spurlock21258a32015-05-27 18:22:55 -0400277 public Condition findCondition(ComponentName component, Uri conditionId) {
278 if (component == null || conditionId == null) return null;
279 synchronized (mMutex) {
280 final ConditionRecord r = getRecordLocked(conditionId, component, false /*create*/);
281 return r != null ? r.condition : null;
282 }
283 }
284
John Spurlockb2278d62015-04-07 12:47:12 -0400285 public void ensureRecordExists(ComponentName component, Uri conditionId,
286 IConditionProvider provider) {
Julia Reynolds3ff1fd92018-08-29 09:57:11 -0400287 synchronized (mMutex) {
288 // constructed by convention, make sure the record exists...
289 final ConditionRecord r = getRecordLocked(conditionId, component, true /*create*/);
290 if (r.info == null) {
291 // ... and is associated with the in-process service
292 r.info = checkServiceTokenLocked(provider);
293 }
John Spurlock530052a2014-11-30 16:26:19 -0500294 }
295 }
296
John Spurlockb2278d62015-04-07 12:47:12 -0400297 public boolean subscribeIfNecessary(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 subscribe to " + component + " " + conditionId);
302 return false;
John Spurlock856edeb2014-06-01 20:36:47 -0400303 }
John Spurlockb2278d62015-04-07 12:47:12 -0400304 if (r.subscribed) return true;
305 subscribeLocked(r);
306 return r.subscribed;
307 }
308 }
309
310 public void unsubscribeIfNecessary(ComponentName component, Uri conditionId) {
311 synchronized (mMutex) {
312 final ConditionRecord r = getRecordLocked(conditionId, component, false /*create*/);
313 if (r == null) {
314 Slog.w(TAG, "Unable to unsubscribe to " + component + " " + conditionId);
315 return;
John Spurlocke77bb362014-04-26 10:24:59 -0400316 }
John Spurlockb2278d62015-04-07 12:47:12 -0400317 if (!r.subscribed) return;
318 unsubscribeLocked(r);;
John Spurlocke77bb362014-04-26 10:24:59 -0400319 }
320 }
321
John Spurlock3b98b3f2014-05-01 09:08:48 -0400322 private void subscribeLocked(ConditionRecord r) {
323 if (DEBUG) Slog.d(TAG, "subscribeLocked " + r);
324 final IConditionProvider provider = provider(r);
John Spurlock6ae82a72014-07-16 16:23:01 -0400325 RemoteException re = null;
326 if (provider != null) {
327 try {
John Spurlockb2278d62015-04-07 12:47:12 -0400328 Slog.d(TAG, "Subscribing to " + r.id + " with " + r.component);
John Spurlock6ae82a72014-07-16 16:23:01 -0400329 provider.onSubscribe(r.id);
John Spurlockb2278d62015-04-07 12:47:12 -0400330 r.subscribed = true;
John Spurlock6ae82a72014-07-16 16:23:01 -0400331 } catch (RemoteException e) {
332 Slog.w(TAG, "Error subscribing to " + r, e);
333 re = e;
334 }
John Spurlocke77bb362014-04-26 10:24:59 -0400335 }
John Spurlock6ae82a72014-07-16 16:23:01 -0400336 ZenLog.traceSubscribe(r != null ? r.id : null, provider, re);
John Spurlock3b98b3f2014-05-01 09:08:48 -0400337 }
338
John Spurlock530052a2014-11-30 16:26:19 -0500339 @SafeVarargs
John Spurlock3b98b3f2014-05-01 09:08:48 -0400340 private static <T> ArraySet<T> safeSet(T... items) {
341 final ArraySet<T> rt = new ArraySet<T>();
342 if (items == null || items.length == 0) return rt;
343 final int N = items.length;
344 for (int i = 0; i < N; i++) {
345 final T item = items[i];
346 if (item != null) {
347 rt.add(item);
348 }
349 }
350 return rt;
351 }
352
John Spurlock3b98b3f2014-05-01 09:08:48 -0400353 private void unsubscribeLocked(ConditionRecord r) {
354 if (DEBUG) Slog.d(TAG, "unsubscribeLocked " + r);
355 final IConditionProvider provider = provider(r);
John Spurlock6ae82a72014-07-16 16:23:01 -0400356 RemoteException re = null;
357 if (provider != null) {
358 try {
359 provider.onUnsubscribe(r.id);
360 } catch (RemoteException e) {
361 Slog.w(TAG, "Error unsubscribing to " + r, e);
362 re = e;
363 }
John Spurlockb2278d62015-04-07 12:47:12 -0400364 r.subscribed = false;
John Spurlock3b98b3f2014-05-01 09:08:48 -0400365 }
John Spurlock6ae82a72014-07-16 16:23:01 -0400366 ZenLog.traceUnsubscribe(r != null ? r.id : null, provider, re);
John Spurlock3b98b3f2014-05-01 09:08:48 -0400367 }
368
369 private static IConditionProvider provider(ConditionRecord r) {
370 return r == null ? null : provider(r.info);
John Spurlocke77bb362014-04-26 10:24:59 -0400371 }
372
373 private static IConditionProvider provider(ManagedServiceInfo info) {
374 return info == null ? null : (IConditionProvider) info.service;
375 }
376
John Spurlock3b98b3f2014-05-01 09:08:48 -0400377 private static class ConditionRecord {
378 public final Uri id;
379 public final ComponentName component;
380 public Condition condition;
381 public ManagedServiceInfo info;
John Spurlockb2278d62015-04-07 12:47:12 -0400382 public boolean subscribed;
John Spurlock3b98b3f2014-05-01 09:08:48 -0400383
384 private ConditionRecord(Uri id, ComponentName component) {
385 this.id = id;
386 this.component = component;
387 }
388
389 @Override
390 public String toString() {
391 final StringBuilder sb = new StringBuilder("ConditionRecord[id=")
John Spurlockb2278d62015-04-07 12:47:12 -0400392 .append(id).append(",component=").append(component)
393 .append(",subscribed=").append(subscribed);
John Spurlock3b98b3f2014-05-01 09:08:48 -0400394 return sb.append(']').toString();
395 }
396 }
John Spurlockb2278d62015-04-07 12:47:12 -0400397
398 public interface Callback {
399 void onBootComplete();
John Spurlock39581cc2015-04-10 11:59:01 -0400400 void onServiceAdded(ComponentName component);
John Spurlockb2278d62015-04-07 12:47:12 -0400401 void onConditionChanged(Uri id, Condition condition);
402 void onUserSwitched();
403 }
404
John Spurlock7340fc82014-04-24 18:50:12 -0400405}