1 /* 2 * Copyright (C) 2019 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 com.android.server.pm; 18 19 import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER; 20 21 import android.annotation.IntDef; 22 import android.annotation.NonNull; 23 import android.annotation.Nullable; 24 import android.apex.ApexInfo; 25 import android.apex.ApexInfoList; 26 import android.apex.ApexSessionInfo; 27 import android.apex.ApexSessionParams; 28 import android.apex.CompressedApexInfoList; 29 import android.apex.IApexService; 30 import android.content.pm.ApplicationInfo; 31 import android.content.pm.PackageInfo; 32 import android.content.pm.PackageManager; 33 import android.content.pm.SigningDetails; 34 import android.content.pm.parsing.result.ParseResult; 35 import android.content.pm.parsing.result.ParseTypeImpl; 36 import android.os.Binder; 37 import android.os.Environment; 38 import android.os.RemoteException; 39 import android.os.ServiceManager; 40 import android.os.Trace; 41 import android.sysprop.ApexProperties; 42 import android.text.TextUtils; 43 import android.util.ArrayMap; 44 import android.util.ArraySet; 45 import android.util.PrintWriterPrinter; 46 import android.util.Singleton; 47 import android.util.Slog; 48 import android.util.SparseArray; 49 import android.util.apk.ApkSignatureVerifier; 50 51 import com.android.internal.annotations.GuardedBy; 52 import com.android.internal.annotations.VisibleForTesting; 53 import com.android.internal.util.IndentingPrintWriter; 54 import com.android.internal.util.Preconditions; 55 import com.android.modules.utils.build.UnboundedSdkLevel; 56 import com.android.server.pm.parsing.PackageParser2; 57 import com.android.server.pm.parsing.pkg.AndroidPackage; 58 import com.android.server.pm.parsing.pkg.ParsedPackage; 59 import com.android.server.pm.pkg.component.ParsedApexSystemService; 60 import com.android.server.pm.pkg.parsing.PackageInfoWithoutStateUtils; 61 import com.android.server.pm.pkg.parsing.ParsingPackageUtils; 62 import com.android.server.utils.TimingsTraceAndSlog; 63 64 import com.google.android.collect.Lists; 65 66 import java.io.File; 67 import java.io.PrintWriter; 68 import java.lang.annotation.Retention; 69 import java.lang.annotation.RetentionPolicy; 70 import java.nio.file.Path; 71 import java.util.ArrayList; 72 import java.util.Collections; 73 import java.util.HashSet; 74 import java.util.List; 75 import java.util.Map; 76 import java.util.Objects; 77 import java.util.Set; 78 import java.util.concurrent.ExecutorService; 79 80 /** 81 * ApexManager class handles communications with the apex service to perform operation and queries, 82 * as well as providing caching to avoid unnecessary calls to the service. 83 */ 84 public abstract class ApexManager { 85 86 private static final String TAG = "ApexManager"; 87 88 public static final int MATCH_ACTIVE_PACKAGE = 1 << 0; 89 static final int MATCH_FACTORY_PACKAGE = 1 << 1; 90 91 private static final String VNDK_APEX_MODULE_NAME_PREFIX = "com.android.vndk."; 92 93 private static final Singleton<ApexManager> sApexManagerSingleton = 94 new Singleton<ApexManager>() { 95 @Override 96 protected ApexManager create() { 97 if (ApexProperties.updatable().orElse(false)) { 98 return new ApexManagerImpl(); 99 } else { 100 return new ApexManagerFlattenedApex(); 101 } 102 } 103 }; 104 105 /** 106 * Returns an instance of either {@link ApexManagerImpl} or {@link ApexManagerFlattenedApex} 107 * depending on whether this device supports APEX, i.e. {@link ApexProperties#updatable()} 108 * evaluates to {@code true}. 109 * @hide 110 */ getInstance()111 public static ApexManager getInstance() { 112 return sApexManagerSingleton.get(); 113 } 114 115 /** 116 * Minimal information about APEX mount points and the original APEX package they refer to. 117 * @hide 118 */ 119 public static class ActiveApexInfo { 120 @Nullable public final String apexModuleName; 121 public final File apexDirectory; 122 public final File preInstalledApexPath; 123 public final File apexFile; 124 public final boolean activeApexChanged; 125 ActiveApexInfo(File apexDirectory, File preInstalledApexPath, File apexFile)126 private ActiveApexInfo(File apexDirectory, File preInstalledApexPath, File apexFile) { 127 this(null, apexDirectory, preInstalledApexPath, apexFile, false); 128 } 129 ActiveApexInfo(@ullable String apexModuleName, File apexDirectory, File preInstalledApexPath, File apexFile, boolean activeApexChanged)130 private ActiveApexInfo(@Nullable String apexModuleName, File apexDirectory, 131 File preInstalledApexPath, File apexFile, boolean activeApexChanged) { 132 this.apexModuleName = apexModuleName; 133 this.apexDirectory = apexDirectory; 134 this.preInstalledApexPath = preInstalledApexPath; 135 this.apexFile = apexFile; 136 this.activeApexChanged = activeApexChanged; 137 } 138 ActiveApexInfo(ApexInfo apexInfo)139 private ActiveApexInfo(ApexInfo apexInfo) { 140 this( 141 apexInfo.moduleName, 142 new File(Environment.getApexDirectory() + File.separator 143 + apexInfo.moduleName), 144 new File(apexInfo.preinstalledModulePath), 145 new File(apexInfo.modulePath), 146 apexInfo.activeApexChanged); 147 } 148 } 149 150 /** 151 * Returns {@link ActiveApexInfo} records relative to all active APEX packages. 152 * 153 * @hide 154 */ getActiveApexInfos()155 public abstract List<ActiveApexInfo> getActiveApexInfos(); 156 157 /** 158 * Called by package manager service to scan apex package files when device boots up. 159 * 160 * @param packageParser The package parser to support apex package parsing and caching parsed 161 * results. 162 * @param executorService An executor to support parallel package parsing. 163 */ scanApexPackagesTraced(@onNull PackageParser2 packageParser, @NonNull ExecutorService executorService)164 abstract void scanApexPackagesTraced(@NonNull PackageParser2 packageParser, 165 @NonNull ExecutorService executorService); 166 167 /** 168 * Retrieves information about an APEX package. 169 * 170 * @param packageName the package name to look for. Note that this is the package name reported 171 * in the APK container manifest (i.e. AndroidManifest.xml), which might 172 * differ from the one reported in the APEX manifest (i.e. 173 * apex_manifest.json). 174 * @param flags the type of package to return. This may match to active packages 175 * and factory (pre-installed) packages. 176 * @return a PackageInfo object with the information about the package, or null if the package 177 * is not found. 178 */ 179 @Nullable getPackageInfo(String packageName, @PackageInfoFlags int flags)180 public abstract PackageInfo getPackageInfo(String packageName, @PackageInfoFlags int flags); 181 182 /** 183 * Retrieves information about all active APEX packages. 184 * 185 * @return a List of PackageInfo object, each one containing information about a different 186 * active package. 187 */ getActivePackages()188 abstract List<PackageInfo> getActivePackages(); 189 190 /** 191 * Retrieves information about all active pre-installed APEX packages. 192 * 193 * @return a List of PackageInfo object, each one containing information about a different 194 * active pre-installed package. 195 */ getFactoryPackages()196 abstract List<PackageInfo> getFactoryPackages(); 197 198 /** 199 * Retrieves information about all inactive APEX packages. 200 * 201 * @return a List of PackageInfo object, each one containing information about a different 202 * inactive package. 203 */ getInactivePackages()204 abstract List<PackageInfo> getInactivePackages(); 205 206 /** 207 * Checks if {@code packageName} is an apex package. 208 * 209 * @param packageName package to check. 210 * @return {@code true} if {@code packageName} is an apex package. 211 */ isApexPackage(String packageName)212 abstract boolean isApexPackage(String packageName); 213 214 /** 215 * Whether the APEX package is pre-installed or not. 216 * 217 * @param packageInfo the package to check 218 * @return {@code true} if this package is pre-installed, {@code false} otherwise. 219 */ isFactory(@onNull PackageInfo packageInfo)220 public static boolean isFactory(@NonNull PackageInfo packageInfo) { 221 return (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0; 222 } 223 224 /** 225 * Returns the active apex package's name that contains the (apk) package. 226 * 227 * @param containedPackageName The (apk) package that might be in a apex 228 * @return the apex package's name of {@code null} if the {@code containedPackage} is not inside 229 * any apex. 230 */ 231 @Nullable getActiveApexPackageNameContainingPackage( @onNull String containedPackageName)232 public abstract String getActiveApexPackageNameContainingPackage( 233 @NonNull String containedPackageName); 234 235 /** 236 * Retrieves information about an apexd staged session i.e. the internal state used by apexd to 237 * track the different states of a session. 238 * 239 * @param sessionId the identifier of the session. 240 * @return an ApexSessionInfo object, or null if the session is not known. 241 */ 242 @Nullable getStagedSessionInfo(int sessionId)243 abstract ApexSessionInfo getStagedSessionInfo(int sessionId); 244 245 /** 246 * Returns array of all staged sessions known to apexd. 247 */ 248 @NonNull getSessions()249 abstract SparseArray<ApexSessionInfo> getSessions(); 250 251 /** 252 * Submit a staged session to apex service. This causes the apex service to perform some initial 253 * verification and accept or reject the session. Submitting a session successfully is not 254 * enough for it to be activated at the next boot, the caller needs to call 255 * {@link #markStagedSessionReady(int)}. 256 * 257 * @throws PackageManagerException if call to apexd fails 258 */ submitStagedSession(ApexSessionParams params)259 abstract ApexInfoList submitStagedSession(ApexSessionParams params) 260 throws PackageManagerException; 261 262 /** 263 * Returns {@code ApeInfo} about apex sessions that have been marked ready via 264 * {@link #markStagedSessionReady(int)} 265 * 266 * Returns empty array if there is no staged apex session or if there is any error. 267 */ getStagedApexInfos(ApexSessionParams params)268 abstract ApexInfo[] getStagedApexInfos(ApexSessionParams params); 269 270 /** 271 * Mark a staged session previously submitted using {@code submitStagedSession} as ready to be 272 * applied at next reboot. 273 * 274 * @param sessionId the identifier of the {@link PackageInstallerSession} being marked as ready. 275 * @throws PackageManagerException if call to apexd fails 276 */ markStagedSessionReady(int sessionId)277 abstract void markStagedSessionReady(int sessionId) throws PackageManagerException; 278 279 /** 280 * Marks a staged session as successful. 281 * 282 * <p>Only activated session can be marked as successful. 283 * 284 * @param sessionId the identifier of the {@link PackageInstallerSession} being marked as 285 * successful. 286 */ markStagedSessionSuccessful(int sessionId)287 abstract void markStagedSessionSuccessful(int sessionId); 288 289 /** 290 * Whether the current device supports the management of APEX packages. 291 * 292 * @return true if APEX packages can be managed on this device, false otherwise. 293 */ isApexSupported()294 abstract boolean isApexSupported(); 295 296 /** 297 * Abandons the (only) active session previously submitted. 298 * 299 * @return {@code true} upon success, {@code false} if any remote exception occurs 300 */ revertActiveSessions()301 abstract boolean revertActiveSessions(); 302 303 /** 304 * Abandons the staged session with the given sessionId. Client should handle {@code false} 305 * return value carefully as failure here can leave device in inconsistent state. 306 * 307 * @return {@code true} upon success, {@code false} if any exception occurs 308 */ abortStagedSession(int sessionId)309 abstract boolean abortStagedSession(int sessionId); 310 311 /** 312 * Uninstalls given {@code apexPackage}. 313 * 314 * <p>NOTE. Device must be rebooted in order for uninstall to take effect. 315 * 316 * @param apexPackagePath package to uninstall. 317 * @return {@code true} upon successful uninstall, {@code false} otherwise. 318 */ uninstallApex(String apexPackagePath)319 abstract boolean uninstallApex(String apexPackagePath); 320 321 /** 322 * Registers an APK package as an embedded apk of apex. 323 */ registerApkInApex(AndroidPackage pkg)324 abstract void registerApkInApex(AndroidPackage pkg); 325 326 /** 327 * Reports error raised during installation of apk-in-apex. 328 * 329 * @param scanDirPath the directory of the apex inside which apk-in-apex resides. 330 * @param errorMsg the actual error that occurred when scanning the path 331 */ reportErrorWithApkInApex(String scanDirPath, String errorMsg)332 abstract void reportErrorWithApkInApex(String scanDirPath, String errorMsg); 333 334 /** 335 * Returns null if there were no errors when installing apk-in-apex inside 336 * {@param apexPackageName}, otherwise returns the error as string 337 * 338 * @param apexPackageName Package name of the apk container of apex 339 */ 340 @Nullable getApkInApexInstallError(String apexPackageName)341 abstract String getApkInApexInstallError(String apexPackageName); 342 343 /** 344 * Returns list of {@code packageName} of apks inside the given apex. 345 * @param apexPackageName Package name of the apk container of apex 346 */ getApksInApex(String apexPackageName)347 abstract List<String> getApksInApex(String apexPackageName); 348 349 /** 350 * Returns the apex module name for the given package name, if the package is an APEX. Otherwise 351 * returns {@code null}. 352 */ 353 @Nullable getApexModuleNameForPackageName(String apexPackageName)354 public abstract String getApexModuleNameForPackageName(String apexPackageName); 355 356 /** 357 * Returns the package name of the active APEX whose name is {@code apexModuleName}. If not 358 * found, returns {@code null}. 359 */ 360 @Nullable getActivePackageNameForApexModuleName(String apexModuleName)361 public abstract String getActivePackageNameForApexModuleName(String apexModuleName); 362 363 /** 364 * Copies the CE apex data directory for the given {@code userId} to a backup location, for use 365 * in case of rollback. 366 * 367 * @return boolean true if the snapshot was successful 368 */ snapshotCeData(int userId, int rollbackId, String apexPackageName)369 public abstract boolean snapshotCeData(int userId, int rollbackId, String apexPackageName); 370 371 /** 372 * Restores the snapshot of the CE apex data directory for the given {@code userId}. 373 * Note the snapshot will be deleted after restoration succeeded. 374 * 375 * @return boolean true if the restore was successful 376 */ restoreCeData(int userId, int rollbackId, String apexPackageName)377 public abstract boolean restoreCeData(int userId, int rollbackId, String apexPackageName); 378 379 /** 380 * Deletes snapshots of the device encrypted apex data directories for the given 381 * {@code rollbackId}. 382 * 383 * @return boolean true if the delete was successful 384 */ destroyDeSnapshots(int rollbackId)385 public abstract boolean destroyDeSnapshots(int rollbackId); 386 387 /** 388 * Deletes snapshots of the credential encrypted apex data directories for the specified user, 389 * for the given rollback id as long as the user is credential unlocked. 390 * 391 * @return boolean true if the delete was successful 392 */ destroyCeSnapshots(int userId, int rollbackId)393 public abstract boolean destroyCeSnapshots(int userId, int rollbackId); 394 395 /** 396 * Deletes snapshots of the credential encrypted apex data directories for the specified user, 397 * where the rollback id is not included in {@code retainRollbackIds} as long as the user is 398 * credential unlocked. 399 * 400 * @return boolean true if the delete was successful 401 */ destroyCeSnapshotsNotSpecified(int userId, int[] retainRollbackIds)402 public abstract boolean destroyCeSnapshotsNotSpecified(int userId, int[] retainRollbackIds); 403 404 /** 405 * Inform apexd that the boot has completed. 406 */ markBootCompleted()407 public abstract void markBootCompleted(); 408 409 /** 410 * Estimate how much storage space is needed on /data/ for decompressing apexes 411 * @param infoList List of apexes that are compressed in target build. 412 * @return Size, in bytes, the amount of space needed on /data/ 413 */ calculateSizeForCompressedApex(CompressedApexInfoList infoList)414 public abstract long calculateSizeForCompressedApex(CompressedApexInfoList infoList) 415 throws RemoteException; 416 417 /** 418 * Reserve space on /data so that apexes can be decompressed after OTA 419 * @param infoList List of apexes that are compressed in target build. 420 */ reserveSpaceForCompressedApex(CompressedApexInfoList infoList)421 public abstract void reserveSpaceForCompressedApex(CompressedApexInfoList infoList) 422 throws RemoteException; 423 424 /** 425 * Performs a non-staged install of the given {@code apexFile}. 426 */ installPackage(File apexFile, PackageParser2 packageParser)427 abstract void installPackage(File apexFile, PackageParser2 packageParser) 428 throws PackageManagerException; 429 430 /** 431 * Get a list of apex system services implemented in an apex. 432 * 433 * <p>The list is sorted by initOrder for consistency. 434 */ getApexSystemServices()435 public abstract List<ApexSystemServiceInfo> getApexSystemServices(); 436 437 /** 438 * Returns an APEX file backing the mount point {@code file} is located on, or {@code null} if 439 * {@code file} doesn't belong to a {@code /apex} mount point. 440 * 441 * <p>Also returns {@code null} if device doesn't support updatable APEX packages. 442 */ 443 @Nullable getBackingApexFile(@onNull File file)444 public abstract File getBackingApexFile(@NonNull File file); 445 446 /** 447 * Dumps various state information to the provided {@link PrintWriter} object. 448 * 449 * @param pw the {@link PrintWriter} object to send information to. 450 * @param packageName a {@link String} containing a package name, or {@code null}. If set, only 451 * information about that specific package will be dumped. 452 */ dump(PrintWriter pw, @Nullable String packageName)453 abstract void dump(PrintWriter pw, @Nullable String packageName); 454 455 @IntDef( 456 flag = true, 457 prefix = { "MATCH_"}, 458 value = {MATCH_ACTIVE_PACKAGE, MATCH_FACTORY_PACKAGE}) 459 @Retention(RetentionPolicy.SOURCE) 460 @interface PackageInfoFlags{} 461 462 /** 463 * An implementation of {@link ApexManager} that should be used in case device supports updating 464 * APEX packages. 465 */ 466 @VisibleForTesting 467 protected static class ApexManagerImpl extends ApexManager { 468 private final Object mLock = new Object(); 469 470 // TODO(ioffe): this should be either List or ArrayMap. 471 @GuardedBy("mLock") 472 private Set<ActiveApexInfo> mActiveApexInfosCache; 473 474 /** 475 * Map of all apex system services to the jar files they are contained in. 476 */ 477 @GuardedBy("mLock") 478 private List<ApexSystemServiceInfo> mApexSystemServices = new ArrayList<>(); 479 480 /** 481 * Contains the list of {@code packageName}s of apks-in-apex for given 482 * {@code apexModuleName}. See {@link #mPackageNameToApexModuleName} to understand the 483 * difference between {@code packageName} and {@code apexModuleName}. 484 */ 485 @GuardedBy("mLock") 486 private ArrayMap<String, List<String>> mApksInApex = new ArrayMap<>(); 487 488 /** 489 * Contains the list of {@code Exception}s that were raised when installing apk-in-apex 490 * inside {@code apexModuleName}. 491 */ 492 @GuardedBy("mLock") 493 private Map<String, String> mErrorWithApkInApex = new ArrayMap<>(); 494 495 @GuardedBy("mLock") 496 private List<PackageInfo> mAllPackagesCache; 497 498 /** 499 * An APEX is a file format that delivers the apex-payload wrapped in an apk container. The 500 * apk container has a reference name, called {@code packageName}, which is found inside the 501 * {@code AndroidManifest.xml}. The apex payload inside the container also has a reference 502 * name, called {@code apexModuleName}, which is found in {@code apex_manifest.json} file. 503 * 504 * {@link #mPackageNameToApexModuleName} contains the mapping from {@code packageName} of 505 * the apk container to {@code apexModuleName} of the apex-payload inside. 506 */ 507 @GuardedBy("mLock") 508 private ArrayMap<String, String> mPackageNameToApexModuleName; 509 510 /** 511 * Reverse mapping of {@link #mPackageNameToApexModuleName}, for active packages only. 512 */ 513 @GuardedBy("mLock") 514 private ArrayMap<String, String> mApexModuleNameToActivePackageName; 515 516 /** 517 * Whether an APEX package is active or not. 518 * 519 * @param packageInfo the package to check 520 * @return {@code true} if this package is active, {@code false} otherwise. 521 */ isActive(PackageInfo packageInfo)522 private static boolean isActive(PackageInfo packageInfo) { 523 return (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) != 0; 524 } 525 526 /** 527 * Retrieve the service from ServiceManager. If the service is not running, it will be 528 * started, and this function will block until it is ready. 529 */ 530 @VisibleForTesting waitForApexService()531 protected IApexService waitForApexService() { 532 // Since apexd is a trusted platform component, synchronized calls are allowable 533 return IApexService.Stub.asInterface( 534 Binder.allowBlocking(ServiceManager.waitForService("apexservice"))); 535 } 536 537 @Override getActiveApexInfos()538 public List<ActiveApexInfo> getActiveApexInfos() { 539 final TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG + "Timing", 540 Trace.TRACE_TAG_PACKAGE_MANAGER); 541 synchronized (mLock) { 542 if (mActiveApexInfosCache == null) { 543 t.traceBegin("getActiveApexInfos_noCache"); 544 try { 545 mActiveApexInfosCache = new ArraySet<>(); 546 final ApexInfo[] activePackages = waitForApexService().getActivePackages(); 547 for (int i = 0; i < activePackages.length; i++) { 548 ApexInfo apexInfo = activePackages[i]; 549 mActiveApexInfosCache.add(new ActiveApexInfo(apexInfo)); 550 } 551 } catch (RemoteException e) { 552 Slog.e(TAG, "Unable to retrieve packages from apexservice", e); 553 } 554 t.traceEnd(); 555 } 556 if (mActiveApexInfosCache != null) { 557 return new ArrayList<>(mActiveApexInfosCache); 558 } else { 559 return Collections.emptyList(); 560 } 561 } 562 } 563 564 @Override scanApexPackagesTraced(@onNull PackageParser2 packageParser, @NonNull ExecutorService executorService)565 void scanApexPackagesTraced(@NonNull PackageParser2 packageParser, 566 @NonNull ExecutorService executorService) { 567 Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanApexPackagesTraced"); 568 try { 569 synchronized (mLock) { 570 scanApexPackagesInternalLocked(packageParser, executorService); 571 } 572 } finally { 573 Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); 574 } 575 } 576 577 @GuardedBy("mLock") scanApexPackagesInternalLocked(PackageParser2 packageParser, ExecutorService executorService)578 private void scanApexPackagesInternalLocked(PackageParser2 packageParser, 579 ExecutorService executorService) { 580 final ApexInfo[] allPkgs; 581 try { 582 mAllPackagesCache = new ArrayList<>(); 583 mPackageNameToApexModuleName = new ArrayMap<>(); 584 mApexModuleNameToActivePackageName = new ArrayMap<>(); 585 allPkgs = waitForApexService().getAllPackages(); 586 } catch (RemoteException re) { 587 Slog.e(TAG, "Unable to retrieve packages from apexservice: " + re.toString()); 588 throw new RuntimeException(re); 589 } 590 if (allPkgs.length == 0) { 591 return; 592 } 593 final int flags = PackageManager.GET_META_DATA 594 | PackageManager.GET_SIGNING_CERTIFICATES 595 | PackageManager.GET_SIGNATURES; 596 ArrayMap<File, ApexInfo> parsingApexInfo = new ArrayMap<>(); 597 ParallelPackageParser parallelPackageParser = 598 new ParallelPackageParser(packageParser, executorService); 599 600 for (ApexInfo ai : allPkgs) { 601 File apexFile = new File(ai.modulePath); 602 parallelPackageParser.submit(apexFile, 603 ParsingPackageUtils.PARSE_COLLECT_CERTIFICATES); 604 parsingApexInfo.put(apexFile, ai); 605 } 606 607 HashSet<String> activePackagesSet = new HashSet<>(); 608 HashSet<String> factoryPackagesSet = new HashSet<>(); 609 // Process results one by one 610 for (int i = 0; i < parsingApexInfo.size(); i++) { 611 ParallelPackageParser.ParseResult parseResult = parallelPackageParser.take(); 612 Throwable throwable = parseResult.throwable; 613 ApexInfo ai = parsingApexInfo.get(parseResult.scanFile); 614 615 if (throwable == null) { 616 // Calling hideAsFinal to assign derived fields for the app info flags. 617 parseResult.parsedPackage.hideAsFinal(); 618 final PackageInfo packageInfo = PackageInfoWithoutStateUtils.generate( 619 parseResult.parsedPackage, ai, flags); 620 if (packageInfo == null) { 621 throw new IllegalStateException("Unable to generate package info: " 622 + ai.modulePath); 623 } 624 mAllPackagesCache.add(packageInfo); 625 for (ParsedApexSystemService service : 626 parseResult.parsedPackage.getApexSystemServices()) { 627 String minSdkVersion = service.getMinSdkVersion(); 628 if (minSdkVersion != null && !UnboundedSdkLevel.isAtLeast(minSdkVersion)) { 629 Slog.d(TAG, String.format( 630 "ApexSystemService %s with min_sdk_version=%s is skipped", 631 service.getName(), service.getMinSdkVersion())); 632 continue; 633 } 634 String maxSdkVersion = service.getMaxSdkVersion(); 635 if (maxSdkVersion != null && !UnboundedSdkLevel.isAtMost(maxSdkVersion)) { 636 Slog.d(TAG, String.format( 637 "ApexSystemService %s with max_sdk_version=%s is skipped", 638 service.getName(), service.getMaxSdkVersion())); 639 continue; 640 } 641 642 if (ai.isActive) { 643 String name = service.getName(); 644 for (int j = 0; j < mApexSystemServices.size(); j++) { 645 ApexSystemServiceInfo info = mApexSystemServices.get(j); 646 if (info.getName().equals(name)) { 647 throw new IllegalStateException(TextUtils.formatSimple( 648 "Duplicate apex-system-service %s from %s, %s", name, 649 info.mJarPath, service.getJarPath())); 650 } 651 } 652 ApexSystemServiceInfo info = new ApexSystemServiceInfo( 653 service.getName(), service.getJarPath(), 654 service.getInitOrder()); 655 mApexSystemServices.add(info); 656 } 657 } 658 Collections.sort(mApexSystemServices); 659 mPackageNameToApexModuleName.put(packageInfo.packageName, ai.moduleName); 660 if (ai.isActive) { 661 if (activePackagesSet.contains(packageInfo.packageName)) { 662 throw new IllegalStateException( 663 "Two active packages have the same name: " 664 + packageInfo.packageName); 665 } 666 activePackagesSet.add(packageInfo.packageName); 667 if (mApexModuleNameToActivePackageName.containsKey(ai.moduleName)) { 668 throw new IllegalStateException( 669 "Two active packages have the same APEX module name: " 670 + ai.moduleName); 671 } 672 mApexModuleNameToActivePackageName.put( 673 ai.moduleName, packageInfo.packageName); 674 } 675 if (ai.isFactory) { 676 // Don't throw when the duplicating APEX is VNDK APEX 677 if (factoryPackagesSet.contains(packageInfo.packageName) 678 && !ai.moduleName.startsWith(VNDK_APEX_MODULE_NAME_PREFIX)) { 679 throw new IllegalStateException( 680 "Two factory packages have the same name: " 681 + packageInfo.packageName); 682 } 683 factoryPackagesSet.add(packageInfo.packageName); 684 } 685 } else if (throwable instanceof PackageManagerException) { 686 final PackageManagerException e = (PackageManagerException) throwable; 687 // Skip parsing non-coreApp apex file if system is in minimal boot state. 688 if (e.error == PackageManager.INSTALL_PARSE_FAILED_ONLY_COREAPP_ALLOWED) { 689 Slog.w(TAG, "Scan apex failed, not a coreApp:" + ai.modulePath); 690 continue; 691 } 692 throw new IllegalStateException("Unable to parse: " + ai.modulePath, throwable); 693 } else { 694 throw new IllegalStateException("Unexpected exception occurred while parsing " 695 + ai.modulePath, throwable); 696 } 697 } 698 } 699 700 @Override 701 @Nullable getPackageInfo(String packageName, @PackageInfoFlags int flags)702 public PackageInfo getPackageInfo(String packageName, @PackageInfoFlags int flags) { 703 synchronized (mLock) { 704 Preconditions.checkState(mAllPackagesCache != null, 705 "APEX packages have not been scanned"); 706 boolean matchActive = (flags & MATCH_ACTIVE_PACKAGE) != 0; 707 boolean matchFactory = (flags & MATCH_FACTORY_PACKAGE) != 0; 708 for (int i = 0, size = mAllPackagesCache.size(); i < size; i++) { 709 final PackageInfo packageInfo = mAllPackagesCache.get(i); 710 if (!packageInfo.packageName.equals(packageName)) { 711 continue; 712 } 713 if ((matchActive && isActive(packageInfo)) 714 || (matchFactory && isFactory(packageInfo))) { 715 return packageInfo; 716 } 717 } 718 return null; 719 } 720 } 721 722 @Override getActivePackages()723 List<PackageInfo> getActivePackages() { 724 synchronized (mLock) { 725 Preconditions.checkState(mAllPackagesCache != null, 726 "APEX packages have not been scanned"); 727 final List<PackageInfo> activePackages = new ArrayList<>(); 728 for (int i = 0; i < mAllPackagesCache.size(); i++) { 729 final PackageInfo packageInfo = mAllPackagesCache.get(i); 730 if (isActive(packageInfo)) { 731 activePackages.add(packageInfo); 732 } 733 } 734 return activePackages; 735 } 736 } 737 738 @Override getFactoryPackages()739 List<PackageInfo> getFactoryPackages() { 740 synchronized (mLock) { 741 Preconditions.checkState(mAllPackagesCache != null, 742 "APEX packages have not been scanned"); 743 final List<PackageInfo> factoryPackages = new ArrayList<>(); 744 for (int i = 0; i < mAllPackagesCache.size(); i++) { 745 final PackageInfo packageInfo = mAllPackagesCache.get(i); 746 if (isFactory(packageInfo)) { 747 factoryPackages.add(packageInfo); 748 } 749 } 750 return factoryPackages; 751 } 752 } 753 754 @Override getInactivePackages()755 List<PackageInfo> getInactivePackages() { 756 synchronized (mLock) { 757 Preconditions.checkState(mAllPackagesCache != null, 758 "APEX packages have not been scanned"); 759 final List<PackageInfo> inactivePackages = new ArrayList<>(); 760 for (int i = 0; i < mAllPackagesCache.size(); i++) { 761 final PackageInfo packageInfo = mAllPackagesCache.get(i); 762 if (!isActive(packageInfo)) { 763 inactivePackages.add(packageInfo); 764 } 765 } 766 return inactivePackages; 767 } 768 } 769 770 @Override isApexPackage(String packageName)771 boolean isApexPackage(String packageName) { 772 if (!isApexSupported()) return false; 773 synchronized (mLock) { 774 Preconditions.checkState(mAllPackagesCache != null, 775 "APEX packages have not been scanned"); 776 for (int i = 0, size = mAllPackagesCache.size(); i < size; i++) { 777 final PackageInfo packageInfo = mAllPackagesCache.get(i); 778 if (packageInfo.packageName.equals(packageName)) { 779 return true; 780 } 781 } 782 } 783 return false; 784 } 785 786 @Override 787 @Nullable getActiveApexPackageNameContainingPackage(String containedPackageName)788 public String getActiveApexPackageNameContainingPackage(String containedPackageName) { 789 Objects.requireNonNull(containedPackageName); 790 synchronized (mLock) { 791 Preconditions.checkState(mPackageNameToApexModuleName != null, 792 "APEX packages have not been scanned"); 793 int numApksInApex = mApksInApex.size(); 794 for (int apkInApexNum = 0; apkInApexNum < numApksInApex; apkInApexNum++) { 795 if (mApksInApex.valueAt(apkInApexNum).contains(containedPackageName)) { 796 String apexModuleName = mApksInApex.keyAt(apkInApexNum); 797 798 int numApexPkgs = mPackageNameToApexModuleName.size(); 799 for (int apexPkgNum = 0; apexPkgNum < numApexPkgs; apexPkgNum++) { 800 if (mPackageNameToApexModuleName.valueAt(apexPkgNum).equals( 801 apexModuleName)) { 802 return mPackageNameToApexModuleName.keyAt(apexPkgNum); 803 } 804 } 805 } 806 } 807 } 808 809 return null; 810 } 811 812 @Override getStagedSessionInfo(int sessionId)813 @Nullable ApexSessionInfo getStagedSessionInfo(int sessionId) { 814 try { 815 ApexSessionInfo apexSessionInfo = 816 waitForApexService().getStagedSessionInfo(sessionId); 817 if (apexSessionInfo.isUnknown) { 818 return null; 819 } 820 return apexSessionInfo; 821 } catch (RemoteException re) { 822 Slog.e(TAG, "Unable to contact apexservice", re); 823 throw new RuntimeException(re); 824 } 825 } 826 827 @Override getSessions()828 SparseArray<ApexSessionInfo> getSessions() { 829 try { 830 final ApexSessionInfo[] sessions = waitForApexService().getSessions(); 831 final SparseArray<ApexSessionInfo> result = new SparseArray<>(sessions.length); 832 for (int i = 0; i < sessions.length; i++) { 833 result.put(sessions[i].sessionId, sessions[i]); 834 } 835 return result; 836 } catch (RemoteException re) { 837 Slog.e(TAG, "Unable to contact apexservice", re); 838 throw new RuntimeException(re); 839 } 840 } 841 842 @Override submitStagedSession(ApexSessionParams params)843 ApexInfoList submitStagedSession(ApexSessionParams params) throws PackageManagerException { 844 try { 845 final ApexInfoList apexInfoList = new ApexInfoList(); 846 waitForApexService().submitStagedSession(params, apexInfoList); 847 return apexInfoList; 848 } catch (RemoteException re) { 849 Slog.e(TAG, "Unable to contact apexservice", re); 850 throw new RuntimeException(re); 851 } catch (Exception e) { 852 throw new PackageManagerException( 853 PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE, 854 "apexd verification failed : " + e.getMessage()); 855 } 856 } 857 858 @Override getStagedApexInfos(ApexSessionParams params)859 ApexInfo[] getStagedApexInfos(ApexSessionParams params) { 860 try { 861 return waitForApexService().getStagedApexInfos(params); 862 } catch (RemoteException re) { 863 Slog.w(TAG, "Unable to contact apexservice" + re.getMessage()); 864 throw new RuntimeException(re); 865 } catch (Exception e) { 866 Slog.w(TAG, "Failed to collect staged apex infos" + e.getMessage()); 867 return new ApexInfo[0]; 868 } 869 } 870 871 @Override markStagedSessionReady(int sessionId)872 void markStagedSessionReady(int sessionId) throws PackageManagerException { 873 try { 874 waitForApexService().markStagedSessionReady(sessionId); 875 } catch (RemoteException re) { 876 Slog.e(TAG, "Unable to contact apexservice", re); 877 throw new RuntimeException(re); 878 } catch (Exception e) { 879 throw new PackageManagerException( 880 PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE, 881 "Failed to mark apexd session as ready : " + e.getMessage()); 882 } 883 } 884 885 @Override markStagedSessionSuccessful(int sessionId)886 void markStagedSessionSuccessful(int sessionId) { 887 try { 888 waitForApexService().markStagedSessionSuccessful(sessionId); 889 } catch (RemoteException re) { 890 Slog.e(TAG, "Unable to contact apexservice", re); 891 throw new RuntimeException(re); 892 } catch (Exception e) { 893 // It is fine to just log an exception in this case. APEXd will be able to recover 894 // in case markStagedSessionSuccessful fails. 895 Slog.e(TAG, "Failed to mark session " + sessionId + " as successful", e); 896 } 897 } 898 899 @Override isApexSupported()900 boolean isApexSupported() { 901 return true; 902 } 903 904 @Override revertActiveSessions()905 boolean revertActiveSessions() { 906 try { 907 waitForApexService().revertActiveSessions(); 908 return true; 909 } catch (RemoteException re) { 910 Slog.e(TAG, "Unable to contact apexservice", re); 911 return false; 912 } catch (Exception e) { 913 Slog.e(TAG, e.getMessage(), e); 914 return false; 915 } 916 } 917 918 @Override abortStagedSession(int sessionId)919 boolean abortStagedSession(int sessionId) { 920 try { 921 waitForApexService().abortStagedSession(sessionId); 922 return true; 923 } catch (Exception e) { 924 Slog.e(TAG, e.getMessage(), e); 925 return false; 926 } 927 } 928 929 @Override uninstallApex(String apexPackagePath)930 boolean uninstallApex(String apexPackagePath) { 931 try { 932 waitForApexService().unstagePackages(Collections.singletonList(apexPackagePath)); 933 return true; 934 } catch (Exception e) { 935 return false; 936 } 937 } 938 939 @Override registerApkInApex(AndroidPackage pkg)940 void registerApkInApex(AndroidPackage pkg) { 941 synchronized (mLock) { 942 for (ActiveApexInfo aai : mActiveApexInfosCache) { 943 if (pkg.getBaseApkPath().startsWith( 944 aai.apexDirectory.getAbsolutePath() + File.separator)) { 945 List<String> apks = mApksInApex.get(aai.apexModuleName); 946 if (apks == null) { 947 apks = Lists.newArrayList(); 948 mApksInApex.put(aai.apexModuleName, apks); 949 } 950 Slog.i(TAG, "Registering " + pkg.getPackageName() + " as apk-in-apex of " 951 + aai.apexModuleName); 952 apks.add(pkg.getPackageName()); 953 } 954 } 955 } 956 } 957 958 @Override reportErrorWithApkInApex(String scanDirPath, String errorMsg)959 void reportErrorWithApkInApex(String scanDirPath, String errorMsg) { 960 synchronized (mLock) { 961 for (ActiveApexInfo aai : mActiveApexInfosCache) { 962 if (scanDirPath.startsWith(aai.apexDirectory.getAbsolutePath())) { 963 mErrorWithApkInApex.put(aai.apexModuleName, errorMsg); 964 } 965 } 966 } 967 } 968 969 @Override 970 @Nullable getApkInApexInstallError(String apexPackageName)971 String getApkInApexInstallError(String apexPackageName) { 972 synchronized (mLock) { 973 Preconditions.checkState(mPackageNameToApexModuleName != null, 974 "APEX packages have not been scanned"); 975 String moduleName = mPackageNameToApexModuleName.get(apexPackageName); 976 if (moduleName == null) { 977 return null; 978 } 979 return mErrorWithApkInApex.get(moduleName); 980 } 981 } 982 983 @Override getApksInApex(String apexPackageName)984 List<String> getApksInApex(String apexPackageName) { 985 synchronized (mLock) { 986 Preconditions.checkState(mPackageNameToApexModuleName != null, 987 "APEX packages have not been scanned"); 988 String moduleName = mPackageNameToApexModuleName.get(apexPackageName); 989 if (moduleName == null) { 990 return Collections.emptyList(); 991 } 992 return mApksInApex.getOrDefault(moduleName, Collections.emptyList()); 993 } 994 } 995 996 @Override 997 @Nullable getApexModuleNameForPackageName(String apexPackageName)998 public String getApexModuleNameForPackageName(String apexPackageName) { 999 synchronized (mLock) { 1000 Preconditions.checkState(mPackageNameToApexModuleName != null, 1001 "APEX packages have not been scanned"); 1002 return mPackageNameToApexModuleName.get(apexPackageName); 1003 } 1004 } 1005 1006 @Override 1007 @Nullable getActivePackageNameForApexModuleName(String apexModuleName)1008 public String getActivePackageNameForApexModuleName(String apexModuleName) { 1009 synchronized (mLock) { 1010 Preconditions.checkState(mApexModuleNameToActivePackageName != null, 1011 "APEX packages have not been scanned"); 1012 return mApexModuleNameToActivePackageName.get(apexModuleName); 1013 } 1014 } 1015 1016 @Override snapshotCeData(int userId, int rollbackId, String apexPackageName)1017 public boolean snapshotCeData(int userId, int rollbackId, String apexPackageName) { 1018 String apexModuleName; 1019 synchronized (mLock) { 1020 Preconditions.checkState(mPackageNameToApexModuleName != null, 1021 "APEX packages have not been scanned"); 1022 apexModuleName = mPackageNameToApexModuleName.get(apexPackageName); 1023 } 1024 if (apexModuleName == null) { 1025 Slog.e(TAG, "Invalid apex package name: " + apexPackageName); 1026 return false; 1027 } 1028 try { 1029 waitForApexService().snapshotCeData(userId, rollbackId, apexModuleName); 1030 return true; 1031 } catch (Exception e) { 1032 Slog.e(TAG, e.getMessage(), e); 1033 return false; 1034 } 1035 } 1036 1037 @Override restoreCeData(int userId, int rollbackId, String apexPackageName)1038 public boolean restoreCeData(int userId, int rollbackId, String apexPackageName) { 1039 String apexModuleName; 1040 synchronized (mLock) { 1041 Preconditions.checkState(mPackageNameToApexModuleName != null, 1042 "APEX packages have not been scanned"); 1043 apexModuleName = mPackageNameToApexModuleName.get(apexPackageName); 1044 } 1045 if (apexModuleName == null) { 1046 Slog.e(TAG, "Invalid apex package name: " + apexPackageName); 1047 return false; 1048 } 1049 try { 1050 waitForApexService().restoreCeData(userId, rollbackId, apexModuleName); 1051 return true; 1052 } catch (Exception e) { 1053 Slog.e(TAG, e.getMessage(), e); 1054 return false; 1055 } 1056 } 1057 1058 @Override destroyDeSnapshots(int rollbackId)1059 public boolean destroyDeSnapshots(int rollbackId) { 1060 try { 1061 waitForApexService().destroyDeSnapshots(rollbackId); 1062 return true; 1063 } catch (Exception e) { 1064 Slog.e(TAG, e.getMessage(), e); 1065 return false; 1066 } 1067 } 1068 1069 @Override destroyCeSnapshots(int userId, int rollbackId)1070 public boolean destroyCeSnapshots(int userId, int rollbackId) { 1071 try { 1072 waitForApexService().destroyCeSnapshots(userId, rollbackId); 1073 return true; 1074 } catch (Exception e) { 1075 Slog.e(TAG, e.getMessage(), e); 1076 return false; 1077 } 1078 } 1079 1080 @Override destroyCeSnapshotsNotSpecified(int userId, int[] retainRollbackIds)1081 public boolean destroyCeSnapshotsNotSpecified(int userId, int[] retainRollbackIds) { 1082 try { 1083 waitForApexService().destroyCeSnapshotsNotSpecified(userId, retainRollbackIds); 1084 return true; 1085 } catch (Exception e) { 1086 Slog.e(TAG, e.getMessage(), e); 1087 return false; 1088 } 1089 } 1090 1091 @Override markBootCompleted()1092 public void markBootCompleted() { 1093 try { 1094 waitForApexService().markBootCompleted(); 1095 } catch (RemoteException re) { 1096 Slog.e(TAG, "Unable to contact apexservice", re); 1097 } 1098 } 1099 1100 @Override calculateSizeForCompressedApex(CompressedApexInfoList infoList)1101 public long calculateSizeForCompressedApex(CompressedApexInfoList infoList) 1102 throws RemoteException { 1103 return waitForApexService().calculateSizeForCompressedApex(infoList); 1104 } 1105 1106 @Override reserveSpaceForCompressedApex(CompressedApexInfoList infoList)1107 public void reserveSpaceForCompressedApex(CompressedApexInfoList infoList) 1108 throws RemoteException { 1109 waitForApexService().reserveSpaceForCompressedApex(infoList); 1110 } 1111 getSigningDetails(PackageInfo pkg)1112 private SigningDetails getSigningDetails(PackageInfo pkg) throws PackageManagerException { 1113 final int minSignatureScheme = 1114 ApkSignatureVerifier.getMinimumSignatureSchemeVersionForTargetSdk( 1115 pkg.applicationInfo.targetSdkVersion); 1116 final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing(); 1117 final ParseResult<SigningDetails> result = ApkSignatureVerifier.verify( 1118 input, pkg.applicationInfo.sourceDir, minSignatureScheme); 1119 if (result.isError()) { 1120 throw new PackageManagerException(result.getErrorCode(), result.getErrorMessage(), 1121 result.getException()); 1122 } 1123 return result.getResult(); 1124 } 1125 checkApexSignature(PackageInfo existingApexPkg, PackageInfo newApexPkg)1126 private void checkApexSignature(PackageInfo existingApexPkg, PackageInfo newApexPkg) 1127 throws PackageManagerException { 1128 final SigningDetails existingSigningDetails = getSigningDetails(existingApexPkg); 1129 final SigningDetails newSigningDetails = getSigningDetails(newApexPkg); 1130 if (!newSigningDetails.checkCapability(existingSigningDetails, 1131 SigningDetails.CertCapabilities.INSTALLED_DATA)) { 1132 throw new PackageManagerException(PackageManager.INSTALL_FAILED_BAD_SIGNATURE, 1133 "APK container signature of " + newApexPkg.applicationInfo.sourceDir 1134 + " is not compatible with currently installed on device"); 1135 } 1136 } 1137 1138 @Override installPackage(File apexFile, PackageParser2 packageParser)1139 void installPackage(File apexFile, PackageParser2 packageParser) 1140 throws PackageManagerException { 1141 try { 1142 final int flags = PackageManager.GET_META_DATA 1143 | PackageManager.GET_SIGNING_CERTIFICATES 1144 | PackageManager.GET_SIGNATURES; 1145 final ParsedPackage parsedPackage = packageParser.parsePackage( 1146 apexFile, flags, /* useCaches= */ false); 1147 final PackageInfo newApexPkg = PackageInfoWithoutStateUtils.generate(parsedPackage, 1148 /* apexInfo= */ null, flags); 1149 if (newApexPkg == null) { 1150 throw new PackageManagerException(PackageManager.INSTALL_FAILED_INVALID_APK, 1151 "Failed to generate package info for " + apexFile.getAbsolutePath()); 1152 } 1153 final PackageInfo existingApexPkg = getPackageInfo(newApexPkg.packageName, 1154 MATCH_ACTIVE_PACKAGE); 1155 if (existingApexPkg == null) { 1156 Slog.w(TAG, "Attempting to install new APEX package " + newApexPkg.packageName); 1157 throw new PackageManagerException(PackageManager.INSTALL_FAILED_PACKAGE_CHANGED, 1158 "It is forbidden to install new APEX packages"); 1159 } 1160 checkApexSignature(existingApexPkg, newApexPkg); 1161 ApexInfo apexInfo = waitForApexService().installAndActivatePackage( 1162 apexFile.getAbsolutePath()); 1163 final ParsedPackage parsedPackage2 = packageParser.parsePackage( 1164 new File(apexInfo.modulePath), flags, /* useCaches= */ false); 1165 final PackageInfo finalApexPkg = PackageInfoWithoutStateUtils.generate( 1166 parsedPackage2, apexInfo, flags); 1167 // Installation was successful, time to update mAllPackagesCache 1168 synchronized (mLock) { 1169 if (isFactory(existingApexPkg)) { 1170 existingApexPkg.applicationInfo.flags &= ~ApplicationInfo.FLAG_INSTALLED; 1171 mAllPackagesCache.add(finalApexPkg); 1172 } else { 1173 for (int i = 0, size = mAllPackagesCache.size(); i < size; i++) { 1174 if (mAllPackagesCache.get(i).equals(existingApexPkg)) { 1175 mAllPackagesCache.set(i, finalApexPkg); 1176 break; 1177 } 1178 } 1179 } 1180 } 1181 } catch (RemoteException e) { 1182 throw new PackageManagerException(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, 1183 "apexservice not available"); 1184 } catch (PackageManagerException e) { 1185 // Catching it in order not to fall back to Exception which rethrows the 1186 // PackageManagerException with a common error code. 1187 throw e; 1188 } catch (Exception e) { 1189 // TODO(b/187864524): is INSTALL_FAILED_INTERNAL_ERROR is the right error code here? 1190 throw new PackageManagerException(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, 1191 e.getMessage()); 1192 } 1193 } 1194 1195 @Override getApexSystemServices()1196 public List<ApexSystemServiceInfo> getApexSystemServices() { 1197 synchronized (mLock) { 1198 Preconditions.checkState(mApexSystemServices != null, 1199 "APEX packages have not been scanned"); 1200 return mApexSystemServices; 1201 } 1202 } 1203 1204 @Override getBackingApexFile(File file)1205 public File getBackingApexFile(File file) { 1206 Path path = file.toPath(); 1207 if (!path.startsWith(Environment.getApexDirectory().toPath())) { 1208 return null; 1209 } 1210 if (path.getNameCount() < 2) { 1211 return null; 1212 } 1213 String moduleName = file.toPath().getName(1).toString(); 1214 final List<ActiveApexInfo> apexes = getActiveApexInfos(); 1215 for (int i = 0; i < apexes.size(); i++) { 1216 if (apexes.get(i).apexModuleName.equals(moduleName)) { 1217 return apexes.get(i).apexFile; 1218 } 1219 } 1220 return null; 1221 } 1222 1223 /** 1224 * Dump information about the packages contained in a particular cache 1225 * @param packagesCache the cache to print information about. 1226 * @param packageName a {@link String} containing a package name, or {@code null}. If set, 1227 * only information about that specific package will be dumped. 1228 * @param ipw the {@link IndentingPrintWriter} object to send information to. 1229 */ dumpFromPackagesCache( List<PackageInfo> packagesCache, @Nullable String packageName, IndentingPrintWriter ipw)1230 void dumpFromPackagesCache( 1231 List<PackageInfo> packagesCache, 1232 @Nullable String packageName, 1233 IndentingPrintWriter ipw) { 1234 ipw.println(); 1235 ipw.increaseIndent(); 1236 for (int i = 0, size = packagesCache.size(); i < size; i++) { 1237 final PackageInfo pi = packagesCache.get(i); 1238 if (packageName != null && !packageName.equals(pi.packageName)) { 1239 continue; 1240 } 1241 ipw.println(pi.packageName); 1242 ipw.increaseIndent(); 1243 ipw.println("Version: " + pi.versionCode); 1244 ipw.println("Path: " + pi.applicationInfo.sourceDir); 1245 ipw.println("IsActive: " + isActive(pi)); 1246 ipw.println("IsFactory: " + isFactory(pi)); 1247 ipw.println("ApplicationInfo: "); 1248 ipw.increaseIndent(); 1249 pi.applicationInfo.dump(new PrintWriterPrinter(ipw), ""); 1250 ipw.decreaseIndent(); 1251 ipw.decreaseIndent(); 1252 } 1253 ipw.decreaseIndent(); 1254 ipw.println(); 1255 } 1256 1257 @Override dump(PrintWriter pw, @Nullable String packageName)1258 void dump(PrintWriter pw, @Nullable String packageName) { 1259 final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ", 120); 1260 try { 1261 ipw.println(); 1262 ipw.println("APEX session state:"); 1263 ipw.increaseIndent(); 1264 final ApexSessionInfo[] sessions = waitForApexService().getSessions(); 1265 for (ApexSessionInfo si : sessions) { 1266 ipw.println("Session ID: " + si.sessionId); 1267 ipw.increaseIndent(); 1268 if (si.isUnknown) { 1269 ipw.println("State: UNKNOWN"); 1270 } else if (si.isVerified) { 1271 ipw.println("State: VERIFIED"); 1272 } else if (si.isStaged) { 1273 ipw.println("State: STAGED"); 1274 } else if (si.isActivated) { 1275 ipw.println("State: ACTIVATED"); 1276 } else if (si.isActivationFailed) { 1277 ipw.println("State: ACTIVATION FAILED"); 1278 } else if (si.isSuccess) { 1279 ipw.println("State: SUCCESS"); 1280 } else if (si.isRevertInProgress) { 1281 ipw.println("State: REVERT IN PROGRESS"); 1282 } else if (si.isReverted) { 1283 ipw.println("State: REVERTED"); 1284 } else if (si.isRevertFailed) { 1285 ipw.println("State: REVERT FAILED"); 1286 } 1287 ipw.decreaseIndent(); 1288 } 1289 ipw.decreaseIndent(); 1290 ipw.println(); 1291 synchronized (mLock) { 1292 if (mAllPackagesCache == null) { 1293 ipw.println("APEX packages have not been scanned"); 1294 return; 1295 } 1296 } 1297 ipw.println("Active APEX packages:"); 1298 dumpFromPackagesCache(getActivePackages(), packageName, ipw); 1299 ipw.println("Inactive APEX packages:"); 1300 dumpFromPackagesCache(getInactivePackages(), packageName, ipw); 1301 ipw.println("Factory APEX packages:"); 1302 dumpFromPackagesCache(getFactoryPackages(), packageName, ipw); 1303 } catch (RemoteException e) { 1304 ipw.println("Couldn't communicate with apexd."); 1305 } 1306 } 1307 } 1308 1309 /** 1310 * An implementation of {@link ApexManager} that should be used in case device does not support 1311 * updating APEX packages. 1312 */ 1313 @VisibleForTesting 1314 static final class ApexManagerFlattenedApex extends ApexManager { 1315 @Override getActiveApexInfos()1316 public List<ActiveApexInfo> getActiveApexInfos() { 1317 // There is no apexd running in case of flattened apex 1318 // We look up the /apex directory and identify the active APEX modules from there. 1319 // As "preinstalled" path, we just report /system since in the case of flattened APEX 1320 // the /apex directory is just a symlink to /system/apex. 1321 List<ActiveApexInfo> result = new ArrayList<>(); 1322 File apexDir = Environment.getApexDirectory(); 1323 if (apexDir.isDirectory()) { 1324 File[] files = apexDir.listFiles(); 1325 // listFiles might be null if system server doesn't have permission to read 1326 // a directory. 1327 if (files != null) { 1328 for (File file : files) { 1329 if (file.isDirectory() && !file.getName().contains("@") 1330 // In flattened configuration, init special-cases the art directory 1331 // and bind-mounts com.android.art.debug to com.android.art. 1332 && !file.getName().equals("com.android.art.debug")) { 1333 result.add( 1334 new ActiveApexInfo(file, Environment.getRootDirectory(), file)); 1335 } 1336 } 1337 } 1338 } 1339 return result; 1340 } 1341 1342 @Override scanApexPackagesTraced(@onNull PackageParser2 packageParser, @NonNull ExecutorService executorService)1343 void scanApexPackagesTraced(@NonNull PackageParser2 packageParser, 1344 @NonNull ExecutorService executorService) { 1345 // No-op 1346 } 1347 1348 @Override getPackageInfo(String packageName, int flags)1349 public PackageInfo getPackageInfo(String packageName, int flags) { 1350 return null; 1351 } 1352 1353 @Override getActivePackages()1354 List<PackageInfo> getActivePackages() { 1355 return Collections.emptyList(); 1356 } 1357 1358 @Override getFactoryPackages()1359 List<PackageInfo> getFactoryPackages() { 1360 return Collections.emptyList(); 1361 } 1362 1363 @Override getInactivePackages()1364 List<PackageInfo> getInactivePackages() { 1365 return Collections.emptyList(); 1366 } 1367 1368 @Override isApexPackage(String packageName)1369 boolean isApexPackage(String packageName) { 1370 return false; 1371 } 1372 1373 @Override 1374 @Nullable getActiveApexPackageNameContainingPackage( @onNull String containedPackageName)1375 public String getActiveApexPackageNameContainingPackage( 1376 @NonNull String containedPackageName) { 1377 Objects.requireNonNull(containedPackageName); 1378 1379 return null; 1380 } 1381 1382 @Override getStagedSessionInfo(int sessionId)1383 ApexSessionInfo getStagedSessionInfo(int sessionId) { 1384 throw new UnsupportedOperationException(); 1385 } 1386 1387 @Override getSessions()1388 SparseArray<ApexSessionInfo> getSessions() { 1389 return new SparseArray<>(0); 1390 } 1391 1392 @Override submitStagedSession(ApexSessionParams params)1393 ApexInfoList submitStagedSession(ApexSessionParams params) 1394 throws PackageManagerException { 1395 throw new PackageManagerException(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, 1396 "Device doesn't support updating APEX"); 1397 } 1398 1399 @Override getStagedApexInfos(ApexSessionParams params)1400 ApexInfo[] getStagedApexInfos(ApexSessionParams params) { 1401 throw new UnsupportedOperationException(); 1402 } 1403 1404 @Override markStagedSessionReady(int sessionId)1405 void markStagedSessionReady(int sessionId) { 1406 throw new UnsupportedOperationException(); 1407 } 1408 1409 @Override markStagedSessionSuccessful(int sessionId)1410 void markStagedSessionSuccessful(int sessionId) { 1411 throw new UnsupportedOperationException(); 1412 } 1413 1414 @Override isApexSupported()1415 boolean isApexSupported() { 1416 return false; 1417 } 1418 1419 @Override revertActiveSessions()1420 boolean revertActiveSessions() { 1421 throw new UnsupportedOperationException(); 1422 } 1423 1424 @Override abortStagedSession(int sessionId)1425 boolean abortStagedSession(int sessionId) { 1426 throw new UnsupportedOperationException(); 1427 } 1428 1429 @Override uninstallApex(String apexPackagePath)1430 boolean uninstallApex(String apexPackagePath) { 1431 throw new UnsupportedOperationException(); 1432 } 1433 1434 @Override registerApkInApex(AndroidPackage pkg)1435 void registerApkInApex(AndroidPackage pkg) { 1436 // No-op 1437 } 1438 1439 @Override reportErrorWithApkInApex(String scanDirPath, String errorMsg)1440 void reportErrorWithApkInApex(String scanDirPath, String errorMsg) { 1441 // No-op 1442 } 1443 1444 @Override 1445 @Nullable getApkInApexInstallError(String apexPackageName)1446 String getApkInApexInstallError(String apexPackageName) { 1447 return null; 1448 } 1449 1450 @Override getApksInApex(String apexPackageName)1451 List<String> getApksInApex(String apexPackageName) { 1452 return Collections.emptyList(); 1453 } 1454 1455 @Override 1456 @Nullable getApexModuleNameForPackageName(String apexPackageName)1457 public String getApexModuleNameForPackageName(String apexPackageName) { 1458 return null; 1459 } 1460 1461 @Override 1462 @Nullable getActivePackageNameForApexModuleName(String apexModuleName)1463 public String getActivePackageNameForApexModuleName(String apexModuleName) { 1464 return null; 1465 } 1466 1467 @Override snapshotCeData(int userId, int rollbackId, String apexPackageName)1468 public boolean snapshotCeData(int userId, int rollbackId, String apexPackageName) { 1469 throw new UnsupportedOperationException(); 1470 } 1471 1472 @Override restoreCeData(int userId, int rollbackId, String apexPackageName)1473 public boolean restoreCeData(int userId, int rollbackId, String apexPackageName) { 1474 throw new UnsupportedOperationException(); 1475 } 1476 1477 @Override destroyDeSnapshots(int rollbackId)1478 public boolean destroyDeSnapshots(int rollbackId) { 1479 throw new UnsupportedOperationException(); 1480 } 1481 1482 @Override destroyCeSnapshots(int userId, int rollbackId)1483 public boolean destroyCeSnapshots(int userId, int rollbackId) { 1484 return true; 1485 } 1486 1487 @Override destroyCeSnapshotsNotSpecified(int userId, int[] retainRollbackIds)1488 public boolean destroyCeSnapshotsNotSpecified(int userId, int[] retainRollbackIds) { 1489 return true; 1490 } 1491 1492 @Override markBootCompleted()1493 public void markBootCompleted() { 1494 // No-op 1495 } 1496 1497 @Override calculateSizeForCompressedApex(CompressedApexInfoList infoList)1498 public long calculateSizeForCompressedApex(CompressedApexInfoList infoList) { 1499 throw new UnsupportedOperationException(); 1500 } 1501 1502 @Override reserveSpaceForCompressedApex(CompressedApexInfoList infoList)1503 public void reserveSpaceForCompressedApex(CompressedApexInfoList infoList) { 1504 throw new UnsupportedOperationException(); 1505 } 1506 1507 @Override installPackage(File apexFile, PackageParser2 packageParser)1508 void installPackage(File apexFile, PackageParser2 packageParser) { 1509 throw new UnsupportedOperationException("APEX updates are not supported"); 1510 } 1511 1512 @Override getApexSystemServices()1513 public List<ApexSystemServiceInfo> getApexSystemServices() { 1514 // TODO(satayev): we can't really support flattened apex use case, and need to migrate 1515 // the manifest entries into system's manifest asap. 1516 return Collections.emptyList(); 1517 } 1518 1519 @Override getBackingApexFile(File file)1520 public File getBackingApexFile(File file) { 1521 return null; 1522 } 1523 1524 @Override dump(PrintWriter pw, String packageName)1525 void dump(PrintWriter pw, String packageName) { 1526 // No-op 1527 } 1528 } 1529 } 1530