Home | History | Annotate | Download | only in notification
      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.annotation.NonNull;
     20 import android.app.INotificationManager;
     21 import android.app.NotificationManager;
     22 import android.content.ComponentName;
     23 import android.content.ContentResolver;
     24 import android.content.Context;
     25 import android.net.Uri;
     26 import android.os.Handler;
     27 import android.os.IBinder;
     28 import android.os.IInterface;
     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.server.notification.NotificationManagerService.DumpFilter;
     42 
     43 import java.io.PrintWriter;
     44 import java.util.ArrayList;
     45 import java.util.Arrays;
     46 
     47 public class ConditionProviders extends ManagedServices {
     48     private final ArrayList<ConditionRecord> mRecords = new ArrayList<>();
     49     private final ArraySet<String> mSystemConditionProviderNames;
     50     private final ArraySet<SystemConditionProviderService> mSystemConditionProviders
     51             = new ArraySet<>();
     52 
     53     private Callback mCallback;
     54 
     55     public ConditionProviders(Context context, Handler handler, UserProfiles userProfiles) {
     56         super(context, handler, new Object(), userProfiles);
     57         mSystemConditionProviderNames = safeSet(PropConfig.getStringArray(mContext,
     58                 "system.condition.providers",
     59                 R.array.config_system_condition_providers));
     60     }
     61 
     62     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);
     73         registerService(service.asInterface(), service.getComponent(), UserHandle.USER_SYSTEM);
     74     }
     75 
     76     public Iterable<SystemConditionProviderService> getSystemProviders() {
     77         return mSystemConditionProviders;
     78     }
     79 
     80     @Override
     81     protected Config getConfig() {
     82         final Config c = new Config();
     83         c.caption = "condition provider";
     84         c.serviceInterface = ConditionProviderService.SERVICE_INTERFACE;
     85         c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES;
     86         c.secondarySettingName = Settings.Secure.ENABLED_NOTIFICATION_LISTENERS;
     87         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
     94     public void dump(PrintWriter pw, DumpFilter filter) {
     95         super.dump(pw, filter);
     96         synchronized(mMutex) {
     97             pw.print("    mRecords("); pw.print(mRecords.size()); pw.println("):");
     98             for (int i = 0; i < mRecords.size(); i++) {
     99                 final ConditionRecord r = mRecords.get(i);
    100                 if (filter != null && !filter.matches(r.component)) continue;
    101                 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                 }
    106             }
    107         }
    108         pw.print("    mSystemConditionProviders: "); pw.println(mSystemConditionProviderNames);
    109         for (int i = 0; i < mSystemConditionProviders.size(); i++) {
    110             mSystemConditionProviders.valueAt(i).dump(pw, filter);
    111         }
    112     }
    113 
    114     @Override
    115     protected IInterface asInterface(IBinder binder) {
    116         return IConditionProvider.Stub.asInterface(binder);
    117     }
    118 
    119     @Override
    120     protected boolean checkType(IInterface service) {
    121         return service instanceof IConditionProvider;
    122     }
    123 
    124     @Override
    125     public void onBootPhaseAppsCanStart() {
    126         super.onBootPhaseAppsCanStart();
    127         for (int i = 0; i < mSystemConditionProviders.size(); i++) {
    128             mSystemConditionProviders.valueAt(i).onBootComplete();
    129         }
    130         if (mCallback != null) {
    131             mCallback.onBootComplete();
    132         }
    133     }
    134 
    135     @Override
    136     public void onUserSwitched(int user) {
    137         super.onUserSwitched(user);
    138         if (mCallback != null) {
    139             mCallback.onUserSwitched();
    140         }
    141     }
    142 
    143     @Override
    144     protected void onServiceAdded(ManagedServiceInfo info) {
    145         final IConditionProvider provider = provider(info);
    146         try {
    147             provider.onConnected();
    148         } catch (RemoteException e) {
    149             // we tried
    150         }
    151         if (mCallback != null) {
    152             mCallback.onServiceAdded(info.component);
    153         }
    154     }
    155 
    156     @Override
    157     protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
    158         if (removed == null) return;
    159         for (int i = mRecords.size() - 1; i >= 0; i--) {
    160             final ConditionRecord r = mRecords.get(i);
    161             if (!r.component.equals(removed.component)) continue;
    162             mRecords.remove(i);
    163         }
    164     }
    165 
    166     @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 
    185     public ManagedServiceInfo checkServiceToken(IConditionProvider provider) {
    186         synchronized(mMutex) {
    187             return checkServiceTokenLocked(provider);
    188         }
    189     }
    190 
    191     private Condition[] removeDuplicateConditions(String pkg, Condition[] conditions) {
    192         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;
    197             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 
    212     private ConditionRecord getRecordLocked(Uri id, ComponentName component, boolean create) {
    213         if (id == null || component == null) return null;
    214         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         }
    221         if (create) {
    222             final ConditionRecord r = new ConditionRecord(id, component);
    223             mRecords.add(r);
    224             return r;
    225         }
    226         return null;
    227     }
    228 
    229     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)));
    233             conditions = removeDuplicateConditions(pkg, conditions);
    234             if (conditions == null || conditions.length == 0) return;
    235             final int N = conditions.length;
    236             for (int i = 0; i < N; i++) {
    237                 final Condition c = conditions[i];
    238                 final ConditionRecord r = getRecordLocked(c.id, info.component, true /*create*/);
    239                 r.info = info;
    240                 r.condition = c;
    241             }
    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);
    248             }
    249         }
    250     }
    251 
    252     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 
    262     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 
    270     public void ensureRecordExists(ComponentName component, Uri conditionId,
    271             IConditionProvider provider) {
    272         // constructed by convention, make sure the record exists...
    273         final ConditionRecord r = getRecordLocked(conditionId, component, true /*create*/);
    274         if (r.info == null) {
    275             // ... and is associated with the in-process service
    276             r.info = checkServiceTokenLocked(provider);
    277         }
    278     }
    279 
    280     @Override
    281     protected @NonNull ArraySet<ComponentName> loadComponentNamesFromSetting(String settingName,
    282             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))
    289             return new ArraySet<>();
    290         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])) {
    294                 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                 }
    300             }
    301         }
    302         return result;
    303     }
    304 
    305     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;
    311             }
    312             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;
    324             }
    325             if (!r.subscribed) return;
    326             unsubscribeLocked(r);;
    327         }
    328     }
    329 
    330     private void subscribeLocked(ConditionRecord r) {
    331         if (DEBUG) Slog.d(TAG, "subscribeLocked " + r);
    332         final IConditionProvider provider = provider(r);
    333         RemoteException re = null;
    334         if (provider != null) {
    335             try {
    336                 Slog.d(TAG, "Subscribing to " + r.id + " with " + r.component);
    337                 provider.onSubscribe(r.id);
    338                 r.subscribed = true;
    339             } catch (RemoteException e) {
    340                 Slog.w(TAG, "Error subscribing to " + r, e);
    341                 re = e;
    342             }
    343         }
    344         ZenLog.traceSubscribe(r != null ? r.id : null, provider, re);
    345     }
    346 
    347     @SafeVarargs
    348     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 
    361     private void unsubscribeLocked(ConditionRecord r) {
    362         if (DEBUG) Slog.d(TAG, "unsubscribeLocked " + r);
    363         final IConditionProvider provider = provider(r);
    364         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             }
    372             r.subscribed = false;
    373         }
    374         ZenLog.traceUnsubscribe(r != null ? r.id : null, provider, re);
    375     }
    376 
    377     private static IConditionProvider provider(ConditionRecord r) {
    378         return r == null ? null : provider(r.info);
    379     }
    380 
    381     private static IConditionProvider provider(ManagedServiceInfo info) {
    382         return info == null ? null : (IConditionProvider) info.service;
    383     }
    384 
    385     private static class ConditionRecord {
    386         public final Uri id;
    387         public final ComponentName component;
    388         public Condition condition;
    389         public ManagedServiceInfo info;
    390         public boolean subscribed;
    391 
    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=")
    400                     .append(id).append(",component=").append(component)
    401                     .append(",subscribed=").append(subscribed);
    402             return sb.append(']').toString();
    403         }
    404     }
    405 
    406     public interface Callback {
    407         void onBootComplete();
    408         void onServiceAdded(ComponentName component);
    409         void onConditionChanged(Uri id, Condition condition);
    410         void onUserSwitched();
    411     }
    412 
    413 }
    414