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.text.TextUtils; 39 import android.util.ArrayMap; 40 import android.util.ArraySet; 41 import android.util.Singleton; 42 import android.util.Slog; 43 import android.util.SparseArray; 44 import android.util.apk.ApkSignatureVerifier; 45 46 import com.android.internal.annotations.GuardedBy; 47 import com.android.internal.annotations.VisibleForTesting; 48 import com.android.internal.pm.pkg.component.ParsedApexSystemService; 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.utils.TimingsTraceAndSlog; 54 55 import com.google.android.collect.Lists; 56 57 import java.io.File; 58 import java.io.PrintWriter; 59 import java.lang.annotation.Retention; 60 import java.lang.annotation.RetentionPolicy; 61 import java.nio.file.Path; 62 import java.util.ArrayList; 63 import java.util.Collections; 64 import java.util.List; 65 import java.util.Map; 66 import java.util.Objects; 67 import java.util.Set; 68 69 /** 70 * ApexManager class handles communications with the apex service to perform operation and queries, 71 * as well as providing caching to avoid unnecessary calls to the service. 72 */ 73 public abstract class ApexManager { 74 75 private static final String TAG = "ApexManager"; 76 77 public static final int MATCH_ACTIVE_PACKAGE = 1 << 0; 78 static final int MATCH_FACTORY_PACKAGE = 1 << 1; 79 80 private static final Singleton<ApexManager> sApexManagerSingleton = 81 new Singleton<ApexManager>() { 82 @Override 83 protected ApexManager create() { 84 return new ApexManagerImpl(); 85 } 86 }; 87 88 /** 89 * Returns an instance of {@link ApexManagerImpl} 90 * @hide 91 */ getInstance()92 public static ApexManager getInstance() { 93 return sApexManagerSingleton.get(); 94 } 95 96 static class ScanResult { 97 public final ApexInfo apexInfo; 98 public final AndroidPackage pkg; 99 public final String packageName; ScanResult(ApexInfo apexInfo, AndroidPackage pkg, String packageName)100 ScanResult(ApexInfo apexInfo, AndroidPackage pkg, String packageName) { 101 this.apexInfo = apexInfo; 102 this.pkg = pkg; 103 this.packageName = packageName; 104 } 105 } 106 107 /** 108 * Minimal information about APEX mount points and the original APEX package they refer to. 109 * @hide 110 */ 111 public static class ActiveApexInfo { 112 @Nullable public final String apexModuleName; 113 public final File apexDirectory; 114 public final File preInstalledApexPath; 115 public final boolean isFactory; 116 public final File apexFile; 117 public final boolean activeApexChanged; 118 ActiveApexInfo(File apexDirectory, File preInstalledApexPath, File apexFile)119 private ActiveApexInfo(File apexDirectory, File preInstalledApexPath, File apexFile) { 120 this(null, apexDirectory, preInstalledApexPath, true, apexFile, false); 121 } 122 ActiveApexInfo(@ullable String apexModuleName, File apexDirectory, File preInstalledApexPath, boolean isFactory, File apexFile, boolean activeApexChanged)123 private ActiveApexInfo(@Nullable String apexModuleName, File apexDirectory, 124 File preInstalledApexPath, boolean isFactory, File apexFile, 125 boolean activeApexChanged) { 126 this.apexModuleName = apexModuleName; 127 this.apexDirectory = apexDirectory; 128 this.preInstalledApexPath = preInstalledApexPath; 129 this.isFactory = isFactory; 130 this.apexFile = apexFile; 131 this.activeApexChanged = activeApexChanged; 132 } 133 ActiveApexInfo(ApexInfo apexInfo)134 public ActiveApexInfo(ApexInfo apexInfo) { 135 this( 136 apexInfo.moduleName, 137 new File(Environment.getApexDirectory() + File.separator 138 + apexInfo.moduleName), 139 new File(apexInfo.preinstalledModulePath), 140 apexInfo.isFactory, 141 new File(apexInfo.modulePath), 142 apexInfo.activeApexChanged); 143 } 144 } 145 getAllApexInfos()146 abstract ApexInfo[] getAllApexInfos(); notifyScanResult(List<ScanResult> scanResults)147 abstract void notifyScanResult(List<ScanResult> scanResults); 148 149 /** 150 * Returns {@link ActiveApexInfo} records relative to all active APEX packages. 151 * 152 * @hide 153 */ getActiveApexInfos()154 public abstract List<ActiveApexInfo> getActiveApexInfos(); 155 156 /** 157 * Returns the active apex package's name that contains the (apk) package. 158 * 159 * @param containedPackageName The (apk) package that might be in a apex 160 * @return the apex package's name of {@code null} if the {@code containedPackage} is not inside 161 * any apex. 162 */ 163 @Nullable getActiveApexPackageNameContainingPackage( @onNull String containedPackageName)164 public abstract String getActiveApexPackageNameContainingPackage( 165 @NonNull String containedPackageName); 166 167 /** 168 * Retrieves information about an apexd staged session i.e. the internal state used by apexd to 169 * track the different states of a session. 170 * 171 * @param sessionId the identifier of the session. 172 * @return an ApexSessionInfo object, or null if the session is not known. 173 */ 174 @Nullable getStagedSessionInfo(int sessionId)175 abstract ApexSessionInfo getStagedSessionInfo(int sessionId); 176 177 /** 178 * Returns array of all staged sessions known to apexd. 179 */ 180 @NonNull getSessions()181 abstract SparseArray<ApexSessionInfo> getSessions(); 182 183 /** 184 * Submit a staged session to apex service. This causes the apex service to perform some initial 185 * verification and accept or reject the session. Submitting a session successfully is not 186 * enough for it to be activated at the next boot, the caller needs to call 187 * {@link #markStagedSessionReady(int)}. 188 * 189 * @throws PackageManagerException if call to apexd fails 190 */ submitStagedSession(ApexSessionParams params)191 abstract ApexInfoList submitStagedSession(ApexSessionParams params) 192 throws PackageManagerException; 193 194 /** 195 * Returns {@code ApeInfo} about apex sessions that have been marked ready via 196 * {@link #markStagedSessionReady(int)} 197 * 198 * Returns empty array if there is no staged apex session or if there is any error. 199 */ getStagedApexInfos(ApexSessionParams params)200 abstract ApexInfo[] getStagedApexInfos(ApexSessionParams params); 201 202 /** 203 * Mark a staged session previously submitted using {@code submitStagedSession} as ready to be 204 * applied at next reboot. 205 * 206 * @param sessionId the identifier of the {@link PackageInstallerSession} being marked as ready. 207 * @throws PackageManagerException if call to apexd fails 208 */ markStagedSessionReady(int sessionId)209 abstract void markStagedSessionReady(int sessionId) throws PackageManagerException; 210 211 /** 212 * Marks a staged session as successful. 213 * 214 * <p>Only activated session can be marked as successful. 215 * 216 * @param sessionId the identifier of the {@link PackageInstallerSession} being marked as 217 * successful. 218 */ markStagedSessionSuccessful(int sessionId)219 abstract void markStagedSessionSuccessful(int sessionId); 220 221 /** 222 * Whether the current device supports the management of APEX packages. 223 * 224 * @return true if APEX packages can be managed on this device, false otherwise. 225 */ isApexSupported()226 abstract boolean isApexSupported(); 227 228 /** 229 * Abandons the (only) active session previously submitted. 230 * 231 * @return {@code true} upon success, {@code false} if any remote exception occurs 232 */ revertActiveSessions()233 abstract boolean revertActiveSessions(); 234 235 /** 236 * Abandons the staged session with the given sessionId. Client should handle {@code false} 237 * return value carefully as failure here can leave device in inconsistent state. 238 * 239 * @return {@code true} upon success, {@code false} if any exception occurs 240 */ abortStagedSession(int sessionId)241 abstract boolean abortStagedSession(int sessionId); 242 243 /** 244 * Uninstalls given {@code apexPackage}. 245 * 246 * <p>NOTE. Device must be rebooted in order for uninstall to take effect. 247 * 248 * @param apexPackagePath package to uninstall. 249 * @return {@code true} upon successful uninstall, {@code false} otherwise. 250 */ uninstallApex(String apexPackagePath)251 abstract boolean uninstallApex(String apexPackagePath); 252 253 /** 254 * Registers an APK package as an embedded apk of apex. 255 */ registerApkInApex(AndroidPackage pkg)256 abstract void registerApkInApex(AndroidPackage pkg); 257 258 /** 259 * Reports error raised during installation of apk-in-apex. 260 * 261 * @param scanDirPath the directory of the apex inside which apk-in-apex resides. 262 * @param errorMsg the actual error that occurred when scanning the path 263 */ reportErrorWithApkInApex(String scanDirPath, String errorMsg)264 abstract void reportErrorWithApkInApex(String scanDirPath, String errorMsg); 265 266 /** 267 * Returns null if there were no errors when installing apk-in-apex inside 268 * {@param apexPackageName}, otherwise returns the error as string 269 * 270 * @param apexPackageName Package name of the apk container of apex 271 */ 272 @Nullable getApkInApexInstallError(String apexPackageName)273 abstract String getApkInApexInstallError(String apexPackageName); 274 275 /** 276 * Returns list of {@code packageName} of apks inside the given apex. 277 * @param apexPackageName Package name of the apk container of apex 278 */ 279 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) getApksInApex(String apexPackageName)280 public abstract List<String> getApksInApex(String apexPackageName); 281 282 /** 283 * Returns the apex module name for the given package name, if the package is an APEX. Otherwise 284 * returns {@code null}. 285 */ 286 @Nullable getApexModuleNameForPackageName(String apexPackageName)287 public abstract String getApexModuleNameForPackageName(String apexPackageName); 288 289 /** 290 * Returns the package name of the active APEX whose name is {@code apexModuleName}. If not 291 * found, returns {@code null}. 292 */ 293 @Nullable getActivePackageNameForApexModuleName(String apexModuleName)294 public abstract String getActivePackageNameForApexModuleName(String apexModuleName); 295 296 /** 297 * Copies the CE apex data directory for the given {@code userId} to a backup location, for use 298 * in case of rollback. 299 * 300 * @return boolean true if the snapshot was successful 301 */ snapshotCeData(int userId, int rollbackId, String apexPackageName)302 public abstract boolean snapshotCeData(int userId, int rollbackId, String apexPackageName); 303 304 /** 305 * Restores the snapshot of the CE apex data directory for the given {@code userId}. 306 * Note the snapshot will be deleted after restoration succeeded. 307 * 308 * @return boolean true if the restore was successful 309 */ restoreCeData(int userId, int rollbackId, String apexPackageName)310 public abstract boolean restoreCeData(int userId, int rollbackId, String apexPackageName); 311 312 /** 313 * Deletes snapshots of the device encrypted apex data directories for the given 314 * {@code rollbackId}. 315 * 316 * @return boolean true if the delete was successful 317 */ destroyDeSnapshots(int rollbackId)318 public abstract boolean destroyDeSnapshots(int rollbackId); 319 320 /** 321 * Deletes snapshots of the credential encrypted apex data directories for the specified user, 322 * for the given rollback id as long as the user is credential unlocked. 323 * 324 * @return boolean true if the delete was successful 325 */ destroyCeSnapshots(int userId, int rollbackId)326 public abstract boolean destroyCeSnapshots(int userId, int rollbackId); 327 328 /** 329 * Deletes snapshots of the credential encrypted apex data directories for the specified user, 330 * where the rollback id is not included in {@code retainRollbackIds} as long as the user is 331 * credential unlocked. 332 * 333 * @return boolean true if the delete was successful 334 */ destroyCeSnapshotsNotSpecified(int userId, int[] retainRollbackIds)335 public abstract boolean destroyCeSnapshotsNotSpecified(int userId, int[] retainRollbackIds); 336 337 /** 338 * Inform apexd that the boot has completed. 339 */ markBootCompleted()340 public abstract void markBootCompleted(); 341 342 /** 343 * Estimate how much storage space is needed on /data/ for decompressing apexes 344 * @param infoList List of apexes that are compressed in target build. 345 * @return Size, in bytes, the amount of space needed on /data/ 346 */ calculateSizeForCompressedApex(CompressedApexInfoList infoList)347 public abstract long calculateSizeForCompressedApex(CompressedApexInfoList infoList) 348 throws RemoteException; 349 350 /** 351 * Reserve space on /data so that apexes can be decompressed after OTA 352 * @param infoList List of apexes that are compressed in target build. 353 */ reserveSpaceForCompressedApex(CompressedApexInfoList infoList)354 public abstract void reserveSpaceForCompressedApex(CompressedApexInfoList infoList) 355 throws RemoteException; 356 357 /** 358 * Performs a non-staged install of the given {@code apexFile}. 359 * 360 * If {@code force} is {@code true}, then update is forced even for APEXes that do not support 361 * non-staged update. This feature is only available on debuggable builds to improve development 362 * velocity of the teams that have their code packaged in an APEX. 363 * 364 * @return {@code ApeInfo} about the newly installed APEX package. 365 */ installPackage(File apexFile, boolean force)366 abstract ApexInfo installPackage(File apexFile, boolean force) throws PackageManagerException; 367 368 /** 369 * Get a list of apex system services implemented in an apex. 370 * 371 * <p>The list is sorted by initOrder for consistency. 372 */ getApexSystemServices()373 public abstract List<ApexSystemServiceInfo> getApexSystemServices(); 374 375 /** 376 * Returns an APEX file backing the mount point {@code file} is located on, or {@code null} if 377 * {@code file} doesn't belong to a {@code /apex} mount point. 378 * 379 * <p>Also returns {@code null} if device doesn't support updatable APEX packages. 380 */ 381 @Nullable getBackingApexFile(@onNull File file)382 public abstract File getBackingApexFile(@NonNull File file); 383 384 /** 385 * Dumps various state information to the provided {@link PrintWriter} object. 386 * 387 * @param pw the {@link PrintWriter} object to send information to. 388 */ dump(PrintWriter pw)389 abstract void dump(PrintWriter pw); 390 391 @IntDef( 392 flag = true, 393 prefix = { "MATCH_"}, 394 value = {MATCH_ACTIVE_PACKAGE, MATCH_FACTORY_PACKAGE}) 395 @Retention(RetentionPolicy.SOURCE) 396 @interface PackageInfoFlags{} 397 398 /** 399 * An implementation of {@link ApexManager} that should be used in case device supports updating 400 * APEX packages. 401 */ 402 @VisibleForTesting 403 protected static class ApexManagerImpl extends ApexManager { 404 private final Object mLock = new Object(); 405 406 // TODO(ioffe): this should be either List or ArrayMap. 407 @GuardedBy("mLock") 408 private Set<ActiveApexInfo> mActiveApexInfosCache; 409 410 /** 411 * Map of all apex system services to the jar files they are contained in. 412 */ 413 @GuardedBy("mLock") 414 private final List<ApexSystemServiceInfo> mApexSystemServices = new ArrayList<>(); 415 416 /** 417 * Contains the list of {@code packageName}s of apks-in-apex for given 418 * {@code apexModuleName}. See {@link #mPackageNameToApexModuleName} to understand the 419 * difference between {@code packageName} and {@code apexModuleName}. 420 */ 421 @GuardedBy("mLock") 422 private final ArrayMap<String, List<String>> mApksInApex = new ArrayMap<>(); 423 424 /** 425 * Contains the list of {@code Exception}s that were raised when installing apk-in-apex 426 * inside {@code apexModuleName}. 427 */ 428 @GuardedBy("mLock") 429 private final Map<String, String> mErrorWithApkInApex = new ArrayMap<>(); 430 431 /** 432 * An APEX is a file format that delivers the apex-payload wrapped in an apk container. The 433 * apk container has a reference name, called {@code packageName}, which is found inside the 434 * {@code AndroidManifest.xml}. The apex payload inside the container also has a reference 435 * name, called {@code apexModuleName}, which is found in {@code apex_manifest.json} file. 436 * 437 * {@link #mPackageNameToApexModuleName} contains the mapping from {@code packageName} of 438 * the apk container to {@code apexModuleName} of the apex-payload inside. 439 */ 440 @GuardedBy("mLock") 441 private ArrayMap<String, String> mPackageNameToApexModuleName; 442 443 /** 444 * Reverse mapping of {@link #mPackageNameToApexModuleName}, for active packages only. 445 */ 446 @GuardedBy("mLock") 447 private ArrayMap<String, String> mApexModuleNameToActivePackageName; 448 449 /** 450 * Retrieve the service from ServiceManager. If the service is not running, it will be 451 * started, and this function will block until it is ready. 452 */ 453 @VisibleForTesting waitForApexService()454 protected IApexService waitForApexService() { 455 // Since apexd is a trusted platform component, synchronized calls are allowable 456 return IApexService.Stub.asInterface( 457 Binder.allowBlocking(ServiceManager.waitForService("apexservice"))); 458 } 459 460 @Override getAllApexInfos()461 ApexInfo[] getAllApexInfos() { 462 try { 463 return waitForApexService().getAllPackages(); 464 } catch (RemoteException re) { 465 Slog.e(TAG, "Unable to retrieve packages from apexservice: " + re.toString()); 466 throw new RuntimeException(re); 467 } 468 } 469 470 @Override notifyScanResult(List<ScanResult> scanResults)471 void notifyScanResult(List<ScanResult> scanResults) { 472 synchronized (mLock) { 473 notifyScanResultLocked(scanResults); 474 } 475 } 476 477 @GuardedBy("mLock") notifyScanResultLocked(List<ScanResult> scanResults)478 private void notifyScanResultLocked(List<ScanResult> scanResults) { 479 mPackageNameToApexModuleName = new ArrayMap<>(); 480 mApexModuleNameToActivePackageName = new ArrayMap<>(); 481 for (ScanResult scanResult : scanResults) { 482 ApexInfo ai = scanResult.apexInfo; 483 String packageName = scanResult.packageName; 484 for (ParsedApexSystemService service : 485 scanResult.pkg.getApexSystemServices()) { 486 String minSdkVersion = service.getMinSdkVersion(); 487 if (minSdkVersion != null && !UnboundedSdkLevel.isAtLeast(minSdkVersion)) { 488 Slog.d(TAG, String.format( 489 "ApexSystemService %s with min_sdk_version=%s is skipped", 490 service.getName(), service.getMinSdkVersion())); 491 continue; 492 } 493 String maxSdkVersion = service.getMaxSdkVersion(); 494 if (maxSdkVersion != null && !UnboundedSdkLevel.isAtMost(maxSdkVersion)) { 495 Slog.d(TAG, String.format( 496 "ApexSystemService %s with max_sdk_version=%s is skipped", 497 service.getName(), service.getMaxSdkVersion())); 498 continue; 499 } 500 501 if (ai.isActive) { 502 String name = service.getName(); 503 for (int j = 0; j < mApexSystemServices.size(); j++) { 504 ApexSystemServiceInfo info = mApexSystemServices.get(j); 505 if (info.getName().equals(name)) { 506 throw new IllegalStateException(TextUtils.formatSimple( 507 "Duplicate apex-system-service %s from %s, %s", name, 508 info.mJarPath, service.getJarPath())); 509 } 510 } 511 ApexSystemServiceInfo info = new ApexSystemServiceInfo( 512 service.getName(), service.getJarPath(), 513 service.getInitOrder()); 514 mApexSystemServices.add(info); 515 } 516 } 517 Collections.sort(mApexSystemServices); 518 mPackageNameToApexModuleName.put(packageName, ai.moduleName); 519 if (ai.isActive) { 520 if (mApexModuleNameToActivePackageName.containsKey(ai.moduleName)) { 521 throw new IllegalStateException( 522 "Two active packages have the same APEX module name: " 523 + ai.moduleName); 524 } 525 mApexModuleNameToActivePackageName.put( 526 ai.moduleName, packageName); 527 } 528 } 529 } 530 531 @Override getActiveApexInfos()532 public List<ActiveApexInfo> getActiveApexInfos() { 533 final TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG + "Timing", 534 Trace.TRACE_TAG_PACKAGE_MANAGER); 535 synchronized (mLock) { 536 if (mActiveApexInfosCache == null) { 537 t.traceBegin("getActiveApexInfos_noCache"); 538 try { 539 mActiveApexInfosCache = new ArraySet<>(); 540 final ApexInfo[] activePackages = waitForApexService().getActivePackages(); 541 for (int i = 0; i < activePackages.length; i++) { 542 ApexInfo apexInfo = activePackages[i]; 543 mActiveApexInfosCache.add(new ActiveApexInfo(apexInfo)); 544 } 545 } catch (RemoteException e) { 546 Slog.e(TAG, "Unable to retrieve packages from apexservice", e); 547 } 548 t.traceEnd(); 549 } 550 if (mActiveApexInfosCache != null) { 551 return new ArrayList<>(mActiveApexInfosCache); 552 } else { 553 return Collections.emptyList(); 554 } 555 } 556 } 557 558 @Override 559 @Nullable getActiveApexPackageNameContainingPackage(String containedPackageName)560 public String getActiveApexPackageNameContainingPackage(String containedPackageName) { 561 Objects.requireNonNull(containedPackageName); 562 synchronized (mLock) { 563 Preconditions.checkState(mPackageNameToApexModuleName != null, 564 "APEX packages have not been scanned"); 565 int numApksInApex = mApksInApex.size(); 566 for (int apkInApexNum = 0; apkInApexNum < numApksInApex; apkInApexNum++) { 567 if (mApksInApex.valueAt(apkInApexNum).contains(containedPackageName)) { 568 String apexModuleName = mApksInApex.keyAt(apkInApexNum); 569 570 int numApexPkgs = mPackageNameToApexModuleName.size(); 571 for (int apexPkgNum = 0; apexPkgNum < numApexPkgs; apexPkgNum++) { 572 if (mPackageNameToApexModuleName.valueAt(apexPkgNum).equals( 573 apexModuleName)) { 574 return mPackageNameToApexModuleName.keyAt(apexPkgNum); 575 } 576 } 577 } 578 } 579 } 580 581 return null; 582 } 583 584 @Override getStagedSessionInfo(int sessionId)585 @Nullable ApexSessionInfo getStagedSessionInfo(int sessionId) { 586 try { 587 ApexSessionInfo apexSessionInfo = 588 waitForApexService().getStagedSessionInfo(sessionId); 589 if (apexSessionInfo.isUnknown) { 590 return null; 591 } 592 return apexSessionInfo; 593 } catch (RemoteException re) { 594 Slog.e(TAG, "Unable to contact apexservice", re); 595 throw new RuntimeException(re); 596 } 597 } 598 599 @Override getSessions()600 SparseArray<ApexSessionInfo> getSessions() { 601 try { 602 final ApexSessionInfo[] sessions = waitForApexService().getSessions(); 603 final SparseArray<ApexSessionInfo> result = new SparseArray<>(sessions.length); 604 for (int i = 0; i < sessions.length; i++) { 605 result.put(sessions[i].sessionId, sessions[i]); 606 } 607 return result; 608 } catch (RemoteException re) { 609 Slog.e(TAG, "Unable to contact apexservice", re); 610 throw new RuntimeException(re); 611 } 612 } 613 614 @Override submitStagedSession(ApexSessionParams params)615 ApexInfoList submitStagedSession(ApexSessionParams params) throws PackageManagerException { 616 try { 617 final ApexInfoList apexInfoList = new ApexInfoList(); 618 waitForApexService().submitStagedSession(params, apexInfoList); 619 return apexInfoList; 620 } catch (RemoteException re) { 621 Slog.e(TAG, "Unable to contact apexservice", re); 622 throw new RuntimeException(re); 623 } catch (Exception e) { 624 throw new PackageManagerException( 625 PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE, 626 "apexd verification failed : " + e.getMessage()); 627 } 628 } 629 630 @Override getStagedApexInfos(ApexSessionParams params)631 ApexInfo[] getStagedApexInfos(ApexSessionParams params) { 632 try { 633 return waitForApexService().getStagedApexInfos(params); 634 } catch (RemoteException re) { 635 Slog.w(TAG, "Unable to contact apexservice" + re.getMessage()); 636 throw new RuntimeException(re); 637 } catch (Exception e) { 638 Slog.w(TAG, "Failed to collect staged apex infos" + e.getMessage()); 639 return new ApexInfo[0]; 640 } 641 } 642 643 @Override markStagedSessionReady(int sessionId)644 void markStagedSessionReady(int sessionId) throws PackageManagerException { 645 try { 646 waitForApexService().markStagedSessionReady(sessionId); 647 } catch (RemoteException re) { 648 Slog.e(TAG, "Unable to contact apexservice", re); 649 throw new RuntimeException(re); 650 } catch (Exception e) { 651 throw new PackageManagerException( 652 PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE, 653 "Failed to mark apexd session as ready : " + e.getMessage()); 654 } 655 } 656 657 @Override markStagedSessionSuccessful(int sessionId)658 void markStagedSessionSuccessful(int sessionId) { 659 try { 660 waitForApexService().markStagedSessionSuccessful(sessionId); 661 } catch (RemoteException re) { 662 Slog.e(TAG, "Unable to contact apexservice", re); 663 throw new RuntimeException(re); 664 } catch (Exception e) { 665 // It is fine to just log an exception in this case. APEXd will be able to recover 666 // in case markStagedSessionSuccessful fails. 667 Slog.e(TAG, "Failed to mark session " + sessionId + " as successful", e); 668 } 669 } 670 671 @Override isApexSupported()672 boolean isApexSupported() { 673 return true; 674 } 675 676 @Override revertActiveSessions()677 boolean revertActiveSessions() { 678 try { 679 waitForApexService().revertActiveSessions(); 680 return true; 681 } catch (RemoteException re) { 682 Slog.e(TAG, "Unable to contact apexservice", re); 683 return false; 684 } catch (Exception e) { 685 Slog.e(TAG, e.getMessage(), e); 686 return false; 687 } 688 } 689 690 @Override abortStagedSession(int sessionId)691 boolean abortStagedSession(int sessionId) { 692 try { 693 waitForApexService().abortStagedSession(sessionId); 694 return true; 695 } catch (Exception e) { 696 Slog.e(TAG, e.getMessage(), e); 697 return false; 698 } 699 } 700 701 @Override uninstallApex(String apexPackagePath)702 boolean uninstallApex(String apexPackagePath) { 703 try { 704 waitForApexService().unstagePackages(Collections.singletonList(apexPackagePath)); 705 return true; 706 } catch (Exception e) { 707 return false; 708 } 709 } 710 711 @Override registerApkInApex(AndroidPackage pkg)712 void registerApkInApex(AndroidPackage pkg) { 713 synchronized (mLock) { 714 for (ActiveApexInfo aai : mActiveApexInfosCache) { 715 if (pkg.getBaseApkPath().startsWith( 716 aai.apexDirectory.getAbsolutePath() + File.separator)) { 717 List<String> apks = mApksInApex.get(aai.apexModuleName); 718 if (apks == null) { 719 apks = Lists.newArrayList(); 720 mApksInApex.put(aai.apexModuleName, apks); 721 } 722 Slog.i(TAG, "Registering " + pkg.getPackageName() + " as apk-in-apex of " 723 + aai.apexModuleName); 724 apks.add(pkg.getPackageName()); 725 } 726 } 727 } 728 } 729 730 @Override reportErrorWithApkInApex(String scanDirPath, String errorMsg)731 void reportErrorWithApkInApex(String scanDirPath, String errorMsg) { 732 synchronized (mLock) { 733 for (ActiveApexInfo aai : mActiveApexInfosCache) { 734 if (scanDirPath.startsWith(aai.apexDirectory.getAbsolutePath())) { 735 mErrorWithApkInApex.put(aai.apexModuleName, errorMsg); 736 } 737 } 738 } 739 } 740 741 @Override 742 @Nullable getApkInApexInstallError(String apexPackageName)743 String getApkInApexInstallError(String apexPackageName) { 744 synchronized (mLock) { 745 Preconditions.checkState(mPackageNameToApexModuleName != null, 746 "APEX packages have not been scanned"); 747 String moduleName = mPackageNameToApexModuleName.get(apexPackageName); 748 if (moduleName == null) { 749 return null; 750 } 751 return mErrorWithApkInApex.get(moduleName); 752 } 753 } 754 755 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) 756 @Override getApksInApex(String apexPackageName)757 public List<String> getApksInApex(String apexPackageName) { 758 synchronized (mLock) { 759 Preconditions.checkState(mPackageNameToApexModuleName != null, 760 "APEX packages have not been scanned"); 761 String moduleName = mPackageNameToApexModuleName.get(apexPackageName); 762 if (moduleName == null) { 763 return Collections.emptyList(); 764 } 765 return mApksInApex.getOrDefault(moduleName, Collections.emptyList()); 766 } 767 } 768 769 @Override 770 @Nullable getApexModuleNameForPackageName(String apexPackageName)771 public String getApexModuleNameForPackageName(String apexPackageName) { 772 synchronized (mLock) { 773 Preconditions.checkState(mPackageNameToApexModuleName != null, 774 "APEX packages have not been scanned"); 775 return mPackageNameToApexModuleName.get(apexPackageName); 776 } 777 } 778 779 @Override 780 @Nullable getActivePackageNameForApexModuleName(String apexModuleName)781 public String getActivePackageNameForApexModuleName(String apexModuleName) { 782 synchronized (mLock) { 783 Preconditions.checkState(mApexModuleNameToActivePackageName != null, 784 "APEX packages have not been scanned"); 785 return mApexModuleNameToActivePackageName.get(apexModuleName); 786 } 787 } 788 789 @Override snapshotCeData(int userId, int rollbackId, String apexPackageName)790 public boolean snapshotCeData(int userId, int rollbackId, String apexPackageName) { 791 String apexModuleName; 792 synchronized (mLock) { 793 Preconditions.checkState(mPackageNameToApexModuleName != null, 794 "APEX packages have not been scanned"); 795 apexModuleName = mPackageNameToApexModuleName.get(apexPackageName); 796 } 797 if (apexModuleName == null) { 798 Slog.e(TAG, "Invalid apex package name: " + apexPackageName); 799 return false; 800 } 801 try { 802 waitForApexService().snapshotCeData(userId, rollbackId, apexModuleName); 803 return true; 804 } catch (Exception e) { 805 Slog.e(TAG, e.getMessage(), e); 806 return false; 807 } 808 } 809 810 @Override restoreCeData(int userId, int rollbackId, String apexPackageName)811 public boolean restoreCeData(int userId, int rollbackId, String apexPackageName) { 812 String apexModuleName; 813 synchronized (mLock) { 814 Preconditions.checkState(mPackageNameToApexModuleName != null, 815 "APEX packages have not been scanned"); 816 apexModuleName = mPackageNameToApexModuleName.get(apexPackageName); 817 } 818 if (apexModuleName == null) { 819 Slog.e(TAG, "Invalid apex package name: " + apexPackageName); 820 return false; 821 } 822 try { 823 waitForApexService().restoreCeData(userId, rollbackId, apexModuleName); 824 return true; 825 } catch (Exception e) { 826 Slog.e(TAG, e.getMessage(), e); 827 return false; 828 } 829 } 830 831 @Override destroyDeSnapshots(int rollbackId)832 public boolean destroyDeSnapshots(int rollbackId) { 833 try { 834 waitForApexService().destroyDeSnapshots(rollbackId); 835 return true; 836 } catch (Exception e) { 837 Slog.e(TAG, e.getMessage(), e); 838 return false; 839 } 840 } 841 842 @Override destroyCeSnapshots(int userId, int rollbackId)843 public boolean destroyCeSnapshots(int userId, int rollbackId) { 844 try { 845 waitForApexService().destroyCeSnapshots(userId, rollbackId); 846 return true; 847 } catch (Exception e) { 848 Slog.e(TAG, e.getMessage(), e); 849 return false; 850 } 851 } 852 853 @Override destroyCeSnapshotsNotSpecified(int userId, int[] retainRollbackIds)854 public boolean destroyCeSnapshotsNotSpecified(int userId, int[] retainRollbackIds) { 855 try { 856 waitForApexService().destroyCeSnapshotsNotSpecified(userId, retainRollbackIds); 857 return true; 858 } catch (Exception e) { 859 Slog.e(TAG, e.getMessage(), e); 860 return false; 861 } 862 } 863 864 @Override markBootCompleted()865 public void markBootCompleted() { 866 try { 867 waitForApexService().markBootCompleted(); 868 } catch (RemoteException re) { 869 Slog.e(TAG, "Unable to contact apexservice", re); 870 } 871 } 872 873 @Override calculateSizeForCompressedApex(CompressedApexInfoList infoList)874 public long calculateSizeForCompressedApex(CompressedApexInfoList infoList) 875 throws RemoteException { 876 return waitForApexService().calculateSizeForCompressedApex(infoList); 877 } 878 879 @Override reserveSpaceForCompressedApex(CompressedApexInfoList infoList)880 public void reserveSpaceForCompressedApex(CompressedApexInfoList infoList) 881 throws RemoteException { 882 waitForApexService().reserveSpaceForCompressedApex(infoList); 883 } 884 getSigningDetails(PackageInfo pkg)885 private SigningDetails getSigningDetails(PackageInfo pkg) throws PackageManagerException { 886 final int minSignatureScheme = 887 ApkSignatureVerifier.getMinimumSignatureSchemeVersionForTargetSdk( 888 pkg.applicationInfo.targetSdkVersion); 889 final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing(); 890 final ParseResult<SigningDetails> result = ApkSignatureVerifier.verify( 891 input, pkg.applicationInfo.sourceDir, minSignatureScheme); 892 if (result.isError()) { 893 throw new PackageManagerException(result.getErrorCode(), result.getErrorMessage(), 894 result.getException()); 895 } 896 return result.getResult(); 897 } 898 checkApexSignature(PackageInfo existingApexPkg, PackageInfo newApexPkg)899 private void checkApexSignature(PackageInfo existingApexPkg, PackageInfo newApexPkg) 900 throws PackageManagerException { 901 final SigningDetails existingSigningDetails = getSigningDetails(existingApexPkg); 902 final SigningDetails newSigningDetails = getSigningDetails(newApexPkg); 903 if (!newSigningDetails.checkCapability(existingSigningDetails, 904 SigningDetails.CertCapabilities.INSTALLED_DATA)) { 905 throw new PackageManagerException(PackageManager.INSTALL_FAILED_BAD_SIGNATURE, 906 "APK container signature of " + newApexPkg.applicationInfo.sourceDir 907 + " is not compatible with currently installed on device"); 908 } 909 } 910 911 @Override installPackage(File apexFile, boolean force)912 ApexInfo installPackage(File apexFile, boolean force) 913 throws PackageManagerException { 914 try { 915 return waitForApexService().installAndActivatePackage(apexFile.getAbsolutePath(), 916 force); 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