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