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