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 android.annotation.IntDef; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.apex.ApexInfo; 23 import android.apex.ApexInfoList; 24 import android.apex.ApexSessionInfo; 25 import android.apex.ApexSessionParams; 26 import android.apex.CompressedApexInfoList; 27 import android.apex.IApexService; 28 import android.content.pm.PackageInfo; 29 import android.content.pm.PackageManager; 30 import android.content.pm.SigningDetails; 31 import android.content.pm.parsing.result.ParseResult; 32 import android.content.pm.parsing.result.ParseTypeImpl; 33 import android.os.Binder; 34 import android.os.Environment; 35 import android.os.RemoteException; 36 import android.os.ServiceManager; 37 import android.os.Trace; 38 import android.sysprop.ApexProperties; 39 import android.text.TextUtils; 40 import android.util.ArrayMap; 41 import android.util.ArraySet; 42 import android.util.Singleton; 43 import android.util.Slog; 44 import android.util.SparseArray; 45 import android.util.apk.ApkSignatureVerifier; 46 47 import com.android.internal.annotations.GuardedBy; 48 import com.android.internal.annotations.VisibleForTesting; 49 import com.android.internal.util.IndentingPrintWriter; 50 import com.android.internal.util.Preconditions; 51 import com.android.modules.utils.build.UnboundedSdkLevel; 52 import com.android.server.pm.pkg.AndroidPackage; 53 import com.android.server.pm.pkg.component.ParsedApexSystemService; 54 import com.android.server.utils.TimingsTraceAndSlog; 55 56 import com.google.android.collect.Lists; 57 58 import java.io.File; 59 import java.io.PrintWriter; 60 import java.lang.annotation.Retention; 61 import java.lang.annotation.RetentionPolicy; 62 import java.nio.file.Path; 63 import java.util.ArrayList; 64 import java.util.Collections; 65 import java.util.List; 66 import java.util.Map; 67 import java.util.Objects; 68 import java.util.Set; 69 70 /** 71 * ApexManager class handles communications with the apex service to perform operation and queries, 72 * as well as providing caching to avoid unnecessary calls to the service. 73 */ 74 public abstract class ApexManager { 75 76 private static final String TAG = "ApexManager"; 77 78 public static final int MATCH_ACTIVE_PACKAGE = 1 << 0; 79 static final int MATCH_FACTORY_PACKAGE = 1 << 1; 80 81 private static final Singleton<ApexManager> sApexManagerSingleton = 82 new Singleton<ApexManager>() { 83 @Override 84 protected ApexManager create() { 85 if (ApexProperties.updatable().orElse(false)) { 86 return new ApexManagerImpl(); 87 } else { 88 return new ApexManagerFlattenedApex(); 89 } 90 } 91 }; 92 93 /** 94 * Returns an instance of either {@link ApexManagerImpl} or {@link ApexManagerFlattenedApex} 95 * depending on whether this device supports APEX, i.e. {@link ApexProperties#updatable()} 96 * evaluates to {@code true}. 97 * @hide 98 */ getInstance()99 public static ApexManager getInstance() { 100 return sApexManagerSingleton.get(); 101 } 102 103 static class ScanResult { 104 public final ApexInfo apexInfo; 105 public final AndroidPackage pkg; 106 public final String packageName; ScanResult(ApexInfo apexInfo, AndroidPackage pkg, String packageName)107 ScanResult(ApexInfo apexInfo, AndroidPackage pkg, String packageName) { 108 this.apexInfo = apexInfo; 109 this.pkg = pkg; 110 this.packageName = packageName; 111 } 112 } 113 114 /** 115 * Minimal information about APEX mount points and the original APEX package they refer to. 116 * @hide 117 */ 118 public static class ActiveApexInfo { 119 @Nullable public final String apexModuleName; 120 public final File apexDirectory; 121 public final File preInstalledApexPath; 122 public final boolean isFactory; 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, true, apexFile, false); 128 } 129 ActiveApexInfo(@ullable String apexModuleName, File apexDirectory, File preInstalledApexPath, boolean isFactory, File apexFile, boolean activeApexChanged)130 private ActiveApexInfo(@Nullable String apexModuleName, File apexDirectory, 131 File preInstalledApexPath, boolean isFactory, File apexFile, 132 boolean activeApexChanged) { 133 this.apexModuleName = apexModuleName; 134 this.apexDirectory = apexDirectory; 135 this.preInstalledApexPath = preInstalledApexPath; 136 this.isFactory = isFactory; 137 this.apexFile = apexFile; 138 this.activeApexChanged = activeApexChanged; 139 } 140 ActiveApexInfo(ApexInfo apexInfo)141 public ActiveApexInfo(ApexInfo apexInfo) { 142 this( 143 apexInfo.moduleName, 144 new File(Environment.getApexDirectory() + File.separator 145 + apexInfo.moduleName), 146 new File(apexInfo.preinstalledModulePath), 147 apexInfo.isFactory, 148 new File(apexInfo.modulePath), 149 apexInfo.activeApexChanged); 150 } 151 } 152 getAllApexInfos()153 abstract ApexInfo[] getAllApexInfos(); notifyScanResult(List<ScanResult> scanResults)154 abstract void notifyScanResult(List<ScanResult> scanResults); 155 156 /** 157 * Returns {@link ActiveApexInfo} records relative to all active APEX packages. 158 * 159 * @hide 160 */ getActiveApexInfos()161 public abstract List<ActiveApexInfo> getActiveApexInfos(); 162 163 /** 164 * Returns the active apex package's name that contains the (apk) package. 165 * 166 * @param containedPackageName The (apk) package that might be in a apex 167 * @return the apex package's name of {@code null} if the {@code containedPackage} is not inside 168 * any apex. 169 */ 170 @Nullable getActiveApexPackageNameContainingPackage( @onNull String containedPackageName)171 public abstract String getActiveApexPackageNameContainingPackage( 172 @NonNull String containedPackageName); 173 174 /** 175 * Retrieves information about an apexd staged session i.e. the internal state used by apexd to 176 * track the different states of a session. 177 * 178 * @param sessionId the identifier of the session. 179 * @return an ApexSessionInfo object, or null if the session is not known. 180 */ 181 @Nullable getStagedSessionInfo(int sessionId)182 abstract ApexSessionInfo getStagedSessionInfo(int sessionId); 183 184 /** 185 * Returns array of all staged sessions known to apexd. 186 */ 187 @NonNull getSessions()188 abstract SparseArray<ApexSessionInfo> getSessions(); 189 190 /** 191 * Submit a staged session to apex service. This causes the apex service to perform some initial 192 * verification and accept or reject the session. Submitting a session successfully is not 193 * enough for it to be activated at the next boot, the caller needs to call 194 * {@link #markStagedSessionReady(int)}. 195 * 196 * @throws PackageManagerException if call to apexd fails 197 */ submitStagedSession(ApexSessionParams params)198 abstract ApexInfoList submitStagedSession(ApexSessionParams params) 199 throws PackageManagerException; 200 201 /** 202 * Returns {@code ApeInfo} about apex sessions that have been marked ready via 203 * {@link #markStagedSessionReady(int)} 204 * 205 * Returns empty array if there is no staged apex session or if there is any error. 206 */ getStagedApexInfos(ApexSessionParams params)207 abstract ApexInfo[] getStagedApexInfos(ApexSessionParams params); 208 209 /** 210 * Mark a staged session previously submitted using {@code submitStagedSession} as ready to be 211 * applied at next reboot. 212 * 213 * @param sessionId the identifier of the {@link PackageInstallerSession} being marked as ready. 214 * @throws PackageManagerException if call to apexd fails 215 */ markStagedSessionReady(int sessionId)216 abstract void markStagedSessionReady(int sessionId) throws PackageManagerException; 217 218 /** 219 * Marks a staged session as successful. 220 * 221 * <p>Only activated session can be marked as successful. 222 * 223 * @param sessionId the identifier of the {@link PackageInstallerSession} being marked as 224 * successful. 225 */ markStagedSessionSuccessful(int sessionId)226 abstract void markStagedSessionSuccessful(int sessionId); 227 228 /** 229 * Whether the current device supports the management of APEX packages. 230 * 231 * @return true if APEX packages can be managed on this device, false otherwise. 232 */ isApexSupported()233 abstract boolean isApexSupported(); 234 235 /** 236 * Abandons the (only) active session previously submitted. 237 * 238 * @return {@code true} upon success, {@code false} if any remote exception occurs 239 */ revertActiveSessions()240 abstract boolean revertActiveSessions(); 241 242 /** 243 * Abandons the staged session with the given sessionId. Client should handle {@code false} 244 * return value carefully as failure here can leave device in inconsistent state. 245 * 246 * @return {@code true} upon success, {@code false} if any exception occurs 247 */ abortStagedSession(int sessionId)248 abstract boolean abortStagedSession(int sessionId); 249 250 /** 251 * Uninstalls given {@code apexPackage}. 252 * 253 * <p>NOTE. Device must be rebooted in order for uninstall to take effect. 254 * 255 * @param apexPackagePath package to uninstall. 256 * @return {@code true} upon successful uninstall, {@code false} otherwise. 257 */ uninstallApex(String apexPackagePath)258 abstract boolean uninstallApex(String apexPackagePath); 259 260 /** 261 * Registers an APK package as an embedded apk of apex. 262 */ registerApkInApex(AndroidPackage pkg)263 abstract void registerApkInApex(AndroidPackage pkg); 264 265 /** 266 * Reports error raised during installation of apk-in-apex. 267 * 268 * @param scanDirPath the directory of the apex inside which apk-in-apex resides. 269 * @param errorMsg the actual error that occurred when scanning the path 270 */ reportErrorWithApkInApex(String scanDirPath, String errorMsg)271 abstract void reportErrorWithApkInApex(String scanDirPath, String errorMsg); 272 273 /** 274 * Returns null if there were no errors when installing apk-in-apex inside 275 * {@param apexPackageName}, otherwise returns the error as string 276 * 277 * @param apexPackageName Package name of the apk container of apex 278 */ 279 @Nullable getApkInApexInstallError(String apexPackageName)280 abstract String getApkInApexInstallError(String apexPackageName); 281 282 /** 283 * Returns list of {@code packageName} of apks inside the given apex. 284 * @param apexPackageName Package name of the apk container of apex 285 */ getApksInApex(String apexPackageName)286 abstract List<String> getApksInApex(String apexPackageName); 287 288 /** 289 * Returns the apex module name for the given package name, if the package is an APEX. Otherwise 290 * returns {@code null}. 291 */ 292 @Nullable getApexModuleNameForPackageName(String apexPackageName)293 public abstract String getApexModuleNameForPackageName(String apexPackageName); 294 295 /** 296 * Returns the package name of the active APEX whose name is {@code apexModuleName}. If not 297 * found, returns {@code null}. 298 */ 299 @Nullable getActivePackageNameForApexModuleName(String apexModuleName)300 public abstract String getActivePackageNameForApexModuleName(String apexModuleName); 301 302 /** 303 * Copies the CE apex data directory for the given {@code userId} to a backup location, for use 304 * in case of rollback. 305 * 306 * @return boolean true if the snapshot was successful 307 */ snapshotCeData(int userId, int rollbackId, String apexPackageName)308 public abstract boolean snapshotCeData(int userId, int rollbackId, String apexPackageName); 309 310 /** 311 * Restores the snapshot of the CE apex data directory for the given {@code userId}. 312 * Note the snapshot will be deleted after restoration succeeded. 313 * 314 * @return boolean true if the restore was successful 315 */ restoreCeData(int userId, int rollbackId, String apexPackageName)316 public abstract boolean restoreCeData(int userId, int rollbackId, String apexPackageName); 317 318 /** 319 * Deletes snapshots of the device encrypted apex data directories for the given 320 * {@code rollbackId}. 321 * 322 * @return boolean true if the delete was successful 323 */ destroyDeSnapshots(int rollbackId)324 public abstract boolean destroyDeSnapshots(int rollbackId); 325 326 /** 327 * Deletes snapshots of the credential encrypted apex data directories for the specified user, 328 * for the given rollback id as long as the user is credential unlocked. 329 * 330 * @return boolean true if the delete was successful 331 */ destroyCeSnapshots(int userId, int rollbackId)332 public abstract boolean destroyCeSnapshots(int userId, int rollbackId); 333 334 /** 335 * Deletes snapshots of the credential encrypted apex data directories for the specified user, 336 * where the rollback id is not included in {@code retainRollbackIds} as long as the user is 337 * credential unlocked. 338 * 339 * @return boolean true if the delete was successful 340 */ destroyCeSnapshotsNotSpecified(int userId, int[] retainRollbackIds)341 public abstract boolean destroyCeSnapshotsNotSpecified(int userId, int[] retainRollbackIds); 342 343 /** 344 * Inform apexd that the boot has completed. 345 */ markBootCompleted()346 public abstract void markBootCompleted(); 347 348 /** 349 * Estimate how much storage space is needed on /data/ for decompressing apexes 350 * @param infoList List of apexes that are compressed in target build. 351 * @return Size, in bytes, the amount of space needed on /data/ 352 */ calculateSizeForCompressedApex(CompressedApexInfoList infoList)353 public abstract long calculateSizeForCompressedApex(CompressedApexInfoList infoList) 354 throws RemoteException; 355 356 /** 357 * Reserve space on /data so that apexes can be decompressed after OTA 358 * @param infoList List of apexes that are compressed in target build. 359 */ reserveSpaceForCompressedApex(CompressedApexInfoList infoList)360 public abstract void reserveSpaceForCompressedApex(CompressedApexInfoList infoList) 361 throws RemoteException; 362 363 /** 364 * Performs a non-staged install of the given {@code apexFile}. 365 * 366 * @return {@code ApeInfo} about the newly installed APEX package. 367 */ installPackage(File apexFile)368 abstract ApexInfo installPackage(File apexFile) throws PackageManagerException; 369 370 /** 371 * Get a list of apex system services implemented in an apex. 372 * 373 * <p>The list is sorted by initOrder for consistency. 374 */ getApexSystemServices()375 public abstract List<ApexSystemServiceInfo> getApexSystemServices(); 376 377 /** 378 * Returns an APEX file backing the mount point {@code file} is located on, or {@code null} if 379 * {@code file} doesn't belong to a {@code /apex} mount point. 380 * 381 * <p>Also returns {@code null} if device doesn't support updatable APEX packages. 382 */ 383 @Nullable getBackingApexFile(@onNull File file)384 public abstract File getBackingApexFile(@NonNull File file); 385 386 /** 387 * Dumps various state information to the provided {@link PrintWriter} object. 388 * 389 * @param pw the {@link PrintWriter} object to send information to. 390 */ dump(PrintWriter pw)391 abstract void dump(PrintWriter pw); 392 393 @IntDef( 394 flag = true, 395 prefix = { "MATCH_"}, 396 value = {MATCH_ACTIVE_PACKAGE, MATCH_FACTORY_PACKAGE}) 397 @Retention(RetentionPolicy.SOURCE) 398 @interface PackageInfoFlags{} 399 400 /** 401 * An implementation of {@link ApexManager} that should be used in case device supports updating 402 * APEX packages. 403 */ 404 @VisibleForTesting 405 protected static class ApexManagerImpl extends ApexManager { 406 private final Object mLock = new Object(); 407 408 // TODO(ioffe): this should be either List or ArrayMap. 409 @GuardedBy("mLock") 410 private Set<ActiveApexInfo> mActiveApexInfosCache; 411 412 /** 413 * Map of all apex system services to the jar files they are contained in. 414 */ 415 @GuardedBy("mLock") 416 private List<ApexSystemServiceInfo> mApexSystemServices = new ArrayList<>(); 417 418 /** 419 * Contains the list of {@code packageName}s of apks-in-apex for given 420 * {@code apexModuleName}. See {@link #mPackageNameToApexModuleName} to understand the 421 * difference between {@code packageName} and {@code apexModuleName}. 422 */ 423 @GuardedBy("mLock") 424 private ArrayMap<String, List<String>> mApksInApex = new ArrayMap<>(); 425 426 /** 427 * Contains the list of {@code Exception}s that were raised when installing apk-in-apex 428 * inside {@code apexModuleName}. 429 */ 430 @GuardedBy("mLock") 431 private Map<String, String> mErrorWithApkInApex = new ArrayMap<>(); 432 433 /** 434 * An APEX is a file format that delivers the apex-payload wrapped in an apk container. The 435 * apk container has a reference name, called {@code packageName}, which is found inside the 436 * {@code AndroidManifest.xml}. The apex payload inside the container also has a reference 437 * name, called {@code apexModuleName}, which is found in {@code apex_manifest.json} file. 438 * 439 * {@link #mPackageNameToApexModuleName} contains the mapping from {@code packageName} of 440 * the apk container to {@code apexModuleName} of the apex-payload inside. 441 */ 442 @GuardedBy("mLock") 443 private ArrayMap<String, String> mPackageNameToApexModuleName; 444 445 /** 446 * Reverse mapping of {@link #mPackageNameToApexModuleName}, for active packages only. 447 */ 448 @GuardedBy("mLock") 449 private ArrayMap<String, String> mApexModuleNameToActivePackageName; 450 451 /** 452 * Retrieve the service from ServiceManager. If the service is not running, it will be 453 * started, and this function will block until it is ready. 454 */ 455 @VisibleForTesting waitForApexService()456 protected IApexService waitForApexService() { 457 // Since apexd is a trusted platform component, synchronized calls are allowable 458 return IApexService.Stub.asInterface( 459 Binder.allowBlocking(ServiceManager.waitForService("apexservice"))); 460 } 461 462 @Override getAllApexInfos()463 ApexInfo[] getAllApexInfos() { 464 try { 465 return waitForApexService().getAllPackages(); 466 } catch (RemoteException re) { 467 Slog.e(TAG, "Unable to retrieve packages from apexservice: " + re.toString()); 468 throw new RuntimeException(re); 469 } 470 } 471 472 @Override notifyScanResult(List<ScanResult> scanResults)473 void notifyScanResult(List<ScanResult> scanResults) { 474 synchronized (mLock) { 475 notifyScanResultLocked(scanResults); 476 } 477 } 478 479 @GuardedBy("mLock") notifyScanResultLocked(List<ScanResult> scanResults)480 private void notifyScanResultLocked(List<ScanResult> scanResults) { 481 mPackageNameToApexModuleName = new ArrayMap<>(); 482 mApexModuleNameToActivePackageName = new ArrayMap<>(); 483 for (ScanResult scanResult : scanResults) { 484 ApexInfo ai = scanResult.apexInfo; 485 String packageName = scanResult.packageName; 486 for (ParsedApexSystemService service : 487 scanResult.pkg.getApexSystemServices()) { 488 String minSdkVersion = service.getMinSdkVersion(); 489 if (minSdkVersion != null && !UnboundedSdkLevel.isAtLeast(minSdkVersion)) { 490 Slog.d(TAG, String.format( 491 "ApexSystemService %s with min_sdk_version=%s is skipped", 492 service.getName(), service.getMinSdkVersion())); 493 continue; 494 } 495 String maxSdkVersion = service.getMaxSdkVersion(); 496 if (maxSdkVersion != null && !UnboundedSdkLevel.isAtMost(maxSdkVersion)) { 497 Slog.d(TAG, String.format( 498 "ApexSystemService %s with max_sdk_version=%s is skipped", 499 service.getName(), service.getMaxSdkVersion())); 500 continue; 501 } 502 503 if (ai.isActive) { 504 String name = service.getName(); 505 for (int j = 0; j < mApexSystemServices.size(); j++) { 506 ApexSystemServiceInfo info = mApexSystemServices.get(j); 507 if (info.getName().equals(name)) { 508 throw new IllegalStateException(TextUtils.formatSimple( 509 "Duplicate apex-system-service %s from %s, %s", name, 510 info.mJarPath, service.getJarPath())); 511 } 512 } 513 ApexSystemServiceInfo info = new ApexSystemServiceInfo( 514 service.getName(), service.getJarPath(), 515 service.getInitOrder()); 516 mApexSystemServices.add(info); 517 } 518 } 519 Collections.sort(mApexSystemServices); 520 mPackageNameToApexModuleName.put(packageName, ai.moduleName); 521 if (ai.isActive) { 522 if (mApexModuleNameToActivePackageName.containsKey(ai.moduleName)) { 523 throw new IllegalStateException( 524 "Two active packages have the same APEX module name: " 525 + ai.moduleName); 526 } 527 mApexModuleNameToActivePackageName.put( 528 ai.moduleName, packageName); 529 } 530 } 531 } 532 533 @Override getActiveApexInfos()534 public List<ActiveApexInfo> getActiveApexInfos() { 535 final TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG + "Timing", 536 Trace.TRACE_TAG_PACKAGE_MANAGER); 537 synchronized (mLock) { 538 if (mActiveApexInfosCache == null) { 539 t.traceBegin("getActiveApexInfos_noCache"); 540 try { 541 mActiveApexInfosCache = new ArraySet<>(); 542 final ApexInfo[] activePackages = waitForApexService().getActivePackages(); 543 for (int i = 0; i < activePackages.length; i++) { 544 ApexInfo apexInfo = activePackages[i]; 545 mActiveApexInfosCache.add(new ActiveApexInfo(apexInfo)); 546 } 547 } catch (RemoteException e) { 548 Slog.e(TAG, "Unable to retrieve packages from apexservice", e); 549 } 550 t.traceEnd(); 551 } 552 if (mActiveApexInfosCache != null) { 553 return new ArrayList<>(mActiveApexInfosCache); 554 } else { 555 return Collections.emptyList(); 556 } 557 } 558 } 559 560 @Override 561 @Nullable getActiveApexPackageNameContainingPackage(String containedPackageName)562 public String getActiveApexPackageNameContainingPackage(String containedPackageName) { 563 Objects.requireNonNull(containedPackageName); 564 synchronized (mLock) { 565 Preconditions.checkState(mPackageNameToApexModuleName != null, 566 "APEX packages have not been scanned"); 567 int numApksInApex = mApksInApex.size(); 568 for (int apkInApexNum = 0; apkInApexNum < numApksInApex; apkInApexNum++) { 569 if (mApksInApex.valueAt(apkInApexNum).contains(containedPackageName)) { 570 String apexModuleName = mApksInApex.keyAt(apkInApexNum); 571 572 int numApexPkgs = mPackageNameToApexModuleName.size(); 573 for (int apexPkgNum = 0; apexPkgNum < numApexPkgs; apexPkgNum++) { 574 if (mPackageNameToApexModuleName.valueAt(apexPkgNum).equals( 575 apexModuleName)) { 576 return mPackageNameToApexModuleName.keyAt(apexPkgNum); 577 } 578 } 579 } 580 } 581 } 582 583 return null; 584 } 585 586 @Override getStagedSessionInfo(int sessionId)587 @Nullable ApexSessionInfo getStagedSessionInfo(int sessionId) { 588 try { 589 ApexSessionInfo apexSessionInfo = 590 waitForApexService().getStagedSessionInfo(sessionId); 591 if (apexSessionInfo.isUnknown) { 592 return null; 593 } 594 return apexSessionInfo; 595 } catch (RemoteException re) { 596 Slog.e(TAG, "Unable to contact apexservice", re); 597 throw new RuntimeException(re); 598 } 599 } 600 601 @Override getSessions()602 SparseArray<ApexSessionInfo> getSessions() { 603 try { 604 final ApexSessionInfo[] sessions = waitForApexService().getSessions(); 605 final SparseArray<ApexSessionInfo> result = new SparseArray<>(sessions.length); 606 for (int i = 0; i < sessions.length; i++) { 607 result.put(sessions[i].sessionId, sessions[i]); 608 } 609 return result; 610 } catch (RemoteException re) { 611 Slog.e(TAG, "Unable to contact apexservice", re); 612 throw new RuntimeException(re); 613 } 614 } 615 616 @Override submitStagedSession(ApexSessionParams params)617 ApexInfoList submitStagedSession(ApexSessionParams params) throws PackageManagerException { 618 try { 619 final ApexInfoList apexInfoList = new ApexInfoList(); 620 waitForApexService().submitStagedSession(params, apexInfoList); 621 return apexInfoList; 622 } catch (RemoteException re) { 623 Slog.e(TAG, "Unable to contact apexservice", re); 624 throw new RuntimeException(re); 625 } catch (Exception e) { 626 throw new PackageManagerException( 627 PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE, 628 "apexd verification failed : " + e.getMessage()); 629 } 630 } 631 632 @Override getStagedApexInfos(ApexSessionParams params)633 ApexInfo[] getStagedApexInfos(ApexSessionParams params) { 634 try { 635 return waitForApexService().getStagedApexInfos(params); 636 } catch (RemoteException re) { 637 Slog.w(TAG, "Unable to contact apexservice" + re.getMessage()); 638 throw new RuntimeException(re); 639 } catch (Exception e) { 640 Slog.w(TAG, "Failed to collect staged apex infos" + e.getMessage()); 641 return new ApexInfo[0]; 642 } 643 } 644 645 @Override markStagedSessionReady(int sessionId)646 void markStagedSessionReady(int sessionId) throws PackageManagerException { 647 try { 648 waitForApexService().markStagedSessionReady(sessionId); 649 } catch (RemoteException re) { 650 Slog.e(TAG, "Unable to contact apexservice", re); 651 throw new RuntimeException(re); 652 } catch (Exception e) { 653 throw new PackageManagerException( 654 PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE, 655 "Failed to mark apexd session as ready : " + e.getMessage()); 656 } 657 } 658 659 @Override markStagedSessionSuccessful(int sessionId)660 void markStagedSessionSuccessful(int sessionId) { 661 try { 662 waitForApexService().markStagedSessionSuccessful(sessionId); 663 } catch (RemoteException re) { 664 Slog.e(TAG, "Unable to contact apexservice", re); 665 throw new RuntimeException(re); 666 } catch (Exception e) { 667 // It is fine to just log an exception in this case. APEXd will be able to recover 668 // in case markStagedSessionSuccessful fails. 669 Slog.e(TAG, "Failed to mark session " + sessionId + " as successful", e); 670 } 671 } 672 673 @Override isApexSupported()674 boolean isApexSupported() { 675 return true; 676 } 677 678 @Override revertActiveSessions()679 boolean revertActiveSessions() { 680 try { 681 waitForApexService().revertActiveSessions(); 682 return true; 683 } catch (RemoteException re) { 684 Slog.e(TAG, "Unable to contact apexservice", re); 685 return false; 686 } catch (Exception e) { 687 Slog.e(TAG, e.getMessage(), e); 688 return false; 689 } 690 } 691 692 @Override abortStagedSession(int sessionId)693 boolean abortStagedSession(int sessionId) { 694 try { 695 waitForApexService().abortStagedSession(sessionId); 696 return true; 697 } catch (Exception e) { 698 Slog.e(TAG, e.getMessage(), e); 699 return false; 700 } 701 } 702 703 @Override uninstallApex(String apexPackagePath)704 boolean uninstallApex(String apexPackagePath) { 705 try { 706 waitForApexService().unstagePackages(Collections.singletonList(apexPackagePath)); 707 return true; 708 } catch (Exception e) { 709 return false; 710 } 711 } 712 713 @Override registerApkInApex(AndroidPackage pkg)714 void registerApkInApex(AndroidPackage pkg) { 715 synchronized (mLock) { 716 for (ActiveApexInfo aai : mActiveApexInfosCache) { 717 if (pkg.getBaseApkPath().startsWith( 718 aai.apexDirectory.getAbsolutePath() + File.separator)) { 719 List<String> apks = mApksInApex.get(aai.apexModuleName); 720 if (apks == null) { 721 apks = Lists.newArrayList(); 722 mApksInApex.put(aai.apexModuleName, apks); 723 } 724 Slog.i(TAG, "Registering " + pkg.getPackageName() + " as apk-in-apex of " 725 + aai.apexModuleName); 726 apks.add(pkg.getPackageName()); 727 } 728 } 729 } 730 } 731 732 @Override reportErrorWithApkInApex(String scanDirPath, String errorMsg)733 void reportErrorWithApkInApex(String scanDirPath, String errorMsg) { 734 synchronized (mLock) { 735 for (ActiveApexInfo aai : mActiveApexInfosCache) { 736 if (scanDirPath.startsWith(aai.apexDirectory.getAbsolutePath())) { 737 mErrorWithApkInApex.put(aai.apexModuleName, errorMsg); 738 } 739 } 740 } 741 } 742 743 @Override 744 @Nullable getApkInApexInstallError(String apexPackageName)745 String getApkInApexInstallError(String apexPackageName) { 746 synchronized (mLock) { 747 Preconditions.checkState(mPackageNameToApexModuleName != null, 748 "APEX packages have not been scanned"); 749 String moduleName = mPackageNameToApexModuleName.get(apexPackageName); 750 if (moduleName == null) { 751 return null; 752 } 753 return mErrorWithApkInApex.get(moduleName); 754 } 755 } 756 757 @Override getApksInApex(String apexPackageName)758 List<String> getApksInApex(String apexPackageName) { 759 synchronized (mLock) { 760 Preconditions.checkState(mPackageNameToApexModuleName != null, 761 "APEX packages have not been scanned"); 762 String moduleName = mPackageNameToApexModuleName.get(apexPackageName); 763 if (moduleName == null) { 764 return Collections.emptyList(); 765 } 766 return mApksInApex.getOrDefault(moduleName, Collections.emptyList()); 767 } 768 } 769 770 @Override 771 @Nullable getApexModuleNameForPackageName(String apexPackageName)772 public String getApexModuleNameForPackageName(String apexPackageName) { 773 synchronized (mLock) { 774 Preconditions.checkState(mPackageNameToApexModuleName != null, 775 "APEX packages have not been scanned"); 776 return mPackageNameToApexModuleName.get(apexPackageName); 777 } 778 } 779 780 @Override 781 @Nullable getActivePackageNameForApexModuleName(String apexModuleName)782 public String getActivePackageNameForApexModuleName(String apexModuleName) { 783 synchronized (mLock) { 784 Preconditions.checkState(mApexModuleNameToActivePackageName != null, 785 "APEX packages have not been scanned"); 786 return mApexModuleNameToActivePackageName.get(apexModuleName); 787 } 788 } 789 790 @Override snapshotCeData(int userId, int rollbackId, String apexPackageName)791 public boolean snapshotCeData(int userId, int rollbackId, String apexPackageName) { 792 String apexModuleName; 793 synchronized (mLock) { 794 Preconditions.checkState(mPackageNameToApexModuleName != null, 795 "APEX packages have not been scanned"); 796 apexModuleName = mPackageNameToApexModuleName.get(apexPackageName); 797 } 798 if (apexModuleName == null) { 799 Slog.e(TAG, "Invalid apex package name: " + apexPackageName); 800 return false; 801 } 802 try { 803 waitForApexService().snapshotCeData(userId, rollbackId, apexModuleName); 804 return true; 805 } catch (Exception e) { 806 Slog.e(TAG, e.getMessage(), e); 807 return false; 808 } 809 } 810 811 @Override restoreCeData(int userId, int rollbackId, String apexPackageName)812 public boolean restoreCeData(int userId, int rollbackId, String apexPackageName) { 813 String apexModuleName; 814 synchronized (mLock) { 815 Preconditions.checkState(mPackageNameToApexModuleName != null, 816 "APEX packages have not been scanned"); 817 apexModuleName = mPackageNameToApexModuleName.get(apexPackageName); 818 } 819 if (apexModuleName == null) { 820 Slog.e(TAG, "Invalid apex package name: " + apexPackageName); 821 return false; 822 } 823 try { 824 waitForApexService().restoreCeData(userId, rollbackId, apexModuleName); 825 return true; 826 } catch (Exception e) { 827 Slog.e(TAG, e.getMessage(), e); 828 return false; 829 } 830 } 831 832 @Override destroyDeSnapshots(int rollbackId)833 public boolean destroyDeSnapshots(int rollbackId) { 834 try { 835 waitForApexService().destroyDeSnapshots(rollbackId); 836 return true; 837 } catch (Exception e) { 838 Slog.e(TAG, e.getMessage(), e); 839 return false; 840 } 841 } 842 843 @Override destroyCeSnapshots(int userId, int rollbackId)844 public boolean destroyCeSnapshots(int userId, int rollbackId) { 845 try { 846 waitForApexService().destroyCeSnapshots(userId, rollbackId); 847 return true; 848 } catch (Exception e) { 849 Slog.e(TAG, e.getMessage(), e); 850 return false; 851 } 852 } 853 854 @Override destroyCeSnapshotsNotSpecified(int userId, int[] retainRollbackIds)855 public boolean destroyCeSnapshotsNotSpecified(int userId, int[] retainRollbackIds) { 856 try { 857 waitForApexService().destroyCeSnapshotsNotSpecified(userId, retainRollbackIds); 858 return true; 859 } catch (Exception e) { 860 Slog.e(TAG, e.getMessage(), e); 861 return false; 862 } 863 } 864 865 @Override markBootCompleted()866 public void markBootCompleted() { 867 try { 868 waitForApexService().markBootCompleted(); 869 } catch (RemoteException re) { 870 Slog.e(TAG, "Unable to contact apexservice", re); 871 } 872 } 873 874 @Override calculateSizeForCompressedApex(CompressedApexInfoList infoList)875 public long calculateSizeForCompressedApex(CompressedApexInfoList infoList) 876 throws RemoteException { 877 return waitForApexService().calculateSizeForCompressedApex(infoList); 878 } 879 880 @Override reserveSpaceForCompressedApex(CompressedApexInfoList infoList)881 public void reserveSpaceForCompressedApex(CompressedApexInfoList infoList) 882 throws RemoteException { 883 waitForApexService().reserveSpaceForCompressedApex(infoList); 884 } 885 getSigningDetails(PackageInfo pkg)886 private SigningDetails getSigningDetails(PackageInfo pkg) throws PackageManagerException { 887 final int minSignatureScheme = 888 ApkSignatureVerifier.getMinimumSignatureSchemeVersionForTargetSdk( 889 pkg.applicationInfo.targetSdkVersion); 890 final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing(); 891 final ParseResult<SigningDetails> result = ApkSignatureVerifier.verify( 892 input, pkg.applicationInfo.sourceDir, minSignatureScheme); 893 if (result.isError()) { 894 throw new PackageManagerException(result.getErrorCode(), result.getErrorMessage(), 895 result.getException()); 896 } 897 return result.getResult(); 898 } 899 checkApexSignature(PackageInfo existingApexPkg, PackageInfo newApexPkg)900 private void checkApexSignature(PackageInfo existingApexPkg, PackageInfo newApexPkg) 901 throws PackageManagerException { 902 final SigningDetails existingSigningDetails = getSigningDetails(existingApexPkg); 903 final SigningDetails newSigningDetails = getSigningDetails(newApexPkg); 904 if (!newSigningDetails.checkCapability(existingSigningDetails, 905 SigningDetails.CertCapabilities.INSTALLED_DATA)) { 906 throw new PackageManagerException(PackageManager.INSTALL_FAILED_BAD_SIGNATURE, 907 "APK container signature of " + newApexPkg.applicationInfo.sourceDir 908 + " is not compatible with currently installed on device"); 909 } 910 } 911 912 @Override installPackage(File apexFile)913 ApexInfo installPackage(File apexFile) 914 throws PackageManagerException { 915 try { 916 return waitForApexService().installAndActivatePackage(apexFile.getAbsolutePath()); 917 } catch (RemoteException e) { 918 throw new PackageManagerException(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, 919 "apexservice not available"); 920 } catch (Exception e) { 921 // TODO(b/187864524): is INSTALL_FAILED_INTERNAL_ERROR is the right error code here? 922 throw new PackageManagerException(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, 923 e.getMessage()); 924 } 925 } 926 927 @Override getApexSystemServices()928 public List<ApexSystemServiceInfo> getApexSystemServices() { 929 synchronized (mLock) { 930 Preconditions.checkState(mApexSystemServices != null, 931 "APEX packages have not been scanned"); 932 return mApexSystemServices; 933 } 934 } 935 936 @Override getBackingApexFile(File file)937 public File getBackingApexFile(File file) { 938 Path path = file.toPath(); 939 if (!path.startsWith(Environment.getApexDirectory().toPath())) { 940 return null; 941 } 942 if (path.getNameCount() < 2) { 943 return null; 944 } 945 String moduleName = file.toPath().getName(1).toString(); 946 final List<ActiveApexInfo> apexes = getActiveApexInfos(); 947 for (int i = 0; i < apexes.size(); i++) { 948 if (apexes.get(i).apexModuleName.equals(moduleName)) { 949 return apexes.get(i).apexFile; 950 } 951 } 952 return null; 953 } 954 955 @Override dump(PrintWriter pw)956 void dump(PrintWriter pw) { 957 final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ", 120); 958 try { 959 ipw.println(); 960 ipw.println("APEX session state:"); 961 ipw.increaseIndent(); 962 final ApexSessionInfo[] sessions = waitForApexService().getSessions(); 963 for (ApexSessionInfo si : sessions) { 964 ipw.println("Session ID: " + si.sessionId); 965 ipw.increaseIndent(); 966 if (si.isUnknown) { 967 ipw.println("State: UNKNOWN"); 968 } else if (si.isVerified) { 969 ipw.println("State: VERIFIED"); 970 } else if (si.isStaged) { 971 ipw.println("State: STAGED"); 972 } else if (si.isActivated) { 973 ipw.println("State: ACTIVATED"); 974 } else if (si.isActivationFailed) { 975 ipw.println("State: ACTIVATION FAILED"); 976 } else if (si.isSuccess) { 977 ipw.println("State: SUCCESS"); 978 } else if (si.isRevertInProgress) { 979 ipw.println("State: REVERT IN PROGRESS"); 980 } else if (si.isReverted) { 981 ipw.println("State: REVERTED"); 982 } else if (si.isRevertFailed) { 983 ipw.println("State: REVERT FAILED"); 984 } 985 ipw.decreaseIndent(); 986 } 987 ipw.decreaseIndent(); 988 ipw.println(); 989 } catch (RemoteException e) { 990 ipw.println("Couldn't communicate with apexd."); 991 } 992 } 993 } 994 995 /** 996 * An implementation of {@link ApexManager} that should be used in case device does not support 997 * updating APEX packages. 998 */ 999 @VisibleForTesting 1000 static final class ApexManagerFlattenedApex extends ApexManager { 1001 @Override getAllApexInfos()1002 ApexInfo[] getAllApexInfos() { 1003 return null; 1004 } 1005 1006 @Override notifyScanResult(List<ScanResult> scanResults)1007 void notifyScanResult(List<ScanResult> scanResults) { 1008 // No-op 1009 } 1010 1011 @Override getActiveApexInfos()1012 public List<ActiveApexInfo> getActiveApexInfos() { 1013 // There is no apexd running in case of flattened apex 1014 // We look up the /apex directory and identify the active APEX modules from there. 1015 // As "preinstalled" path, we just report /system since in the case of flattened APEX 1016 // the /apex directory is just a symlink to /system/apex. 1017 List<ActiveApexInfo> result = new ArrayList<>(); 1018 File apexDir = Environment.getApexDirectory(); 1019 if (apexDir.isDirectory()) { 1020 File[] files = apexDir.listFiles(); 1021 // listFiles might be null if system server doesn't have permission to read 1022 // a directory. 1023 if (files != null) { 1024 for (File file : files) { 1025 if (file.isDirectory() && !file.getName().contains("@") 1026 // In flattened configuration, init special-cases the art directory 1027 // and bind-mounts com.android.art.debug to com.android.art. 1028 && !file.getName().equals("com.android.art.debug")) { 1029 result.add( 1030 new ActiveApexInfo(file, Environment.getRootDirectory(), file)); 1031 } 1032 } 1033 } 1034 } 1035 return result; 1036 } 1037 1038 @Override 1039 @Nullable getActiveApexPackageNameContainingPackage( @onNull String containedPackageName)1040 public String getActiveApexPackageNameContainingPackage( 1041 @NonNull String containedPackageName) { 1042 Objects.requireNonNull(containedPackageName); 1043 1044 return null; 1045 } 1046 1047 @Override getStagedSessionInfo(int sessionId)1048 ApexSessionInfo getStagedSessionInfo(int sessionId) { 1049 throw new UnsupportedOperationException(); 1050 } 1051 1052 @Override getSessions()1053 SparseArray<ApexSessionInfo> getSessions() { 1054 return new SparseArray<>(0); 1055 } 1056 1057 @Override submitStagedSession(ApexSessionParams params)1058 ApexInfoList submitStagedSession(ApexSessionParams params) 1059 throws PackageManagerException { 1060 throw new PackageManagerException(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, 1061 "Device doesn't support updating APEX"); 1062 } 1063 1064 @Override getStagedApexInfos(ApexSessionParams params)1065 ApexInfo[] getStagedApexInfos(ApexSessionParams params) { 1066 throw new UnsupportedOperationException(); 1067 } 1068 1069 @Override markStagedSessionReady(int sessionId)1070 void markStagedSessionReady(int sessionId) { 1071 throw new UnsupportedOperationException(); 1072 } 1073 1074 @Override markStagedSessionSuccessful(int sessionId)1075 void markStagedSessionSuccessful(int sessionId) { 1076 throw new UnsupportedOperationException(); 1077 } 1078 1079 @Override isApexSupported()1080 boolean isApexSupported() { 1081 return false; 1082 } 1083 1084 @Override revertActiveSessions()1085 boolean revertActiveSessions() { 1086 throw new UnsupportedOperationException(); 1087 } 1088 1089 @Override abortStagedSession(int sessionId)1090 boolean abortStagedSession(int sessionId) { 1091 throw new UnsupportedOperationException(); 1092 } 1093 1094 @Override uninstallApex(String apexPackagePath)1095 boolean uninstallApex(String apexPackagePath) { 1096 throw new UnsupportedOperationException(); 1097 } 1098 1099 @Override registerApkInApex(AndroidPackage pkg)1100 void registerApkInApex(AndroidPackage pkg) { 1101 // No-op 1102 } 1103 1104 @Override reportErrorWithApkInApex(String scanDirPath, String errorMsg)1105 void reportErrorWithApkInApex(String scanDirPath, String errorMsg) { 1106 // No-op 1107 } 1108 1109 @Override 1110 @Nullable getApkInApexInstallError(String apexPackageName)1111 String getApkInApexInstallError(String apexPackageName) { 1112 return null; 1113 } 1114 1115 @Override getApksInApex(String apexPackageName)1116 List<String> getApksInApex(String apexPackageName) { 1117 return Collections.emptyList(); 1118 } 1119 1120 @Override 1121 @Nullable getApexModuleNameForPackageName(String apexPackageName)1122 public String getApexModuleNameForPackageName(String apexPackageName) { 1123 return null; 1124 } 1125 1126 @Override 1127 @Nullable getActivePackageNameForApexModuleName(String apexModuleName)1128 public String getActivePackageNameForApexModuleName(String apexModuleName) { 1129 return null; 1130 } 1131 1132 @Override snapshotCeData(int userId, int rollbackId, String apexPackageName)1133 public boolean snapshotCeData(int userId, int rollbackId, String apexPackageName) { 1134 throw new UnsupportedOperationException(); 1135 } 1136 1137 @Override restoreCeData(int userId, int rollbackId, String apexPackageName)1138 public boolean restoreCeData(int userId, int rollbackId, String apexPackageName) { 1139 throw new UnsupportedOperationException(); 1140 } 1141 1142 @Override destroyDeSnapshots(int rollbackId)1143 public boolean destroyDeSnapshots(int rollbackId) { 1144 throw new UnsupportedOperationException(); 1145 } 1146 1147 @Override destroyCeSnapshots(int userId, int rollbackId)1148 public boolean destroyCeSnapshots(int userId, int rollbackId) { 1149 return true; 1150 } 1151 1152 @Override destroyCeSnapshotsNotSpecified(int userId, int[] retainRollbackIds)1153 public boolean destroyCeSnapshotsNotSpecified(int userId, int[] retainRollbackIds) { 1154 return true; 1155 } 1156 1157 @Override markBootCompleted()1158 public void markBootCompleted() { 1159 // No-op 1160 } 1161 1162 @Override calculateSizeForCompressedApex(CompressedApexInfoList infoList)1163 public long calculateSizeForCompressedApex(CompressedApexInfoList infoList) { 1164 throw new UnsupportedOperationException(); 1165 } 1166 1167 @Override reserveSpaceForCompressedApex(CompressedApexInfoList infoList)1168 public void reserveSpaceForCompressedApex(CompressedApexInfoList infoList) { 1169 throw new UnsupportedOperationException(); 1170 } 1171 1172 @Override installPackage(File apexFile)1173 ApexInfo installPackage(File apexFile) { 1174 throw new UnsupportedOperationException("APEX updates are not supported"); 1175 } 1176 1177 @Override getApexSystemServices()1178 public List<ApexSystemServiceInfo> getApexSystemServices() { 1179 // TODO(satayev): we can't really support flattened apex use case, and need to migrate 1180 // the manifest entries into system's manifest asap. 1181 return Collections.emptyList(); 1182 } 1183 1184 @Override dump(PrintWriter pw)1185 void dump(PrintWriter pw) { 1186 } 1187 1188 @Override getBackingApexFile(File file)1189 public File getBackingApexFile(File file) { 1190 return null; 1191 } 1192 } 1193 } 1194