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