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