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