Home | History | Annotate | Download | only in pm
      1 /*
      2  * Copyright (C) 2009 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 android.content.pm;
     18 
     19 import android.Manifest;
     20 import android.content.BroadcastReceiver;
     21 import android.content.ComponentName;
     22 import android.content.Context;
     23 import android.content.Intent;
     24 import android.content.IntentFilter;
     25 import android.content.pm.PackageManager.NameNotFoundException;
     26 import android.content.res.Resources;
     27 import android.content.res.XmlResourceParser;
     28 import android.os.Environment;
     29 import android.os.Handler;
     30 import android.os.UserHandle;
     31 import android.os.UserManager;
     32 import android.util.AtomicFile;
     33 import android.util.AttributeSet;
     34 import android.util.IntArray;
     35 import android.util.Log;
     36 import android.util.Slog;
     37 import android.util.SparseArray;
     38 import android.util.Xml;
     39 
     40 import com.android.internal.annotations.GuardedBy;
     41 import com.android.internal.annotations.VisibleForTesting;
     42 import com.android.internal.util.ArrayUtils;
     43 import com.android.internal.util.FastXmlSerializer;
     44 
     45 import com.google.android.collect.Lists;
     46 import com.google.android.collect.Maps;
     47 
     48 import libcore.io.IoUtils;
     49 
     50 import org.xmlpull.v1.XmlPullParser;
     51 import org.xmlpull.v1.XmlPullParserException;
     52 import org.xmlpull.v1.XmlSerializer;
     53 
     54 import java.io.File;
     55 import java.io.FileDescriptor;
     56 import java.io.FileOutputStream;
     57 import java.io.IOException;
     58 import java.io.InputStream;
     59 import java.io.PrintWriter;
     60 import java.nio.charset.StandardCharsets;
     61 import java.util.ArrayList;
     62 import java.util.Arrays;
     63 import java.util.Collection;
     64 import java.util.Collections;
     65 import java.util.List;
     66 import java.util.Map;
     67 
     68 /**
     69  * Cache of registered services. This cache is lazily built by interrogating
     70  * {@link PackageManager} on a per-user basis. It's updated as packages are
     71  * added, removed and changed. Users are responsible for calling
     72  * {@link #invalidateCache(int)} when a user is started, since
     73  * {@link PackageManager} broadcasts aren't sent for stopped users.
     74  * <p>
     75  * The services are referred to by type V and are made available via the
     76  * {@link #getServiceInfo} method.
     77  *
     78  * @hide
     79  */
     80 public abstract class RegisteredServicesCache<V> {
     81     private static final String TAG = "PackageManager";
     82     private static final boolean DEBUG = false;
     83     protected static final String REGISTERED_SERVICES_DIR = "registered_services";
     84 
     85     public final Context mContext;
     86     private final String mInterfaceName;
     87     private final String mMetaDataName;
     88     private final String mAttributesName;
     89     private final XmlSerializerAndParser<V> mSerializerAndParser;
     90 
     91     protected final Object mServicesLock = new Object();
     92 
     93     @GuardedBy("mServicesLock")
     94     private final SparseArray<UserServices<V>> mUserServices = new SparseArray<UserServices<V>>(2);
     95 
     96     private static class UserServices<V> {
     97         @GuardedBy("mServicesLock")
     98         final Map<V, Integer> persistentServices = Maps.newHashMap();
     99         @GuardedBy("mServicesLock")
    100         Map<V, ServiceInfo<V>> services = null;
    101         @GuardedBy("mServicesLock")
    102         boolean mPersistentServicesFileDidNotExist = true;
    103         @GuardedBy("mServicesLock")
    104         boolean mBindInstantServiceAllowed = false;
    105     }
    106 
    107     @GuardedBy("mServicesLock")
    108     private UserServices<V> findOrCreateUserLocked(int userId) {
    109         return findOrCreateUserLocked(userId, true);
    110     }
    111 
    112     @GuardedBy("mServicesLock")
    113     private UserServices<V> findOrCreateUserLocked(int userId, boolean loadFromFileIfNew) {
    114         UserServices<V> services = mUserServices.get(userId);
    115         if (services == null) {
    116             services = new UserServices<V>();
    117             mUserServices.put(userId, services);
    118             if (loadFromFileIfNew && mSerializerAndParser != null) {
    119                 // Check if user exists and try loading data from file
    120                 // clear existing data if there was an error during migration
    121                 UserInfo user = getUser(userId);
    122                 if (user != null) {
    123                     AtomicFile file = createFileForUser(user.id);
    124                     if (file.getBaseFile().exists()) {
    125                         if (DEBUG) {
    126                             Slog.i(TAG, String.format("Loading u%s data from %s", user.id, file));
    127                         }
    128                         InputStream is = null;
    129                         try {
    130                             is = file.openRead();
    131                             readPersistentServicesLocked(is);
    132                         } catch (Exception e) {
    133                             Log.w(TAG, "Error reading persistent services for user " + user.id, e);
    134                         } finally {
    135                             IoUtils.closeQuietly(is);
    136                         }
    137                     }
    138                 }
    139             }
    140         }
    141         return services;
    142     }
    143 
    144     // the listener and handler are synchronized on "this" and must be updated together
    145     private RegisteredServicesCacheListener<V> mListener;
    146     private Handler mHandler;
    147 
    148     public RegisteredServicesCache(Context context, String interfaceName, String metaDataName,
    149             String attributeName, XmlSerializerAndParser<V> serializerAndParser) {
    150         mContext = context;
    151         mInterfaceName = interfaceName;
    152         mMetaDataName = metaDataName;
    153         mAttributesName = attributeName;
    154         mSerializerAndParser = serializerAndParser;
    155 
    156         migrateIfNecessaryLocked();
    157 
    158         IntentFilter intentFilter = new IntentFilter();
    159         intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
    160         intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
    161         intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
    162         intentFilter.addDataScheme("package");
    163         mContext.registerReceiverAsUser(mPackageReceiver, UserHandle.ALL, intentFilter, null, null);
    164 
    165         // Register for events related to sdcard installation.
    166         IntentFilter sdFilter = new IntentFilter();
    167         sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
    168         sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
    169         mContext.registerReceiver(mExternalReceiver, sdFilter);
    170 
    171         // Register for user-related events
    172         IntentFilter userFilter = new IntentFilter();
    173         sdFilter.addAction(Intent.ACTION_USER_REMOVED);
    174         mContext.registerReceiver(mUserRemovedReceiver, userFilter);
    175     }
    176 
    177     private final void handlePackageEvent(Intent intent, int userId) {
    178         // Don't regenerate the services map when the package is removed or its
    179         // ASEC container unmounted as a step in replacement.  The subsequent
    180         // _ADDED / _AVAILABLE call will regenerate the map in the final state.
    181         final String action = intent.getAction();
    182         // it's a new-component action if it isn't some sort of removal
    183         final boolean isRemoval = Intent.ACTION_PACKAGE_REMOVED.equals(action)
    184                 || Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action);
    185         // if it's a removal, is it part of an update-in-place step?
    186         final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
    187 
    188         if (isRemoval && replacing) {
    189             // package is going away, but it's the middle of an upgrade: keep the current
    190             // state and do nothing here.  This clause is intentionally empty.
    191         } else {
    192             int[] uids = null;
    193             // either we're adding/changing, or it's a removal without replacement, so
    194             // we need to update the set of available services
    195             if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)
    196                     || Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
    197                 uids = intent.getIntArrayExtra(Intent.EXTRA_CHANGED_UID_LIST);
    198             } else {
    199                 int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
    200                 if (uid > 0) {
    201                     uids = new int[] { uid };
    202                 }
    203             }
    204             generateServicesMap(uids, userId);
    205         }
    206     }
    207 
    208     private final BroadcastReceiver mPackageReceiver = new BroadcastReceiver() {
    209         @Override
    210         public void onReceive(Context context, Intent intent) {
    211             final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
    212             if (uid != -1) {
    213                 handlePackageEvent(intent, UserHandle.getUserId(uid));
    214             }
    215         }
    216     };
    217 
    218     private final BroadcastReceiver mExternalReceiver = new BroadcastReceiver() {
    219         @Override
    220         public void onReceive(Context context, Intent intent) {
    221             // External apps can't coexist with multi-user, so scan owner
    222             handlePackageEvent(intent, UserHandle.USER_SYSTEM);
    223         }
    224     };
    225 
    226     private final BroadcastReceiver mUserRemovedReceiver = new BroadcastReceiver() {
    227         @Override
    228         public void onReceive(Context context, Intent intent) {
    229             int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
    230             if (DEBUG) {
    231                 Slog.d(TAG, "u" + userId + " removed - cleaning up");
    232             }
    233             onUserRemoved(userId);
    234         }
    235     };
    236 
    237     public void invalidateCache(int userId) {
    238         synchronized (mServicesLock) {
    239             final UserServices<V> user = findOrCreateUserLocked(userId);
    240             user.services = null;
    241             onServicesChangedLocked(userId);
    242         }
    243     }
    244 
    245     public void dump(FileDescriptor fd, PrintWriter fout, String[] args, int userId) {
    246         synchronized (mServicesLock) {
    247             final UserServices<V> user = findOrCreateUserLocked(userId);
    248             if (user.services != null) {
    249                 fout.println("RegisteredServicesCache: " + user.services.size() + " services");
    250                 for (ServiceInfo<?> info : user.services.values()) {
    251                     fout.println("  " + info);
    252                 }
    253             } else {
    254                 fout.println("RegisteredServicesCache: services not loaded");
    255             }
    256         }
    257     }
    258 
    259     public RegisteredServicesCacheListener<V> getListener() {
    260         synchronized (this) {
    261             return mListener;
    262         }
    263     }
    264 
    265     public void setListener(RegisteredServicesCacheListener<V> listener, Handler handler) {
    266         if (handler == null) {
    267             handler = new Handler(mContext.getMainLooper());
    268         }
    269         synchronized (this) {
    270             mHandler = handler;
    271             mListener = listener;
    272         }
    273     }
    274 
    275     private void notifyListener(final V type, final int userId, final boolean removed) {
    276         if (DEBUG) {
    277             Log.d(TAG, "notifyListener: " + type + " is " + (removed ? "removed" : "added"));
    278         }
    279         RegisteredServicesCacheListener<V> listener;
    280         Handler handler;
    281         synchronized (this) {
    282             listener = mListener;
    283             handler = mHandler;
    284         }
    285         if (listener == null) {
    286             return;
    287         }
    288 
    289         final RegisteredServicesCacheListener<V> listener2 = listener;
    290         handler.post(new Runnable() {
    291             public void run() {
    292                 listener2.onServiceChanged(type, userId, removed);
    293             }
    294         });
    295     }
    296 
    297     /**
    298      * Value type that describes a Service. The information within can be used
    299      * to bind to the service.
    300      */
    301     public static class ServiceInfo<V> {
    302         public final V type;
    303         public final ComponentInfo componentInfo;
    304         public final ComponentName componentName;
    305         public final int uid;
    306 
    307         /** @hide */
    308         public ServiceInfo(V type, ComponentInfo componentInfo, ComponentName componentName) {
    309             this.type = type;
    310             this.componentInfo = componentInfo;
    311             this.componentName = componentName;
    312             this.uid = (componentInfo != null) ? componentInfo.applicationInfo.uid : -1;
    313         }
    314 
    315         @Override
    316         public String toString() {
    317             return "ServiceInfo: " + type + ", " + componentName + ", uid " + uid;
    318         }
    319     }
    320 
    321     /**
    322      * Accessor for the registered authenticators.
    323      * @param type the account type of the authenticator
    324      * @return the AuthenticatorInfo that matches the account type or null if none is present
    325      */
    326     public ServiceInfo<V> getServiceInfo(V type, int userId) {
    327         synchronized (mServicesLock) {
    328             // Find user and lazily populate cache
    329             final UserServices<V> user = findOrCreateUserLocked(userId);
    330             if (user.services == null) {
    331                 generateServicesMap(null, userId);
    332             }
    333             return user.services.get(type);
    334         }
    335     }
    336 
    337     /**
    338      * @return a collection of {@link RegisteredServicesCache.ServiceInfo} objects for all
    339      * registered authenticators.
    340      */
    341     public Collection<ServiceInfo<V>> getAllServices(int userId) {
    342         synchronized (mServicesLock) {
    343             // Find user and lazily populate cache
    344             final UserServices<V> user = findOrCreateUserLocked(userId);
    345             if (user.services == null) {
    346                 generateServicesMap(null, userId);
    347             }
    348             return Collections.unmodifiableCollection(
    349                     new ArrayList<ServiceInfo<V>>(user.services.values()));
    350         }
    351     }
    352 
    353     public void updateServices(int userId) {
    354         if (DEBUG) {
    355             Slog.d(TAG, "updateServices u" + userId);
    356         }
    357         List<ServiceInfo<V>> allServices;
    358         synchronized (mServicesLock) {
    359             final UserServices<V> user = findOrCreateUserLocked(userId);
    360             // If services haven't been initialized yet - no updates required
    361             if (user.services == null) {
    362                 return;
    363             }
    364             allServices = new ArrayList<>(user.services.values());
    365         }
    366         IntArray updatedUids = null;
    367         for (ServiceInfo<V> service : allServices) {
    368             long versionCode = service.componentInfo.applicationInfo.versionCode;
    369             String pkg = service.componentInfo.packageName;
    370             ApplicationInfo newAppInfo = null;
    371             try {
    372                 newAppInfo = mContext.getPackageManager().getApplicationInfoAsUser(pkg, 0, userId);
    373             } catch (NameNotFoundException e) {
    374                 // Package uninstalled - treat as null app info
    375             }
    376             // If package updated or removed
    377             if ((newAppInfo == null) || (newAppInfo.versionCode != versionCode)) {
    378                 if (DEBUG) {
    379                     Slog.d(TAG, "Package " + pkg + " uid=" + service.uid
    380                             + " updated. New appInfo: " + newAppInfo);
    381                 }
    382                 if (updatedUids == null) {
    383                     updatedUids = new IntArray();
    384                 }
    385                 updatedUids.add(service.uid);
    386             }
    387         }
    388         if (updatedUids != null && updatedUids.size() > 0) {
    389             int[] updatedUidsArray = updatedUids.toArray();
    390             generateServicesMap(updatedUidsArray, userId);
    391         }
    392     }
    393 
    394     /**
    395      * @return whether the binding to service is allowed for instant apps.
    396      */
    397     public boolean getBindInstantServiceAllowed(int userId) {
    398         mContext.enforceCallingOrSelfPermission(
    399                 Manifest.permission.MANAGE_BIND_INSTANT_SERVICE,
    400                 "getBindInstantServiceAllowed");
    401 
    402         synchronized (mServicesLock) {
    403             final UserServices<V> user = findOrCreateUserLocked(userId);
    404             return user.mBindInstantServiceAllowed;
    405         }
    406     }
    407 
    408     /**
    409      * Set whether the binding to service is allowed or not for instant apps.
    410      */
    411     public void setBindInstantServiceAllowed(int userId, boolean allowed) {
    412         mContext.enforceCallingOrSelfPermission(
    413                 Manifest.permission.MANAGE_BIND_INSTANT_SERVICE,
    414                 "setBindInstantServiceAllowed");
    415 
    416         synchronized (mServicesLock) {
    417             final UserServices<V> user = findOrCreateUserLocked(userId);
    418             user.mBindInstantServiceAllowed = allowed;
    419         }
    420     }
    421 
    422     @VisibleForTesting
    423     protected boolean inSystemImage(int callerUid) {
    424         String[] packages = mContext.getPackageManager().getPackagesForUid(callerUid);
    425         if (packages != null) {
    426             for (String name : packages) {
    427                 try {
    428                     PackageInfo packageInfo =
    429                             mContext.getPackageManager().getPackageInfo(name, 0 /* flags */);
    430                     if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
    431                         return true;
    432                     }
    433                 } catch (PackageManager.NameNotFoundException e) {
    434                     return false;
    435                 }
    436             }
    437         }
    438         return false;
    439     }
    440 
    441     @VisibleForTesting
    442     protected List<ResolveInfo> queryIntentServices(int userId) {
    443         final PackageManager pm = mContext.getPackageManager();
    444         int flags = PackageManager.GET_META_DATA
    445                 | PackageManager.MATCH_DIRECT_BOOT_AWARE
    446                 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
    447         synchronized (mServicesLock) {
    448             final UserServices<V> user = findOrCreateUserLocked(userId);
    449             if (user.mBindInstantServiceAllowed) {
    450                 flags |= PackageManager.MATCH_INSTANT;
    451             }
    452         }
    453         return pm.queryIntentServicesAsUser(new Intent(mInterfaceName), flags, userId);
    454     }
    455 
    456     /**
    457      * Populate {@link UserServices#services} by scanning installed packages for
    458      * given {@link UserHandle}.
    459      * @param changedUids the array of uids that have been affected, as mentioned in the broadcast
    460      *                    or null to assume that everything is affected.
    461      * @param userId the user for whom to update the services map.
    462      */
    463     private void generateServicesMap(int[] changedUids, int userId) {
    464         if (DEBUG) {
    465             Slog.d(TAG, "generateServicesMap() for " + userId + ", changed UIDs = "
    466                     + Arrays.toString(changedUids));
    467         }
    468 
    469         final ArrayList<ServiceInfo<V>> serviceInfos = new ArrayList<>();
    470         final List<ResolveInfo> resolveInfos = queryIntentServices(userId);
    471         for (ResolveInfo resolveInfo : resolveInfos) {
    472             try {
    473                 ServiceInfo<V> info = parseServiceInfo(resolveInfo);
    474                 if (info == null) {
    475                     Log.w(TAG, "Unable to load service info " + resolveInfo.toString());
    476                     continue;
    477                 }
    478                 serviceInfos.add(info);
    479             } catch (XmlPullParserException|IOException e) {
    480                 Log.w(TAG, "Unable to load service info " + resolveInfo.toString(), e);
    481             }
    482         }
    483 
    484         synchronized (mServicesLock) {
    485             final UserServices<V> user = findOrCreateUserLocked(userId);
    486             final boolean firstScan = user.services == null;
    487             if (firstScan) {
    488                 user.services = Maps.newHashMap();
    489             }
    490 
    491             StringBuilder changes = new StringBuilder();
    492             boolean changed = false;
    493             for (ServiceInfo<V> info : serviceInfos) {
    494                 // four cases:
    495                 // - doesn't exist yet
    496                 //   - add, notify user that it was added
    497                 // - exists and the UID is the same
    498                 //   - replace, don't notify user
    499                 // - exists, the UID is different, and the new one is not a system package
    500                 //   - ignore
    501                 // - exists, the UID is different, and the new one is a system package
    502                 //   - add, notify user that it was added
    503                 Integer previousUid = user.persistentServices.get(info.type);
    504                 if (previousUid == null) {
    505                     if (DEBUG) {
    506                         changes.append("  New service added: ").append(info).append("\n");
    507                     }
    508                     changed = true;
    509                     user.services.put(info.type, info);
    510                     user.persistentServices.put(info.type, info.uid);
    511                     if (!(user.mPersistentServicesFileDidNotExist && firstScan)) {
    512                         notifyListener(info.type, userId, false /* removed */);
    513                     }
    514                 } else if (previousUid == info.uid) {
    515                     if (DEBUG) {
    516                         changes.append("  Existing service (nop): ").append(info).append("\n");
    517                     }
    518                     user.services.put(info.type, info);
    519                 } else if (inSystemImage(info.uid)
    520                         || !containsTypeAndUid(serviceInfos, info.type, previousUid)) {
    521                     if (DEBUG) {
    522                         if (inSystemImage(info.uid)) {
    523                             changes.append("  System service replacing existing: ").append(info)
    524                                     .append("\n");
    525                         } else {
    526                             changes.append("  Existing service replacing a removed service: ")
    527                                     .append(info).append("\n");
    528                         }
    529                     }
    530                     changed = true;
    531                     user.services.put(info.type, info);
    532                     user.persistentServices.put(info.type, info.uid);
    533                     notifyListener(info.type, userId, false /* removed */);
    534                 } else {
    535                     // ignore
    536                     if (DEBUG) {
    537                         changes.append("  Existing service with new uid ignored: ").append(info)
    538                                 .append("\n");
    539                     }
    540                 }
    541             }
    542 
    543             ArrayList<V> toBeRemoved = Lists.newArrayList();
    544             for (V v1 : user.persistentServices.keySet()) {
    545                 // Remove a persisted service that's not in the currently available services list.
    546                 // And only if it is in the list of changedUids.
    547                 if (!containsType(serviceInfos, v1)
    548                         && containsUid(changedUids, user.persistentServices.get(v1))) {
    549                     toBeRemoved.add(v1);
    550                 }
    551             }
    552             for (V v1 : toBeRemoved) {
    553                 if (DEBUG) {
    554                     changes.append("  Service removed: ").append(v1).append("\n");
    555                 }
    556                 changed = true;
    557                 user.persistentServices.remove(v1);
    558                 user.services.remove(v1);
    559                 notifyListener(v1, userId, true /* removed */);
    560             }
    561             if (DEBUG) {
    562                 Log.d(TAG, "user.services=");
    563                 for (V v : user.services.keySet()) {
    564                     Log.d(TAG, "  " + v + " " + user.services.get(v));
    565                 }
    566                 Log.d(TAG, "user.persistentServices=");
    567                 for (V v : user.persistentServices.keySet()) {
    568                     Log.d(TAG, "  " + v + " " + user.persistentServices.get(v));
    569                 }
    570             }
    571             if (DEBUG) {
    572                 if (changes.length() > 0) {
    573                     Log.d(TAG, "generateServicesMap(" + mInterfaceName + "): " +
    574                             serviceInfos.size() + " services:\n" + changes);
    575                 } else {
    576                     Log.d(TAG, "generateServicesMap(" + mInterfaceName + "): " +
    577                             serviceInfos.size() + " services unchanged");
    578                 }
    579             }
    580             if (changed) {
    581                 onServicesChangedLocked(userId);
    582                 writePersistentServicesLocked(user, userId);
    583             }
    584         }
    585     }
    586 
    587     protected void onServicesChangedLocked(int userId) {
    588         // Feel free to override
    589     }
    590 
    591     /**
    592      * Returns true if the list of changed uids is null (wildcard) or the specified uid
    593      * is contained in the list of changed uids.
    594      */
    595     private boolean containsUid(int[] changedUids, int uid) {
    596         return changedUids == null || ArrayUtils.contains(changedUids, uid);
    597     }
    598 
    599     private boolean containsType(ArrayList<ServiceInfo<V>> serviceInfos, V type) {
    600         for (int i = 0, N = serviceInfos.size(); i < N; i++) {
    601             if (serviceInfos.get(i).type.equals(type)) {
    602                 return true;
    603             }
    604         }
    605 
    606         return false;
    607     }
    608 
    609     private boolean containsTypeAndUid(ArrayList<ServiceInfo<V>> serviceInfos, V type, int uid) {
    610         for (int i = 0, N = serviceInfos.size(); i < N; i++) {
    611             final ServiceInfo<V> serviceInfo = serviceInfos.get(i);
    612             if (serviceInfo.type.equals(type) && serviceInfo.uid == uid) {
    613                 return true;
    614             }
    615         }
    616 
    617         return false;
    618     }
    619 
    620     @VisibleForTesting
    621     protected ServiceInfo<V> parseServiceInfo(ResolveInfo service)
    622             throws XmlPullParserException, IOException {
    623         android.content.pm.ServiceInfo si = service.serviceInfo;
    624         ComponentName componentName = new ComponentName(si.packageName, si.name);
    625 
    626         PackageManager pm = mContext.getPackageManager();
    627 
    628         XmlResourceParser parser = null;
    629         try {
    630             parser = si.loadXmlMetaData(pm, mMetaDataName);
    631             if (parser == null) {
    632                 throw new XmlPullParserException("No " + mMetaDataName + " meta-data");
    633             }
    634 
    635             AttributeSet attrs = Xml.asAttributeSet(parser);
    636 
    637             int type;
    638             while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
    639                     && type != XmlPullParser.START_TAG) {
    640             }
    641 
    642             String nodeName = parser.getName();
    643             if (!mAttributesName.equals(nodeName)) {
    644                 throw new XmlPullParserException(
    645                         "Meta-data does not start with " + mAttributesName +  " tag");
    646             }
    647 
    648             V v = parseServiceAttributes(pm.getResourcesForApplication(si.applicationInfo),
    649                     si.packageName, attrs);
    650             if (v == null) {
    651                 return null;
    652             }
    653             final android.content.pm.ServiceInfo serviceInfo = service.serviceInfo;
    654             return new ServiceInfo<V>(v, serviceInfo, componentName);
    655         } catch (NameNotFoundException e) {
    656             throw new XmlPullParserException(
    657                     "Unable to load resources for pacakge " + si.packageName);
    658         } finally {
    659             if (parser != null) parser.close();
    660         }
    661     }
    662 
    663     /**
    664      * Read all sync status back in to the initial engine state.
    665      */
    666     private void readPersistentServicesLocked(InputStream is)
    667             throws XmlPullParserException, IOException {
    668         XmlPullParser parser = Xml.newPullParser();
    669         parser.setInput(is, StandardCharsets.UTF_8.name());
    670         int eventType = parser.getEventType();
    671         while (eventType != XmlPullParser.START_TAG
    672                 && eventType != XmlPullParser.END_DOCUMENT) {
    673             eventType = parser.next();
    674         }
    675         String tagName = parser.getName();
    676         if ("services".equals(tagName)) {
    677             eventType = parser.next();
    678             do {
    679                 if (eventType == XmlPullParser.START_TAG && parser.getDepth() == 2) {
    680                     tagName = parser.getName();
    681                     if ("service".equals(tagName)) {
    682                         V service = mSerializerAndParser.createFromXml(parser);
    683                         if (service == null) {
    684                             break;
    685                         }
    686                         String uidString = parser.getAttributeValue(null, "uid");
    687                         final int uid = Integer.parseInt(uidString);
    688                         final int userId = UserHandle.getUserId(uid);
    689                         final UserServices<V> user = findOrCreateUserLocked(userId,
    690                                 false /*loadFromFileIfNew*/) ;
    691                         user.persistentServices.put(service, uid);
    692                     }
    693                 }
    694                 eventType = parser.next();
    695             } while (eventType != XmlPullParser.END_DOCUMENT);
    696         }
    697     }
    698 
    699     private void migrateIfNecessaryLocked() {
    700         if (mSerializerAndParser == null) {
    701             return;
    702         }
    703         File systemDir = new File(getDataDirectory(), "system");
    704         File syncDir = new File(systemDir, REGISTERED_SERVICES_DIR);
    705         AtomicFile oldFile = new AtomicFile(new File(syncDir, mInterfaceName + ".xml"));
    706         boolean oldFileExists = oldFile.getBaseFile().exists();
    707 
    708         if (oldFileExists) {
    709             File marker = new File(syncDir, mInterfaceName + ".xml.migrated");
    710             // if not migrated, perform the migration and add a marker
    711             if (!marker.exists()) {
    712                 if (DEBUG) {
    713                     Slog.i(TAG, "Marker file " + marker + " does not exist - running migration");
    714                 }
    715                 InputStream is = null;
    716                 try {
    717                     is = oldFile.openRead();
    718                     mUserServices.clear();
    719                     readPersistentServicesLocked(is);
    720                 } catch (Exception e) {
    721                     Log.w(TAG, "Error reading persistent services, starting from scratch", e);
    722                 } finally {
    723                     IoUtils.closeQuietly(is);
    724                 }
    725                 try {
    726                     for (UserInfo user : getUsers()) {
    727                         UserServices<V> userServices = mUserServices.get(user.id);
    728                         if (userServices != null) {
    729                             if (DEBUG) {
    730                                 Slog.i(TAG, "Migrating u" + user.id + " services "
    731                                         + userServices.persistentServices);
    732                             }
    733                             writePersistentServicesLocked(userServices, user.id);
    734                         }
    735                     }
    736                     marker.createNewFile();
    737                 } catch (Exception e) {
    738                     Log.w(TAG, "Migration failed", e);
    739                 }
    740                 // Migration is complete and we don't need to keep data for all users anymore,
    741                 // It will be loaded from a new location when requested
    742                 mUserServices.clear();
    743             }
    744         }
    745     }
    746 
    747     /**
    748      * Writes services of a specified user to the file.
    749      */
    750     private void writePersistentServicesLocked(UserServices<V> user, int userId) {
    751         if (mSerializerAndParser == null) {
    752             return;
    753         }
    754         AtomicFile atomicFile = createFileForUser(userId);
    755         FileOutputStream fos = null;
    756         try {
    757             fos = atomicFile.startWrite();
    758             XmlSerializer out = new FastXmlSerializer();
    759             out.setOutput(fos, StandardCharsets.UTF_8.name());
    760             out.startDocument(null, true);
    761             out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
    762             out.startTag(null, "services");
    763             for (Map.Entry<V, Integer> service : user.persistentServices.entrySet()) {
    764                 out.startTag(null, "service");
    765                 out.attribute(null, "uid", Integer.toString(service.getValue()));
    766                 mSerializerAndParser.writeAsXml(service.getKey(), out);
    767                 out.endTag(null, "service");
    768             }
    769             out.endTag(null, "services");
    770             out.endDocument();
    771             atomicFile.finishWrite(fos);
    772         } catch (IOException e1) {
    773             Log.w(TAG, "Error writing accounts", e1);
    774             if (fos != null) {
    775                 atomicFile.failWrite(fos);
    776             }
    777         }
    778     }
    779 
    780     @VisibleForTesting
    781     protected void onUserRemoved(int userId) {
    782         synchronized (mServicesLock) {
    783             mUserServices.remove(userId);
    784         }
    785     }
    786 
    787     @VisibleForTesting
    788     protected List<UserInfo> getUsers() {
    789         return UserManager.get(mContext).getUsers(true);
    790     }
    791 
    792     @VisibleForTesting
    793     protected UserInfo getUser(int userId) {
    794         return UserManager.get(mContext).getUserInfo(userId);
    795     }
    796 
    797     private AtomicFile createFileForUser(int userId) {
    798         File userDir = getUserSystemDirectory(userId);
    799         File userFile = new File(userDir, REGISTERED_SERVICES_DIR + "/" + mInterfaceName + ".xml");
    800         return new AtomicFile(userFile);
    801     }
    802 
    803     @VisibleForTesting
    804     protected File getUserSystemDirectory(int userId) {
    805         return Environment.getUserSystemDirectory(userId);
    806     }
    807 
    808     @VisibleForTesting
    809     protected File getDataDirectory() {
    810         return Environment.getDataDirectory();
    811     }
    812 
    813     @VisibleForTesting
    814     protected Map<V, Integer> getPersistentServices(int userId) {
    815         return findOrCreateUserLocked(userId).persistentServices;
    816     }
    817 
    818     public abstract V parseServiceAttributes(Resources res,
    819             String packageName, AttributeSet attrs);
    820 }
    821