• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.android.server.notification;
18 
19 import android.app.INotificationManager;
20 import android.app.NotificationManager;
21 import android.content.ComponentName;
22 import android.content.Context;
23 import android.content.pm.IPackageManager;
24 import android.net.Uri;
25 import android.os.IBinder;
26 import android.os.IInterface;
27 import android.os.RemoteException;
28 import android.os.UserHandle;
29 import android.provider.Settings;
30 import android.service.notification.Condition;
31 import android.service.notification.ConditionProviderService;
32 import android.service.notification.IConditionProvider;
33 import android.text.TextUtils;
34 import android.util.ArrayMap;
35 import android.util.ArraySet;
36 import android.util.Slog;
37 
38 import com.android.internal.R;
39 import com.android.internal.annotations.VisibleForTesting;
40 import com.android.server.notification.NotificationManagerService.DumpFilter;
41 
42 import org.xmlpull.v1.XmlSerializer;
43 
44 import java.io.IOException;
45 import java.io.PrintWriter;
46 import java.util.ArrayList;
47 import java.util.Arrays;
48 
49 public class ConditionProviders extends ManagedServices {
50 
51     @VisibleForTesting
52     static final String TAG_ENABLED_DND_APPS = "dnd_apps";
53 
54     private final ArrayList<ConditionRecord> mRecords = new ArrayList<>();
55     private final ArraySet<String> mSystemConditionProviderNames;
56     private final ArraySet<SystemConditionProviderService> mSystemConditionProviders
57             = new ArraySet<>();
58     private Callback mCallback;
59 
ConditionProviders(Context context, UserProfiles userProfiles, IPackageManager pm)60     public ConditionProviders(Context context, UserProfiles userProfiles, IPackageManager pm) {
61         super(context, new Object(), userProfiles, pm);
62         mSystemConditionProviderNames = safeSet(PropConfig.getStringArray(mContext,
63                 "system.condition.providers",
64                 R.array.config_system_condition_providers));
65         mApprovalLevel = APPROVAL_BY_PACKAGE;
66     }
67 
setCallback(Callback callback)68     public void setCallback(Callback callback) {
69         mCallback = callback;
70     }
71 
isSystemProviderEnabled(String path)72     public boolean isSystemProviderEnabled(String path) {
73         return mSystemConditionProviderNames.contains(path);
74     }
75 
addSystemProvider(SystemConditionProviderService service)76     public void addSystemProvider(SystemConditionProviderService service) {
77         mSystemConditionProviders.add(service);
78         service.attachBase(mContext);
79         registerSystemService(
80                 service.asInterface(), service.getComponent(), UserHandle.USER_SYSTEM);
81     }
82 
getSystemProviders()83     public Iterable<SystemConditionProviderService> getSystemProviders() {
84         return mSystemConditionProviders;
85     }
86 
87     @Override
88     protected ArrayMap<Boolean, ArrayList<ComponentName>>
resetComponents(String packageName, int userId)89             resetComponents(String packageName, int userId) {
90         resetPackage(packageName, userId);
91         ArrayMap<Boolean, ArrayList<ComponentName>> changes = new ArrayMap<>();
92         changes.put(true, new ArrayList<>(0));
93         changes.put(false, new ArrayList<>(0));
94         return changes;
95     }
96 
97     /**
98      *  @return true if the passed package is enabled. false otherwise
99      */
resetPackage(String packageName, int userId)100     boolean resetPackage(String packageName, int userId) {
101         boolean isAllowed = super.isPackageOrComponentAllowed(packageName, userId);
102         boolean isDefault = super.isDefaultComponentOrPackage(packageName);
103         if (!isAllowed && isDefault) {
104             setPackageOrComponentEnabled(packageName, userId, true, true);
105         }
106         if (isAllowed && !isDefault) {
107             setPackageOrComponentEnabled(packageName, userId, true, false);
108         }
109         return !isAllowed && isDefault;
110     }
111 
112     @Override
writeDefaults(XmlSerializer out)113     void writeDefaults(XmlSerializer out) throws IOException {
114         synchronized (mDefaultsLock) {
115             String defaults = String.join(ENABLED_SERVICES_SEPARATOR, mDefaultPackages);
116             out.attribute(null, ATT_DEFAULTS, defaults);
117         }
118     }
119 
120     @Override
getConfig()121     protected Config getConfig() {
122         final Config c = new Config();
123         c.caption = "condition provider";
124         c.serviceInterface = ConditionProviderService.SERVICE_INTERFACE;
125         c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES;
126         c.xmlTag = TAG_ENABLED_DND_APPS;
127         c.secondarySettingName = Settings.Secure.ENABLED_NOTIFICATION_LISTENERS;
128         c.bindPermission = android.Manifest.permission.BIND_CONDITION_PROVIDER_SERVICE;
129         c.settingsAction = Settings.ACTION_CONDITION_PROVIDER_SETTINGS;
130         c.clientLabel = R.string.condition_provider_service_binding_label;
131         return c;
132     }
133 
134     @Override
dump(PrintWriter pw, DumpFilter filter)135     public void dump(PrintWriter pw, DumpFilter filter) {
136         super.dump(pw, filter);
137         synchronized(mMutex) {
138             pw.print("    mRecords("); pw.print(mRecords.size()); pw.println("):");
139             for (int i = 0; i < mRecords.size(); i++) {
140                 final ConditionRecord r = mRecords.get(i);
141                 if (filter != null && !filter.matches(r.component)) continue;
142                 pw.print("      "); pw.println(r);
143                 final String countdownDesc =  CountdownConditionProvider.tryParseDescription(r.id);
144                 if (countdownDesc != null) {
145                     pw.print("        ("); pw.print(countdownDesc); pw.println(")");
146                 }
147             }
148         }
149         pw.print("    mSystemConditionProviders: "); pw.println(mSystemConditionProviderNames);
150         for (int i = 0; i < mSystemConditionProviders.size(); i++) {
151             mSystemConditionProviders.valueAt(i).dump(pw, filter);
152         }
153     }
154 
155     @Override
asInterface(IBinder binder)156     protected IInterface asInterface(IBinder binder) {
157         return IConditionProvider.Stub.asInterface(binder);
158     }
159 
160     @Override
checkType(IInterface service)161     protected boolean checkType(IInterface service) {
162         return service instanceof IConditionProvider;
163     }
164 
165     @Override
onBootPhaseAppsCanStart()166     public void onBootPhaseAppsCanStart() {
167         super.onBootPhaseAppsCanStart();
168         for (int i = 0; i < mSystemConditionProviders.size(); i++) {
169             mSystemConditionProviders.valueAt(i).onBootComplete();
170         }
171         if (mCallback != null) {
172             mCallback.onBootComplete();
173         }
174     }
175 
176     @Override
onUserSwitched(int user)177     public void onUserSwitched(int user) {
178         super.onUserSwitched(user);
179         if (mCallback != null) {
180             mCallback.onUserSwitched();
181         }
182     }
183 
184     @Override
onServiceAdded(ManagedServiceInfo info)185     protected void onServiceAdded(ManagedServiceInfo info) {
186         final IConditionProvider provider = provider(info);
187         try {
188             provider.onConnected();
189         } catch (RemoteException e) {
190             Slog.e(TAG, "can't connect to service " + info, e);
191             // we tried
192         }
193         if (mCallback != null) {
194             mCallback.onServiceAdded(info.component);
195         }
196     }
197 
198     @Override
loadDefaultsFromConfig()199     protected void loadDefaultsFromConfig() {
200         String defaultDndAccess = mContext.getResources().getString(
201                 R.string.config_defaultDndAccessPackages);
202         if (defaultDndAccess != null) {
203             String[] dnds = defaultDndAccess.split(ManagedServices.ENABLED_SERVICES_SEPARATOR);
204             for (int i = 0; i < dnds.length; i++) {
205                 if (TextUtils.isEmpty(dnds[i])) {
206                     continue;
207                 }
208                 addDefaultComponentOrPackage(dnds[i]);
209             }
210         }
211     }
212 
213     @Override
onServiceRemovedLocked(ManagedServiceInfo removed)214     protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
215         if (removed == null) return;
216         for (int i = mRecords.size() - 1; i >= 0; i--) {
217             final ConditionRecord r = mRecords.get(i);
218             if (!r.component.equals(removed.component)) continue;
219             mRecords.remove(i);
220         }
221     }
222 
223     @Override
onPackagesChanged(boolean removingPackage, String[] pkgList, int[] uid)224     public void onPackagesChanged(boolean removingPackage, String[] pkgList, int[] uid) {
225         if (removingPackage) {
226             INotificationManager inm = NotificationManager.getService();
227 
228             if (pkgList != null && (pkgList.length > 0)) {
229                 for (String pkgName : pkgList) {
230                     try {
231                         inm.removeAutomaticZenRules(pkgName);
232                         inm.setNotificationPolicyAccessGranted(pkgName, false);
233                     } catch (Exception e) {
234                         Slog.e(TAG, "Failed to clean up rules for " + pkgName, e);
235                     }
236                 }
237             }
238         }
239         super.onPackagesChanged(removingPackage, pkgList, uid);
240     }
241 
242     @Override
isValidEntry(String packageOrComponent, int userId)243     protected boolean isValidEntry(String packageOrComponent, int userId) {
244         return true;
245     }
246 
247     @Override
getRequiredPermission()248     protected String getRequiredPermission() {
249         return null;
250     }
251 
checkServiceToken(IConditionProvider provider)252     public ManagedServiceInfo checkServiceToken(IConditionProvider provider) {
253         synchronized(mMutex) {
254             return checkServiceTokenLocked(provider);
255         }
256     }
257 
removeDuplicateConditions(String pkg, Condition[] conditions)258     private Condition[] removeDuplicateConditions(String pkg, Condition[] conditions) {
259         if (conditions == null || conditions.length == 0) return null;
260         final int N = conditions.length;
261         final ArrayMap<Uri, Condition> valid = new ArrayMap<Uri, Condition>(N);
262         for (int i = 0; i < N; i++) {
263             final Uri id = conditions[i].id;
264             if (valid.containsKey(id)) {
265                 Slog.w(TAG, "Ignoring condition from " + pkg + " for duplicate id: " + id);
266                 continue;
267             }
268             valid.put(id, conditions[i]);
269         }
270         if (valid.size() == 0) return null;
271         if (valid.size() == N) return conditions;
272         final Condition[] rt = new Condition[valid.size()];
273         for (int i = 0; i < rt.length; i++) {
274             rt[i] = valid.valueAt(i);
275         }
276         return rt;
277     }
278 
getRecordLocked(Uri id, ComponentName component, boolean create)279     private ConditionRecord getRecordLocked(Uri id, ComponentName component, boolean create) {
280         if (id == null || component == null) return null;
281         final int N = mRecords.size();
282         for (int i = 0; i < N; i++) {
283             final ConditionRecord r = mRecords.get(i);
284             if (r.id.equals(id) && r.component.equals(component)) {
285                 return r;
286             }
287         }
288         if (create) {
289             final ConditionRecord r = new ConditionRecord(id, component);
290             mRecords.add(r);
291             return r;
292         }
293         return null;
294     }
295 
notifyConditions(String pkg, ManagedServiceInfo info, Condition[] conditions)296     public void notifyConditions(String pkg, ManagedServiceInfo info, Condition[] conditions) {
297         synchronized(mMutex) {
298             if (DEBUG) Slog.d(TAG, "notifyConditions pkg=" + pkg + " info=" + info + " conditions="
299                     + (conditions == null ? null : Arrays.asList(conditions)));
300             conditions = removeDuplicateConditions(pkg, conditions);
301             if (conditions == null || conditions.length == 0) return;
302             final int N = conditions.length;
303             for (int i = 0; i < N; i++) {
304                 final Condition c = conditions[i];
305                 final ConditionRecord r = getRecordLocked(c.id, info.component, true /*create*/);
306                 r.info = info;
307                 r.condition = c;
308             }
309         }
310         final int N = conditions.length;
311         for (int i = 0; i < N; i++) {
312             final Condition c = conditions[i];
313             if (mCallback != null) {
314                 mCallback.onConditionChanged(c.id, c);
315             }
316         }
317     }
318 
findConditionProvider(ComponentName component)319     public IConditionProvider findConditionProvider(ComponentName component) {
320         if (component == null) return null;
321         for (ManagedServiceInfo service : getServices()) {
322             if (component.equals(service.component)) {
323                 return provider(service);
324             }
325         }
326         return null;
327     }
328 
findCondition(ComponentName component, Uri conditionId)329     public Condition findCondition(ComponentName component, Uri conditionId) {
330         if (component == null || conditionId == null) return null;
331         synchronized (mMutex) {
332             final ConditionRecord r = getRecordLocked(conditionId, component, false /*create*/);
333             return r != null ? r.condition : null;
334         }
335     }
336 
ensureRecordExists(ComponentName component, Uri conditionId, IConditionProvider provider)337     public void ensureRecordExists(ComponentName component, Uri conditionId,
338             IConditionProvider provider) {
339         synchronized (mMutex) {
340             // constructed by convention, make sure the record exists...
341             final ConditionRecord r = getRecordLocked(conditionId, component, true /*create*/);
342             if (r.info == null) {
343                 // ... and is associated with the in-process service
344                 r.info = checkServiceTokenLocked(provider);
345             }
346         }
347     }
348 
subscribeIfNecessary(ComponentName component, Uri conditionId)349     public boolean subscribeIfNecessary(ComponentName component, Uri conditionId) {
350         synchronized (mMutex) {
351             final ConditionRecord r = getRecordLocked(conditionId, component, false /*create*/);
352             if (r == null) {
353                 Slog.w(TAG, "Unable to subscribe to " + component + " " + conditionId);
354                 return false;
355             }
356             if (r.subscribed) return true;
357             subscribeLocked(r);
358             return r.subscribed;
359         }
360     }
361 
unsubscribeIfNecessary(ComponentName component, Uri conditionId)362     public void unsubscribeIfNecessary(ComponentName component, Uri conditionId) {
363         synchronized (mMutex) {
364             final ConditionRecord r = getRecordLocked(conditionId, component, false /*create*/);
365             if (r == null) {
366                 Slog.w(TAG, "Unable to unsubscribe to " + component + " " + conditionId);
367                 return;
368             }
369             if (!r.subscribed) return;
370             unsubscribeLocked(r);;
371         }
372     }
373 
subscribeLocked(ConditionRecord r)374     private void subscribeLocked(ConditionRecord r) {
375         if (DEBUG) Slog.d(TAG, "subscribeLocked " + r);
376         final IConditionProvider provider = provider(r);
377         RemoteException re = null;
378         if (provider != null) {
379             try {
380                 Slog.d(TAG, "Subscribing to " + r.id + " with " + r.component);
381                 provider.onSubscribe(r.id);
382                 r.subscribed = true;
383             } catch (RemoteException e) {
384                 Slog.w(TAG, "Error subscribing to " + r, e);
385                 re = e;
386             }
387         }
388         ZenLog.traceSubscribe(r != null ? r.id : null, provider, re);
389     }
390 
391     @SafeVarargs
safeSet(T... items)392     private static <T> ArraySet<T> safeSet(T... items) {
393         final ArraySet<T> rt = new ArraySet<T>();
394         if (items == null || items.length == 0) return rt;
395         final int N = items.length;
396         for (int i = 0; i < N; i++) {
397             final T item = items[i];
398             if (item != null) {
399                 rt.add(item);
400             }
401         }
402         return rt;
403     }
404 
unsubscribeLocked(ConditionRecord r)405     private void unsubscribeLocked(ConditionRecord r) {
406         if (DEBUG) Slog.d(TAG, "unsubscribeLocked " + r);
407         final IConditionProvider provider = provider(r);
408         RemoteException re = null;
409         if (provider != null) {
410             try {
411                 provider.onUnsubscribe(r.id);
412             } catch (RemoteException e) {
413                 Slog.w(TAG, "Error unsubscribing to " + r, e);
414                 re = e;
415             }
416             r.subscribed = false;
417         }
418         ZenLog.traceUnsubscribe(r != null ? r.id : null, provider, re);
419     }
420 
provider(ConditionRecord r)421     private static IConditionProvider provider(ConditionRecord r) {
422         return r == null ? null : provider(r.info);
423     }
424 
provider(ManagedServiceInfo info)425     private static IConditionProvider provider(ManagedServiceInfo info) {
426         return info == null ? null : (IConditionProvider) info.service;
427     }
428 
429     private static class ConditionRecord {
430         public final Uri id;
431         public final ComponentName component;
432         public Condition condition;
433         public ManagedServiceInfo info;
434         public boolean subscribed;
435 
ConditionRecord(Uri id, ComponentName component)436         private ConditionRecord(Uri id, ComponentName component) {
437             this.id = id;
438             this.component = component;
439         }
440 
441         @Override
toString()442         public String toString() {
443             final StringBuilder sb = new StringBuilder("ConditionRecord[id=")
444                     .append(id).append(",component=").append(component)
445                     .append(",subscribed=").append(subscribed);
446             return sb.append(']').toString();
447         }
448     }
449 
450     public interface Callback {
onBootComplete()451         void onBootComplete();
onServiceAdded(ComponentName component)452         void onServiceAdded(ComponentName component);
onConditionChanged(Uri id, Condition condition)453         void onConditionChanged(Uri id, Condition condition);
onUserSwitched()454         void onUserSwitched();
455     }
456 
457 }
458