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.app.ActivityManager;
     20 import android.app.PendingIntent;
     21 import android.content.BroadcastReceiver;
     22 import android.content.ComponentName;
     23 import android.content.ContentResolver;
     24 import android.content.Context;
     25 import android.content.Intent;
     26 import android.content.IntentFilter;
     27 import android.content.ServiceConnection;
     28 import android.content.pm.ApplicationInfo;
     29 import android.content.pm.PackageManager;
     30 import android.content.pm.PackageManager.NameNotFoundException;
     31 import android.content.pm.ResolveInfo;
     32 import android.content.pm.ServiceInfo;
     33 import android.content.pm.UserInfo;
     34 import android.database.ContentObserver;
     35 import android.net.Uri;
     36 import android.os.Build;
     37 import android.os.Handler;
     38 import android.os.IBinder;
     39 import android.os.IInterface;
     40 import android.os.RemoteException;
     41 import android.os.UserHandle;
     42 import android.os.UserManager;
     43 import android.provider.Settings;
     44 import android.text.TextUtils;
     45 import android.util.ArraySet;
     46 import android.util.Log;
     47 import android.util.Slog;
     48 import android.util.SparseArray;
     49 
     50 import com.android.server.notification.NotificationManagerService.DumpFilter;
     51 
     52 import java.io.PrintWriter;
     53 import java.util.ArrayList;
     54 import java.util.Arrays;
     55 import java.util.List;
     56 import java.util.Objects;
     57 import java.util.Set;
     58 
     59 /**
     60  * Manages the lifecycle of application-provided services bound by system server.
     61  *
     62  * Services managed by this helper must have:
     63  *  - An associated system settings value with a list of enabled component names.
     64  *  - A well-known action for services to use in their intent-filter.
     65  *  - A system permission for services to require in order to ensure system has exclusive binding.
     66  *  - A settings page for user configuration of enabled services, and associated intent action.
     67  *  - A remote interface definition (aidl) provided by the service used for communication.
     68  */
     69 abstract public class ManagedServices {
     70     protected final String TAG = getClass().getSimpleName();
     71     protected final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
     72 
     73     private static final String ENABLED_SERVICES_SEPARATOR = ":";
     74 
     75     protected final Context mContext;
     76     protected final Object mMutex;
     77     private final UserProfiles mUserProfiles;
     78     private final SettingsObserver mSettingsObserver;
     79     private final Config mConfig;
     80     private ArraySet<String> mRestored;
     81 
     82     // contains connections to all connected services, including app services
     83     // and system services
     84     protected final ArrayList<ManagedServiceInfo> mServices = new ArrayList<ManagedServiceInfo>();
     85     // things that will be put into mServices as soon as they're ready
     86     private final ArrayList<String> mServicesBinding = new ArrayList<String>();
     87     // lists the component names of all enabled (and therefore connected)
     88     // app services for current profiles.
     89     private ArraySet<ComponentName> mEnabledServicesForCurrentProfiles
     90             = new ArraySet<ComponentName>();
     91     // Just the packages from mEnabledServicesForCurrentProfiles
     92     private ArraySet<String> mEnabledServicesPackageNames = new ArraySet<String>();
     93 
     94     // Kept to de-dupe user change events (experienced after boot, when we receive a settings and a
     95     // user change).
     96     private int[] mLastSeenProfileIds;
     97 
     98     private final BroadcastReceiver mRestoreReceiver;
     99 
    100     public ManagedServices(Context context, Handler handler, Object mutex,
    101             UserProfiles userProfiles) {
    102         mContext = context;
    103         mMutex = mutex;
    104         mUserProfiles = userProfiles;
    105         mConfig = getConfig();
    106         mSettingsObserver = new SettingsObserver(handler);
    107 
    108         mRestoreReceiver = new SettingRestoredReceiver();
    109         IntentFilter filter = new IntentFilter(Intent.ACTION_SETTING_RESTORED);
    110         context.registerReceiver(mRestoreReceiver, filter);
    111     }
    112 
    113     class SettingRestoredReceiver extends BroadcastReceiver {
    114         @Override
    115         public void onReceive(Context context, Intent intent) {
    116             if (Intent.ACTION_SETTING_RESTORED.equals(intent.getAction())) {
    117                 String element = intent.getStringExtra(Intent.EXTRA_SETTING_NAME);
    118                 if (Objects.equals(element, mConfig.secureSettingName)) {
    119                     String prevValue = intent.getStringExtra(Intent.EXTRA_SETTING_PREVIOUS_VALUE);
    120                     String newValue = intent.getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE);
    121                     settingRestored(element, prevValue, newValue, getSendingUserId());
    122                 }
    123             }
    124         }
    125     }
    126 
    127     abstract protected Config getConfig();
    128 
    129     private String getCaption() {
    130         return mConfig.caption;
    131     }
    132 
    133     abstract protected IInterface asInterface(IBinder binder);
    134 
    135     abstract protected void onServiceAdded(ManagedServiceInfo info);
    136 
    137     protected void onServiceRemovedLocked(ManagedServiceInfo removed) { }
    138 
    139     private ManagedServiceInfo newServiceInfo(IInterface service,
    140             ComponentName component, int userid, boolean isSystem, ServiceConnection connection,
    141             int targetSdkVersion) {
    142         return new ManagedServiceInfo(service, component, userid, isSystem, connection,
    143                 targetSdkVersion);
    144     }
    145 
    146     public void onBootPhaseAppsCanStart() {
    147         mSettingsObserver.observe();
    148     }
    149 
    150     public void dump(PrintWriter pw, DumpFilter filter) {
    151         pw.println("    All " + getCaption() + "s (" + mEnabledServicesForCurrentProfiles.size()
    152                 + ") enabled for current profiles:");
    153         for (ComponentName cmpt : mEnabledServicesForCurrentProfiles) {
    154             if (filter != null && !filter.matches(cmpt)) continue;
    155             pw.println("      " + cmpt);
    156         }
    157 
    158         pw.println("    Live " + getCaption() + "s (" + mServices.size() + "):");
    159         for (ManagedServiceInfo info : mServices) {
    160             if (filter != null && !filter.matches(info.component)) continue;
    161             pw.println("      " + info.component
    162                     + " (user " + info.userid + "): " + info.service
    163                     + (info.isSystem?" SYSTEM":""));
    164         }
    165     }
    166 
    167     // By convention, restored settings are replicated to another settings
    168     // entry, named similarly but with a disambiguation suffix.
    169     public static final String restoredSettingName(Config config) {
    170         return config.secureSettingName + ":restored";
    171     }
    172 
    173     // The OS has done a restore of this service's saved state.  We clone it to the
    174     // 'restored' reserve, and then once we return and the actual write to settings is
    175     // performed, our observer will do the work of maintaining the restored vs live
    176     // settings data.
    177     public void settingRestored(String element, String oldValue, String newValue, int userid) {
    178         if (DEBUG) Slog.d(TAG, "Restored managed service setting: " + element
    179                 + " ovalue=" + oldValue + " nvalue=" + newValue);
    180         if (mConfig.secureSettingName.equals(element)) {
    181             if (element != null) {
    182                 mRestored = null;
    183                 Settings.Secure.putStringForUser(mContext.getContentResolver(),
    184                         restoredSettingName(mConfig),
    185                         newValue,
    186                         userid);
    187                 disableNonexistentServices(userid);
    188             }
    189         }
    190     }
    191 
    192     public boolean isComponentEnabledForPackage(String pkg) {
    193         return mEnabledServicesPackageNames.contains(pkg);
    194     }
    195 
    196     public void onPackagesChanged(boolean queryReplace, String[] pkgList) {
    197         if (DEBUG) Slog.d(TAG, "onPackagesChanged queryReplace=" + queryReplace
    198                 + " pkgList=" + (pkgList == null ? null : Arrays.asList(pkgList))
    199                 + " mEnabledServicesPackageNames=" + mEnabledServicesPackageNames);
    200         boolean anyServicesInvolved = false;
    201         if (pkgList != null && (pkgList.length > 0)) {
    202             for (String pkgName : pkgList) {
    203                 if (mEnabledServicesPackageNames.contains(pkgName)) {
    204                     anyServicesInvolved = true;
    205                 }
    206             }
    207         }
    208 
    209         if (anyServicesInvolved) {
    210             // if we're not replacing a package, clean up orphaned bits
    211             if (!queryReplace) {
    212                 disableNonexistentServices();
    213             }
    214             // make sure we're still bound to any of our services who may have just upgraded
    215             rebindServices();
    216         }
    217     }
    218 
    219     public void onUserSwitched(int user) {
    220         if (DEBUG) Slog.d(TAG, "onUserSwitched u=" + user);
    221         if (Arrays.equals(mLastSeenProfileIds, mUserProfiles.getCurrentProfileIds())) {
    222             if (DEBUG) Slog.d(TAG, "Current profile IDs didn't change, skipping rebindServices().");
    223             return;
    224         }
    225         rebindServices();
    226     }
    227 
    228     public ManagedServiceInfo checkServiceTokenLocked(IInterface service) {
    229         checkNotNull(service);
    230         final IBinder token = service.asBinder();
    231         final int N = mServices.size();
    232         for (int i=0; i<N; i++) {
    233             final ManagedServiceInfo info = mServices.get(i);
    234             if (info.service.asBinder() == token) return info;
    235         }
    236         throw new SecurityException("Disallowed call from unknown " + getCaption() + ": "
    237                 + service);
    238     }
    239 
    240     public void unregisterService(IInterface service, int userid) {
    241         checkNotNull(service);
    242         // no need to check permissions; if your service binder is in the list,
    243         // that's proof that you had permission to add it in the first place
    244         unregisterServiceImpl(service, userid);
    245     }
    246 
    247     public void registerService(IInterface service, ComponentName component, int userid) {
    248         checkNotNull(service);
    249         ManagedServiceInfo info = registerServiceImpl(service, component, userid);
    250         if (info != null) {
    251             onServiceAdded(info);
    252         }
    253     }
    254 
    255     /**
    256      * Remove access for any services that no longer exist.
    257      */
    258     private void disableNonexistentServices() {
    259         int[] userIds = mUserProfiles.getCurrentProfileIds();
    260         final int N = userIds.length;
    261         for (int i = 0 ; i < N; ++i) {
    262             disableNonexistentServices(userIds[i]);
    263         }
    264     }
    265 
    266     private void disableNonexistentServices(int userId) {
    267         final ContentResolver cr = mContext.getContentResolver();
    268         boolean restoredChanged = false;
    269         if (mRestored == null) {
    270             String restoredSetting = Settings.Secure.getStringForUser(
    271                     cr,
    272                     restoredSettingName(mConfig),
    273                     userId);
    274             if (!TextUtils.isEmpty(restoredSetting)) {
    275                 if (DEBUG) Slog.d(TAG, "restored: " + restoredSetting);
    276                 String[] restored = restoredSetting.split(ENABLED_SERVICES_SEPARATOR);
    277                 mRestored = new ArraySet<String>(Arrays.asList(restored));
    278             } else {
    279                 mRestored = new ArraySet<String>();
    280             }
    281         }
    282         String flatIn = Settings.Secure.getStringForUser(
    283                 cr,
    284                 mConfig.secureSettingName,
    285                 userId);
    286         if (!TextUtils.isEmpty(flatIn)) {
    287             if (DEBUG) Slog.v(TAG, "flat before: " + flatIn);
    288             PackageManager pm = mContext.getPackageManager();
    289             List<ResolveInfo> installedServices = pm.queryIntentServicesAsUser(
    290                     new Intent(mConfig.serviceInterface),
    291                     PackageManager.GET_SERVICES | PackageManager.GET_META_DATA,
    292                     userId);
    293             if (DEBUG) Slog.v(TAG, mConfig.serviceInterface + " services: " + installedServices);
    294             Set<ComponentName> installed = new ArraySet<ComponentName>();
    295             for (int i = 0, count = installedServices.size(); i < count; i++) {
    296                 ResolveInfo resolveInfo = installedServices.get(i);
    297                 ServiceInfo info = resolveInfo.serviceInfo;
    298 
    299                 ComponentName component = new ComponentName(info.packageName, info.name);
    300                 if (!mConfig.bindPermission.equals(info.permission)) {
    301                     Slog.w(TAG, "Skipping " + getCaption() + " service "
    302                             + info.packageName + "/" + info.name
    303                             + ": it does not require the permission "
    304                             + mConfig.bindPermission);
    305                     restoredChanged |= mRestored.remove(component.flattenToString());
    306                     continue;
    307                 }
    308                 installed.add(component);
    309             }
    310 
    311             String flatOut = "";
    312             if (!installed.isEmpty()) {
    313                 String[] enabled = flatIn.split(ENABLED_SERVICES_SEPARATOR);
    314                 ArrayList<String> remaining = new ArrayList<String>(enabled.length);
    315                 for (int i = 0; i < enabled.length; i++) {
    316                     ComponentName enabledComponent = ComponentName.unflattenFromString(enabled[i]);
    317                     if (installed.contains(enabledComponent)) {
    318                         remaining.add(enabled[i]);
    319                         restoredChanged |= mRestored.remove(enabled[i]);
    320                     }
    321                 }
    322                 remaining.addAll(mRestored);
    323                 flatOut = TextUtils.join(ENABLED_SERVICES_SEPARATOR, remaining);
    324             }
    325             if (DEBUG) Slog.v(TAG, "flat after: " + flatOut);
    326             if (!flatIn.equals(flatOut)) {
    327                 Settings.Secure.putStringForUser(cr,
    328                         mConfig.secureSettingName,
    329                         flatOut, userId);
    330             }
    331             if (restoredChanged) {
    332                 if (DEBUG) Slog.d(TAG, "restored changed; rewriting");
    333                 final String flatRestored = TextUtils.join(ENABLED_SERVICES_SEPARATOR,
    334                         mRestored.toArray());
    335                 Settings.Secure.putStringForUser(cr,
    336                         restoredSettingName(mConfig),
    337                         flatRestored,
    338                         userId);
    339             }
    340         }
    341     }
    342 
    343     /**
    344      * Called whenever packages change, the user switches, or the secure setting
    345      * is altered. (For example in response to USER_SWITCHED in our broadcast receiver)
    346      */
    347     private void rebindServices() {
    348         if (DEBUG) Slog.d(TAG, "rebindServices");
    349         final int[] userIds = mUserProfiles.getCurrentProfileIds();
    350         final int nUserIds = userIds.length;
    351 
    352         final SparseArray<String> flat = new SparseArray<String>();
    353 
    354         for (int i = 0; i < nUserIds; ++i) {
    355             flat.put(userIds[i], Settings.Secure.getStringForUser(
    356                     mContext.getContentResolver(),
    357                     mConfig.secureSettingName,
    358                     userIds[i]));
    359         }
    360 
    361         ArrayList<ManagedServiceInfo> toRemove = new ArrayList<ManagedServiceInfo>();
    362         final SparseArray<ArrayList<ComponentName>> toAdd
    363                 = new SparseArray<ArrayList<ComponentName>>();
    364 
    365         synchronized (mMutex) {
    366             // Unbind automatically bound services, retain system services.
    367             for (ManagedServiceInfo service : mServices) {
    368                 if (!service.isSystem) {
    369                     toRemove.add(service);
    370                 }
    371             }
    372 
    373             final ArraySet<ComponentName> newEnabled = new ArraySet<ComponentName>();
    374             final ArraySet<String> newPackages = new ArraySet<String>();
    375 
    376             for (int i = 0; i < nUserIds; ++i) {
    377                 final ArrayList<ComponentName> add = new ArrayList<ComponentName>();
    378                 toAdd.put(userIds[i], add);
    379 
    380                 // decode the list of components
    381                 String toDecode = flat.get(userIds[i]);
    382                 if (toDecode != null) {
    383                     String[] components = toDecode.split(ENABLED_SERVICES_SEPARATOR);
    384                     for (int j = 0; j < components.length; j++) {
    385                         final ComponentName component
    386                                 = ComponentName.unflattenFromString(components[j]);
    387                         if (component != null) {
    388                             newEnabled.add(component);
    389                             add.add(component);
    390                             newPackages.add(component.getPackageName());
    391                         }
    392                     }
    393 
    394                 }
    395             }
    396             mEnabledServicesForCurrentProfiles = newEnabled;
    397             mEnabledServicesPackageNames = newPackages;
    398         }
    399 
    400         for (ManagedServiceInfo info : toRemove) {
    401             final ComponentName component = info.component;
    402             final int oldUser = info.userid;
    403             Slog.v(TAG, "disabling " + getCaption() + " for user "
    404                     + oldUser + ": " + component);
    405             unregisterService(component, info.userid);
    406         }
    407 
    408         for (int i = 0; i < nUserIds; ++i) {
    409             final ArrayList<ComponentName> add = toAdd.get(userIds[i]);
    410             final int N = add.size();
    411             for (int j = 0; j < N; j++) {
    412                 final ComponentName component = add.get(j);
    413                 Slog.v(TAG, "enabling " + getCaption() + " for user " + userIds[i] + ": "
    414                         + component);
    415                 registerService(component, userIds[i]);
    416             }
    417         }
    418 
    419         mLastSeenProfileIds = mUserProfiles.getCurrentProfileIds();
    420     }
    421 
    422     /**
    423      * Version of registerService that takes the name of a service component to bind to.
    424      */
    425     private void registerService(final ComponentName name, final int userid) {
    426         if (DEBUG) Slog.v(TAG, "registerService: " + name + " u=" + userid);
    427 
    428         synchronized (mMutex) {
    429             final String servicesBindingTag = name.toString() + "/" + userid;
    430             if (mServicesBinding.contains(servicesBindingTag)) {
    431                 // stop registering this thing already! we're working on it
    432                 return;
    433             }
    434             mServicesBinding.add(servicesBindingTag);
    435 
    436             final int N = mServices.size();
    437             for (int i=N-1; i>=0; i--) {
    438                 final ManagedServiceInfo info = mServices.get(i);
    439                 if (name.equals(info.component)
    440                         && info.userid == userid) {
    441                     // cut old connections
    442                     if (DEBUG) Slog.v(TAG, "    disconnecting old " + getCaption() + ": "
    443                             + info.service);
    444                     removeServiceLocked(i);
    445                     if (info.connection != null) {
    446                         mContext.unbindService(info.connection);
    447                     }
    448                 }
    449             }
    450 
    451             Intent intent = new Intent(mConfig.serviceInterface);
    452             intent.setComponent(name);
    453 
    454             intent.putExtra(Intent.EXTRA_CLIENT_LABEL, mConfig.clientLabel);
    455 
    456             final PendingIntent pendingIntent = PendingIntent.getActivity(
    457                     mContext, 0, new Intent(mConfig.settingsAction), 0);
    458             intent.putExtra(Intent.EXTRA_CLIENT_INTENT, pendingIntent);
    459 
    460             ApplicationInfo appInfo = null;
    461             try {
    462                 appInfo = mContext.getPackageManager().getApplicationInfo(
    463                         name.getPackageName(), 0);
    464             } catch (NameNotFoundException e) {
    465                 // Ignore if the package doesn't exist we won't be able to bind to the service.
    466             }
    467             final int targetSdkVersion =
    468                     appInfo != null ? appInfo.targetSdkVersion : Build.VERSION_CODES.BASE;
    469 
    470             try {
    471                 if (DEBUG) Slog.v(TAG, "binding: " + intent);
    472                 if (!mContext.bindServiceAsUser(intent,
    473                         new ServiceConnection() {
    474                             IInterface mService;
    475 
    476                             @Override
    477                             public void onServiceConnected(ComponentName name, IBinder binder) {
    478                                 boolean added = false;
    479                                 ManagedServiceInfo info = null;
    480                                 synchronized (mMutex) {
    481                                     mServicesBinding.remove(servicesBindingTag);
    482                                     try {
    483                                         mService = asInterface(binder);
    484                                         info = newServiceInfo(mService, name,
    485                                                 userid, false /*isSystem*/, this, targetSdkVersion);
    486                                         binder.linkToDeath(info, 0);
    487                                         added = mServices.add(info);
    488                                     } catch (RemoteException e) {
    489                                         // already dead
    490                                     }
    491                                 }
    492                                 if (added) {
    493                                     onServiceAdded(info);
    494                                 }
    495                             }
    496 
    497                             @Override
    498                             public void onServiceDisconnected(ComponentName name) {
    499                                 Slog.v(TAG, getCaption() + " connection lost: " + name);
    500                             }
    501                         },
    502                         Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
    503                         new UserHandle(userid)))
    504                 {
    505                     mServicesBinding.remove(servicesBindingTag);
    506                     Slog.w(TAG, "Unable to bind " + getCaption() + " service: " + intent);
    507                     return;
    508                 }
    509             } catch (SecurityException ex) {
    510                 Slog.e(TAG, "Unable to bind " + getCaption() + " service: " + intent, ex);
    511                 return;
    512             }
    513         }
    514     }
    515 
    516     /**
    517      * Remove a service for the given user by ComponentName
    518      */
    519     private void unregisterService(ComponentName name, int userid) {
    520         synchronized (mMutex) {
    521             final int N = mServices.size();
    522             for (int i=N-1; i>=0; i--) {
    523                 final ManagedServiceInfo info = mServices.get(i);
    524                 if (name.equals(info.component)
    525                         && info.userid == userid) {
    526                     removeServiceLocked(i);
    527                     if (info.connection != null) {
    528                         try {
    529                             mContext.unbindService(info.connection);
    530                         } catch (IllegalArgumentException ex) {
    531                             // something happened to the service: we think we have a connection
    532                             // but it's bogus.
    533                             Slog.e(TAG, getCaption() + " " + name + " could not be unbound: " + ex);
    534                         }
    535                     }
    536                 }
    537             }
    538         }
    539     }
    540 
    541     /**
    542      * Removes a service from the list but does not unbind
    543      *
    544      * @return the removed service.
    545      */
    546     private ManagedServiceInfo removeServiceImpl(IInterface service, final int userid) {
    547         if (DEBUG) Slog.d(TAG, "removeServiceImpl service=" + service + " u=" + userid);
    548         ManagedServiceInfo serviceInfo = null;
    549         synchronized (mMutex) {
    550             final int N = mServices.size();
    551             for (int i=N-1; i>=0; i--) {
    552                 final ManagedServiceInfo info = mServices.get(i);
    553                 if (info.service.asBinder() == service.asBinder()
    554                         && info.userid == userid) {
    555                     if (DEBUG) Slog.d(TAG, "Removing active service " + info.component);
    556                     serviceInfo = removeServiceLocked(i);
    557                 }
    558             }
    559         }
    560         return serviceInfo;
    561     }
    562 
    563     private ManagedServiceInfo removeServiceLocked(int i) {
    564         final ManagedServiceInfo info = mServices.remove(i);
    565         onServiceRemovedLocked(info);
    566         return info;
    567     }
    568 
    569     private void checkNotNull(IInterface service) {
    570         if (service == null) {
    571             throw new IllegalArgumentException(getCaption() + " must not be null");
    572         }
    573     }
    574 
    575     private ManagedServiceInfo registerServiceImpl(final IInterface service,
    576             final ComponentName component, final int userid) {
    577         synchronized (mMutex) {
    578             try {
    579                 ManagedServiceInfo info = newServiceInfo(service, component, userid,
    580                         true /*isSystem*/, null, Build.VERSION_CODES.LOLLIPOP);
    581                 service.asBinder().linkToDeath(info, 0);
    582                 mServices.add(info);
    583                 return info;
    584             } catch (RemoteException e) {
    585                 // already dead
    586             }
    587         }
    588         return null;
    589     }
    590 
    591     /**
    592      * Removes a service from the list and unbinds.
    593      */
    594     private void unregisterServiceImpl(IInterface service, int userid) {
    595         ManagedServiceInfo info = removeServiceImpl(service, userid);
    596         if (info != null && info.connection != null) {
    597             mContext.unbindService(info.connection);
    598         }
    599     }
    600 
    601     private class SettingsObserver extends ContentObserver {
    602         private final Uri mSecureSettingsUri = Settings.Secure.getUriFor(mConfig.secureSettingName);
    603 
    604         private SettingsObserver(Handler handler) {
    605             super(handler);
    606         }
    607 
    608         private void observe() {
    609             ContentResolver resolver = mContext.getContentResolver();
    610             resolver.registerContentObserver(mSecureSettingsUri,
    611                     false, this, UserHandle.USER_ALL);
    612             update(null);
    613         }
    614 
    615         @Override
    616         public void onChange(boolean selfChange, Uri uri) {
    617             update(uri);
    618         }
    619 
    620         private void update(Uri uri) {
    621             if (uri == null || mSecureSettingsUri.equals(uri)) {
    622                 if (DEBUG) Slog.d(TAG, "Setting changed: mSecureSettingsUri=" + mSecureSettingsUri +
    623                         " / uri=" + uri);
    624                 rebindServices();
    625             }
    626         }
    627     }
    628 
    629     public class ManagedServiceInfo implements IBinder.DeathRecipient {
    630         public IInterface service;
    631         public ComponentName component;
    632         public int userid;
    633         public boolean isSystem;
    634         public ServiceConnection connection;
    635         public int targetSdkVersion;
    636 
    637         public ManagedServiceInfo(IInterface service, ComponentName component,
    638                 int userid, boolean isSystem, ServiceConnection connection, int targetSdkVersion) {
    639             this.service = service;
    640             this.component = component;
    641             this.userid = userid;
    642             this.isSystem = isSystem;
    643             this.connection = connection;
    644             this.targetSdkVersion = targetSdkVersion;
    645         }
    646 
    647         @Override
    648         public String toString() {
    649             return new StringBuilder("ManagedServiceInfo[")
    650                     .append("component=").append(component)
    651                     .append(",userid=").append(userid)
    652                     .append(",isSystem=").append(isSystem)
    653                     .append(",targetSdkVersion=").append(targetSdkVersion)
    654                     .append(",connection=").append(connection == null ? null : "<connection>")
    655                     .append(",service=").append(service)
    656                     .append(']').toString();
    657         }
    658 
    659         public boolean enabledAndUserMatches(int nid) {
    660             if (!isEnabledForCurrentProfiles()) {
    661                 return false;
    662             }
    663             if (this.userid == UserHandle.USER_ALL) return true;
    664             if (nid == UserHandle.USER_ALL || nid == this.userid) return true;
    665             return supportsProfiles() && mUserProfiles.isCurrentProfile(nid);
    666         }
    667 
    668         public boolean supportsProfiles() {
    669             return targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP;
    670         }
    671 
    672         @Override
    673         public void binderDied() {
    674             if (DEBUG) Slog.d(TAG, "binderDied");
    675             // Remove the service, but don't unbind from the service. The system will bring the
    676             // service back up, and the onServiceConnected handler will readd the service with the
    677             // new binding. If this isn't a bound service, and is just a registered
    678             // service, just removing it from the list is all we need to do anyway.
    679             removeServiceImpl(this.service, this.userid);
    680         }
    681 
    682         /** convenience method for looking in mEnabledServicesForCurrentProfiles */
    683         public boolean isEnabledForCurrentProfiles() {
    684             if (this.isSystem) return true;
    685             if (this.connection == null) return false;
    686             return mEnabledServicesForCurrentProfiles.contains(this.component);
    687         }
    688     }
    689 
    690     public static class UserProfiles {
    691         // Profiles of the current user.
    692         private final SparseArray<UserInfo> mCurrentProfiles = new SparseArray<UserInfo>();
    693 
    694         public void updateCache(Context context) {
    695             UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
    696             if (userManager != null) {
    697                 int currentUserId = ActivityManager.getCurrentUser();
    698                 List<UserInfo> profiles = userManager.getProfiles(currentUserId);
    699                 synchronized (mCurrentProfiles) {
    700                     mCurrentProfiles.clear();
    701                     for (UserInfo user : profiles) {
    702                         mCurrentProfiles.put(user.id, user);
    703                     }
    704                 }
    705             }
    706         }
    707 
    708         public int[] getCurrentProfileIds() {
    709             synchronized (mCurrentProfiles) {
    710                 int[] users = new int[mCurrentProfiles.size()];
    711                 final int N = mCurrentProfiles.size();
    712                 for (int i = 0; i < N; ++i) {
    713                     users[i] = mCurrentProfiles.keyAt(i);
    714                 }
    715                 return users;
    716             }
    717         }
    718 
    719         public boolean isCurrentProfile(int userId) {
    720             synchronized (mCurrentProfiles) {
    721                 return mCurrentProfiles.get(userId) != null;
    722             }
    723         }
    724     }
    725 
    726     protected static class Config {
    727         String caption;
    728         String serviceInterface;
    729         String secureSettingName;
    730         String bindPermission;
    731         String settingsAction;
    732         int clientLabel;
    733     }
    734 }
    735