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