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