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