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