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