1 /* 2 * Copyright (C) 2014 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.app.AppOpsManager.MODE_DEFAULT; 20 import static android.app.admin.DevicePolicyResources.Strings.Core.PACKAGE_INSTALLED_BY_DO; 21 import static android.app.admin.DevicePolicyResources.Strings.Core.PACKAGE_UPDATED_BY_DO; 22 import static android.content.pm.DataLoaderType.INCREMENTAL; 23 import static android.content.pm.DataLoaderType.STREAMING; 24 import static android.content.pm.PackageInstaller.LOCATION_DATA_APP; 25 import static android.content.pm.PackageManager.INSTALL_FAILED_ABORTED; 26 import static android.content.pm.PackageManager.INSTALL_FAILED_BAD_SIGNATURE; 27 import static android.content.pm.PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; 28 import static android.content.pm.PackageManager.INSTALL_FAILED_INTERNAL_ERROR; 29 import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK; 30 import static android.content.pm.PackageManager.INSTALL_FAILED_MEDIA_UNAVAILABLE; 31 import static android.content.pm.PackageManager.INSTALL_FAILED_MISSING_SPLIT; 32 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES; 33 import static android.content.pm.PackageManager.INSTALL_STAGED; 34 import static android.content.pm.PackageManager.INSTALL_SUCCEEDED; 35 import static android.system.OsConstants.O_CREAT; 36 import static android.system.OsConstants.O_RDONLY; 37 import static android.system.OsConstants.O_WRONLY; 38 39 import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE; 40 import static com.android.internal.util.XmlUtils.readBitmapAttribute; 41 import static com.android.internal.util.XmlUtils.readByteArrayAttribute; 42 import static com.android.internal.util.XmlUtils.readStringAttribute; 43 import static com.android.internal.util.XmlUtils.readUriAttribute; 44 import static com.android.internal.util.XmlUtils.writeBooleanAttribute; 45 import static com.android.internal.util.XmlUtils.writeByteArrayAttribute; 46 import static com.android.internal.util.XmlUtils.writeStringAttribute; 47 import static com.android.internal.util.XmlUtils.writeUriAttribute; 48 import static com.android.server.pm.PackageInstallerService.prepareStageDir; 49 50 import android.Manifest; 51 import android.annotation.AnyThread; 52 import android.annotation.IntDef; 53 import android.annotation.NonNull; 54 import android.annotation.Nullable; 55 import android.annotation.WorkerThread; 56 import android.app.AppOpsManager; 57 import android.app.BroadcastOptions; 58 import android.app.Notification; 59 import android.app.NotificationManager; 60 import android.app.admin.DevicePolicyEventLogger; 61 import android.app.admin.DevicePolicyManager; 62 import android.app.admin.DevicePolicyManagerInternal; 63 import android.content.ComponentName; 64 import android.content.Context; 65 import android.content.Intent; 66 import android.content.IntentSender; 67 import android.content.pm.ApplicationInfo; 68 import android.content.pm.Checksum; 69 import android.content.pm.DataLoaderManager; 70 import android.content.pm.DataLoaderParams; 71 import android.content.pm.DataLoaderParamsParcel; 72 import android.content.pm.FileSystemControlParcel; 73 import android.content.pm.IDataLoader; 74 import android.content.pm.IDataLoaderStatusListener; 75 import android.content.pm.IOnChecksumsReadyListener; 76 import android.content.pm.IPackageInstallObserver2; 77 import android.content.pm.IPackageInstallerSession; 78 import android.content.pm.IPackageInstallerSessionFileSystemConnector; 79 import android.content.pm.IPackageLoadingProgressCallback; 80 import android.content.pm.InstallSourceInfo; 81 import android.content.pm.InstallationFile; 82 import android.content.pm.InstallationFileParcel; 83 import android.content.pm.PackageInfo; 84 import android.content.pm.PackageInstaller; 85 import android.content.pm.PackageInstaller.SessionInfo; 86 import android.content.pm.PackageInstaller.SessionParams; 87 import android.content.pm.PackageManager; 88 import android.content.pm.PackageManagerInternal; 89 import android.content.pm.SigningDetails; 90 import android.content.pm.dex.DexMetadataHelper; 91 import android.content.pm.parsing.ApkLite; 92 import android.content.pm.parsing.ApkLiteParseUtils; 93 import android.content.pm.parsing.PackageLite; 94 import android.content.pm.parsing.result.ParseResult; 95 import android.content.pm.parsing.result.ParseTypeImpl; 96 import android.graphics.Bitmap; 97 import android.graphics.BitmapFactory; 98 import android.os.Binder; 99 import android.os.Build; 100 import android.os.Bundle; 101 import android.os.FileBridge; 102 import android.os.FileUtils; 103 import android.os.Handler; 104 import android.os.Looper; 105 import android.os.Message; 106 import android.os.ParcelFileDescriptor; 107 import android.os.ParcelableException; 108 import android.os.Process; 109 import android.os.RemoteException; 110 import android.os.RevocableFileDescriptor; 111 import android.os.SELinux; 112 import android.os.SystemProperties; 113 import android.os.UserHandle; 114 import android.os.incremental.IStorageHealthListener; 115 import android.os.incremental.IncrementalFileStorages; 116 import android.os.incremental.IncrementalManager; 117 import android.os.incremental.PerUidReadTimeouts; 118 import android.os.incremental.StorageHealthCheckParams; 119 import android.os.storage.StorageManager; 120 import android.provider.Settings.Secure; 121 import android.stats.devicepolicy.DevicePolicyEnums; 122 import android.system.ErrnoException; 123 import android.system.Int64Ref; 124 import android.system.Os; 125 import android.system.OsConstants; 126 import android.system.StructStat; 127 import android.text.TextUtils; 128 import android.util.ArrayMap; 129 import android.util.ArraySet; 130 import android.util.EventLog; 131 import android.util.ExceptionUtils; 132 import android.util.IntArray; 133 import android.util.MathUtils; 134 import android.util.Slog; 135 import android.util.SparseArray; 136 import android.util.TypedXmlPullParser; 137 import android.util.TypedXmlSerializer; 138 import android.util.apk.ApkSignatureVerifier; 139 140 import com.android.internal.R; 141 import com.android.internal.annotations.GuardedBy; 142 import com.android.internal.annotations.VisibleForTesting; 143 import com.android.internal.content.InstallLocationUtils; 144 import com.android.internal.content.NativeLibraryHelper; 145 import com.android.internal.messages.nano.SystemMessageProto; 146 import com.android.internal.os.SomeArgs; 147 import com.android.internal.security.VerityUtils; 148 import com.android.internal.util.ArrayUtils; 149 import com.android.internal.util.CollectionUtils; 150 import com.android.internal.util.FrameworkStatsLog; 151 import com.android.internal.util.IndentingPrintWriter; 152 import com.android.internal.util.Preconditions; 153 import com.android.server.LocalServices; 154 import com.android.server.pm.Installer.InstallerException; 155 import com.android.server.pm.dex.DexManager; 156 import com.android.server.pm.parsing.pkg.AndroidPackage; 157 import com.android.server.pm.pkg.PackageStateInternal; 158 import com.android.server.pm.pkg.parsing.ParsingPackageUtils; 159 160 import libcore.io.IoUtils; 161 import libcore.util.EmptyArray; 162 163 import org.xmlpull.v1.XmlPullParser; 164 import org.xmlpull.v1.XmlPullParserException; 165 166 import java.io.ByteArrayOutputStream; 167 import java.io.File; 168 import java.io.FileDescriptor; 169 import java.io.FileFilter; 170 import java.io.FileNotFoundException; 171 import java.io.FileOutputStream; 172 import java.io.IOException; 173 import java.security.NoSuchAlgorithmException; 174 import java.security.SignatureException; 175 import java.security.cert.Certificate; 176 import java.util.ArrayList; 177 import java.util.Arrays; 178 import java.util.Collections; 179 import java.util.List; 180 import java.util.Objects; 181 import java.util.Set; 182 import java.util.concurrent.CompletableFuture; 183 import java.util.concurrent.atomic.AtomicBoolean; 184 import java.util.concurrent.atomic.AtomicInteger; 185 import java.util.function.Predicate; 186 187 public class PackageInstallerSession extends IPackageInstallerSession.Stub { 188 private static final String TAG = "PackageInstallerSession"; 189 private static final boolean LOGD = true; 190 private static final String REMOVE_MARKER_EXTENSION = ".removed"; 191 192 private static final int MSG_ON_SESSION_SEALED = 1; 193 private static final int MSG_STREAM_VALIDATE_AND_COMMIT = 2; 194 private static final int MSG_INSTALL = 3; 195 private static final int MSG_ON_PACKAGE_INSTALLED = 4; 196 private static final int MSG_SESSION_VALIDATION_FAILURE = 5; 197 198 /** XML constants used for persisting a session */ 199 static final String TAG_SESSION = "session"; 200 static final String TAG_CHILD_SESSION = "childSession"; 201 static final String TAG_SESSION_FILE = "sessionFile"; 202 static final String TAG_SESSION_CHECKSUM = "sessionChecksum"; 203 static final String TAG_SESSION_CHECKSUM_SIGNATURE = "sessionChecksumSignature"; 204 private static final String TAG_GRANTED_RUNTIME_PERMISSION = "granted-runtime-permission"; 205 private static final String TAG_WHITELISTED_RESTRICTED_PERMISSION = 206 "whitelisted-restricted-permission"; 207 private static final String TAG_AUTO_REVOKE_PERMISSIONS_MODE = 208 "auto-revoke-permissions-mode"; 209 private static final String ATTR_SESSION_ID = "sessionId"; 210 private static final String ATTR_USER_ID = "userId"; 211 private static final String ATTR_INSTALLER_PACKAGE_NAME = "installerPackageName"; 212 private static final String ATTR_INSTALLER_ATTRIBUTION_TAG = "installerAttributionTag"; 213 private static final String ATTR_INSTALLER_UID = "installerUid"; 214 private static final String ATTR_INITIATING_PACKAGE_NAME = 215 "installInitiatingPackageName"; 216 private static final String ATTR_ORIGINATING_PACKAGE_NAME = 217 "installOriginatingPackageName"; 218 private static final String ATTR_CREATED_MILLIS = "createdMillis"; 219 private static final String ATTR_UPDATED_MILLIS = "updatedMillis"; 220 private static final String ATTR_COMMITTED_MILLIS = "committedMillis"; 221 private static final String ATTR_SESSION_STAGE_DIR = "sessionStageDir"; 222 private static final String ATTR_SESSION_STAGE_CID = "sessionStageCid"; 223 private static final String ATTR_PREPARED = "prepared"; 224 private static final String ATTR_COMMITTED = "committed"; 225 private static final String ATTR_DESTROYED = "destroyed"; 226 private static final String ATTR_SEALED = "sealed"; 227 private static final String ATTR_MULTI_PACKAGE = "multiPackage"; 228 private static final String ATTR_PARENT_SESSION_ID = "parentSessionId"; 229 private static final String ATTR_STAGED_SESSION = "stagedSession"; 230 private static final String ATTR_IS_READY = "isReady"; 231 private static final String ATTR_IS_FAILED = "isFailed"; 232 private static final String ATTR_IS_APPLIED = "isApplied"; 233 private static final String ATTR_PACKAGE_SOURCE = "packageSource"; 234 private static final String ATTR_SESSION_ERROR_CODE = "errorCode"; 235 private static final String ATTR_SESSION_ERROR_MESSAGE = "errorMessage"; 236 private static final String ATTR_MODE = "mode"; 237 private static final String ATTR_INSTALL_FLAGS = "installFlags"; 238 private static final String ATTR_INSTALL_LOCATION = "installLocation"; 239 private static final String ATTR_SIZE_BYTES = "sizeBytes"; 240 private static final String ATTR_APP_PACKAGE_NAME = "appPackageName"; 241 @Deprecated 242 private static final String ATTR_APP_ICON = "appIcon"; 243 private static final String ATTR_APP_LABEL = "appLabel"; 244 private static final String ATTR_ORIGINATING_URI = "originatingUri"; 245 private static final String ATTR_ORIGINATING_UID = "originatingUid"; 246 private static final String ATTR_REFERRER_URI = "referrerUri"; 247 private static final String ATTR_ABI_OVERRIDE = "abiOverride"; 248 private static final String ATTR_VOLUME_UUID = "volumeUuid"; 249 private static final String ATTR_NAME = "name"; 250 private static final String ATTR_INSTALL_REASON = "installRason"; 251 private static final String ATTR_IS_DATALOADER = "isDataLoader"; 252 private static final String ATTR_DATALOADER_TYPE = "dataLoaderType"; 253 private static final String ATTR_DATALOADER_PACKAGE_NAME = "dataLoaderPackageName"; 254 private static final String ATTR_DATALOADER_CLASS_NAME = "dataLoaderClassName"; 255 private static final String ATTR_DATALOADER_ARGUMENTS = "dataLoaderArguments"; 256 private static final String ATTR_LOCATION = "location"; 257 private static final String ATTR_LENGTH_BYTES = "lengthBytes"; 258 private static final String ATTR_METADATA = "metadata"; 259 private static final String ATTR_SIGNATURE = "signature"; 260 private static final String ATTR_CHECKSUM_KIND = "checksumKind"; 261 private static final String ATTR_CHECKSUM_VALUE = "checksumValue"; 262 263 private static final String PROPERTY_NAME_INHERIT_NATIVE = "pi.inherit_native_on_dont_kill"; 264 private static final int[] EMPTY_CHILD_SESSION_ARRAY = EmptyArray.INT; 265 private static final InstallationFile[] EMPTY_INSTALLATION_FILE_ARRAY = {}; 266 267 private static final String SYSTEM_DATA_LOADER_PACKAGE = "android"; 268 private static final String APEX_FILE_EXTENSION = ".apex"; 269 270 private static final int INCREMENTAL_STORAGE_BLOCKED_TIMEOUT_MS = 2000; 271 private static final int INCREMENTAL_STORAGE_UNHEALTHY_TIMEOUT_MS = 7000; 272 private static final int INCREMENTAL_STORAGE_UNHEALTHY_MONITORING_MS = 60000; 273 274 /** 275 * The default value of {@link #mValidatedTargetSdk} is {@link Integer#MAX_VALUE}. If {@link 276 * #mValidatedTargetSdk} is compared with {@link Build.VERSION_CODES#R} before getting the 277 * target sdk version from a validated apk in {@link #validateApkInstallLocked()}, the compared 278 * result will not trigger any user action in 279 * {@link #checkUserActionRequirement(PackageInstallerSession)}. 280 */ 281 private static final int INVALID_TARGET_SDK_VERSION = Integer.MAX_VALUE; 282 283 // TODO: enforce INSTALL_ALLOW_TEST 284 // TODO: enforce INSTALL_ALLOW_DOWNGRADE 285 286 private final PackageInstallerService.InternalCallback mCallback; 287 private final Context mContext; 288 private final PackageManagerService mPm; 289 private final Installer mInstaller; 290 private final Handler mHandler; 291 private final PackageSessionProvider mSessionProvider; 292 private final SilentUpdatePolicy mSilentUpdatePolicy; 293 /** 294 * Note all calls must be done outside {@link #mLock} to prevent lock inversion. 295 */ 296 private final StagingManager mStagingManager; 297 298 final int sessionId; 299 final int userId; 300 final SessionParams params; 301 final long createdMillis; 302 303 /** Staging location where client data is written. */ 304 final File stageDir; 305 final String stageCid; 306 307 private final AtomicInteger mActiveCount = new AtomicInteger(); 308 309 private final Object mLock = new Object(); 310 311 /** 312 * Used to detect and reject concurrent access to this session object to ensure mutation 313 * to multiple objects like {@link #addChildSessionId} are done atomically. 314 */ 315 private final AtomicBoolean mTransactionLock = new AtomicBoolean(false); 316 317 /** Timestamp of the last time this session changed state */ 318 @GuardedBy("mLock") 319 private long updatedMillis; 320 321 /** Timestamp of the time this session is committed */ 322 @GuardedBy("mLock") 323 private long committedMillis; 324 325 /** Uid of the creator of this session. */ 326 private final int mOriginalInstallerUid; 327 328 /** Package name of the app that created the installation session. */ 329 private final String mOriginalInstallerPackageName; 330 331 /** Uid of the owner of the installer session */ 332 private volatile int mInstallerUid; 333 334 /** Where this install request came from */ 335 @GuardedBy("mLock") 336 private InstallSource mInstallSource; 337 338 private final Object mProgressLock = new Object(); 339 340 @GuardedBy("mProgressLock") 341 private float mClientProgress = 0; 342 @GuardedBy("mProgressLock") 343 private float mInternalProgress = 0; 344 345 @GuardedBy("mProgressLock") 346 private float mProgress = 0; 347 @GuardedBy("mProgressLock") 348 private float mReportedProgress = -1; 349 @GuardedBy("mProgressLock") 350 private float mIncrementalProgress = 0; 351 352 /** State of the session. */ 353 @GuardedBy("mLock") 354 private boolean mPrepared = false; 355 @GuardedBy("mLock") 356 private boolean mSealed = false; 357 @GuardedBy("mLock") 358 private boolean mShouldBeSealed = false; 359 360 private final AtomicBoolean mCommitted = new AtomicBoolean(false); 361 362 /** 363 * True if staging files are being used by external entities like {@link PackageSessionVerifier} 364 * or {@link PackageManagerService} which means it is not safe for {@link #abandon()} to clean 365 * up the files. 366 */ 367 @GuardedBy("mLock") 368 private boolean mStageDirInUse = false; 369 370 /** 371 * True if the verification is already in progress. This is used to prevent running 372 * verification again while one is already in progress which will break internal states. 373 * 374 * Worker thread only. 375 */ 376 private boolean mVerificationInProgress = false; 377 378 /** Permissions have been accepted by the user (see {@link #setPermissionsResult}) */ 379 @GuardedBy("mLock") 380 private boolean mPermissionsManuallyAccepted = false; 381 382 @GuardedBy("mLock") 383 private int mFinalStatus; 384 @GuardedBy("mLock") 385 private String mFinalMessage; 386 387 @GuardedBy("mLock") 388 private final ArrayList<RevocableFileDescriptor> mFds = new ArrayList<>(); 389 @GuardedBy("mLock") 390 private final ArrayList<FileBridge> mBridges = new ArrayList<>(); 391 392 @GuardedBy("mLock") 393 private IntentSender mRemoteStatusReceiver; 394 395 /** Fields derived from commit parsing */ 396 @GuardedBy("mLock") 397 private String mPackageName; 398 @GuardedBy("mLock") 399 private long mVersionCode; 400 @GuardedBy("mLock") 401 private SigningDetails mSigningDetails; 402 @GuardedBy("mLock") 403 private SparseArray<PackageInstallerSession> mChildSessions = new SparseArray<>(); 404 @GuardedBy("mLock") 405 private int mParentSessionId; 406 407 @GuardedBy("mLock") 408 private boolean mHasDeviceAdminReceiver; 409 410 static class FileEntry { 411 private final int mIndex; 412 private final InstallationFile mFile; 413 FileEntry(int index, InstallationFile file)414 FileEntry(int index, InstallationFile file) { 415 this.mIndex = index; 416 this.mFile = file; 417 } 418 getIndex()419 int getIndex() { 420 return this.mIndex; 421 } 422 getFile()423 InstallationFile getFile() { 424 return this.mFile; 425 } 426 427 @Override equals(Object obj)428 public boolean equals(Object obj) { 429 if (!(obj instanceof FileEntry)) { 430 return false; 431 } 432 final FileEntry rhs = (FileEntry) obj; 433 return (mFile.getLocation() == rhs.mFile.getLocation()) && TextUtils.equals( 434 mFile.getName(), rhs.mFile.getName()); 435 } 436 437 @Override hashCode()438 public int hashCode() { 439 return Objects.hash(mFile.getLocation(), mFile.getName()); 440 } 441 } 442 443 @GuardedBy("mLock") 444 private ArraySet<FileEntry> mFiles = new ArraySet<>(); 445 446 static class PerFileChecksum { 447 private final Checksum[] mChecksums; 448 private final byte[] mSignature; 449 PerFileChecksum(Checksum[] checksums, byte[] signature)450 PerFileChecksum(Checksum[] checksums, byte[] signature) { 451 mChecksums = checksums; 452 mSignature = signature; 453 } 454 getChecksums()455 Checksum[] getChecksums() { 456 return this.mChecksums; 457 } 458 getSignature()459 byte[] getSignature() { 460 return this.mSignature; 461 } 462 } 463 464 @GuardedBy("mLock") 465 private ArrayMap<String, PerFileChecksum> mChecksums = new ArrayMap<>(); 466 467 @GuardedBy("mLock") 468 private boolean mSessionApplied; 469 @GuardedBy("mLock") 470 private boolean mSessionReady; 471 @GuardedBy("mLock") 472 private boolean mSessionFailed; 473 @GuardedBy("mLock") 474 private int mSessionErrorCode = PackageManager.INSTALL_UNKNOWN; 475 @GuardedBy("mLock") 476 private String mSessionErrorMessage; 477 478 @Nullable 479 final StagedSession mStagedSession; 480 481 /** 482 * The callback to run when pre-reboot verification has ended. Used by {@link #abandon()} 483 * to delay session clean-up until it is safe to do so. 484 */ 485 @GuardedBy("mLock") 486 @Nullable 487 private Runnable mPendingAbandonCallback; 488 489 @VisibleForTesting 490 public class StagedSession implements StagingManager.StagedSession { 491 @Override getChildSessions()492 public List<StagingManager.StagedSession> getChildSessions() { 493 if (!params.isMultiPackage) { 494 return Collections.EMPTY_LIST; 495 } 496 synchronized (mLock) { 497 int size = mChildSessions.size(); 498 List<StagingManager.StagedSession> childSessions = new ArrayList<>(size); 499 for (int i = 0; i < size; ++i) { 500 childSessions.add(mChildSessions.valueAt(i).mStagedSession); 501 } 502 return childSessions; 503 } 504 } 505 506 @Override sessionParams()507 public SessionParams sessionParams() { 508 return params; 509 } 510 511 @Override isMultiPackage()512 public boolean isMultiPackage() { 513 return params.isMultiPackage; 514 } 515 516 @Override isApexSession()517 public boolean isApexSession() { 518 return (params.installFlags & PackageManager.INSTALL_APEX) != 0; 519 } 520 521 @Override sessionId()522 public int sessionId() { 523 return sessionId; 524 } 525 526 @Override containsApexSession()527 public boolean containsApexSession() { 528 return sessionContains((s) -> s.isApexSession()); 529 } 530 531 @Override getPackageName()532 public String getPackageName() { 533 return PackageInstallerSession.this.getPackageName(); 534 } 535 536 @Override setSessionReady()537 public void setSessionReady() { 538 PackageInstallerSession.this.setSessionReady(); 539 } 540 541 @Override setSessionFailed(int errorCode, String errorMessage)542 public void setSessionFailed(int errorCode, String errorMessage) { 543 PackageInstallerSession.this.setSessionFailed(errorCode, errorMessage); 544 } 545 546 @Override setSessionApplied()547 public void setSessionApplied() { 548 PackageInstallerSession.this.setSessionApplied(); 549 } 550 551 @Override containsApkSession()552 public boolean containsApkSession() { 553 return PackageInstallerSession.this.containsApkSession(); 554 } 555 556 /** 557 * Installs apks of staged session while skipping the verification process for a committed 558 * and ready session. 559 * 560 * @return a CompletableFuture that will be completed when installation completes. 561 */ 562 @Override installSession()563 public CompletableFuture<Void> installSession() { 564 assertCallerIsOwnerOrRootOrSystem(); 565 assertNotChild("StagedSession#installSession"); 566 Preconditions.checkArgument(isCommitted() && isSessionReady()); 567 return install(); 568 } 569 570 @Override hasParentSessionId()571 public boolean hasParentSessionId() { 572 return PackageInstallerSession.this.hasParentSessionId(); 573 } 574 575 @Override getParentSessionId()576 public int getParentSessionId() { 577 return PackageInstallerSession.this.getParentSessionId(); 578 } 579 580 @Override isCommitted()581 public boolean isCommitted() { 582 return PackageInstallerSession.this.isCommitted(); 583 } 584 585 @Override isInTerminalState()586 public boolean isInTerminalState() { 587 return PackageInstallerSession.this.isInTerminalState(); 588 } 589 590 @Override isDestroyed()591 public boolean isDestroyed() { 592 return PackageInstallerSession.this.isDestroyed(); 593 } 594 595 @Override getCommittedMillis()596 public long getCommittedMillis() { 597 return PackageInstallerSession.this.getCommittedMillis(); 598 } 599 600 @Override sessionContains(Predicate<StagingManager.StagedSession> filter)601 public boolean sessionContains(Predicate<StagingManager.StagedSession> filter) { 602 return PackageInstallerSession.this.sessionContains(s -> filter.test(s.mStagedSession)); 603 } 604 605 @Override isSessionReady()606 public boolean isSessionReady() { 607 return PackageInstallerSession.this.isSessionReady(); 608 } 609 610 @Override isSessionApplied()611 public boolean isSessionApplied() { 612 return PackageInstallerSession.this.isSessionApplied(); 613 } 614 615 @Override isSessionFailed()616 public boolean isSessionFailed() { 617 return PackageInstallerSession.this.isSessionFailed(); 618 } 619 620 @Override abandon()621 public void abandon() { 622 PackageInstallerSession.this.abandon(); 623 } 624 625 /** 626 * Resumes verification process for non-final committed staged session. 627 * 628 * Useful if a device gets rebooted before verification is complete and we need to restart 629 * the verification. 630 */ 631 @Override verifySession()632 public void verifySession() { 633 assertCallerIsOwnerOrRootOrSystem(); 634 Preconditions.checkArgument(isCommitted()); 635 Preconditions.checkArgument(!isInTerminalState()); 636 verify(); 637 } 638 } 639 640 /** 641 * Path to the validated base APK for this session, which may point at an 642 * APK inside the session (when the session defines the base), or it may 643 * point at the existing base APK (when adding splits to an existing app). 644 * <p> 645 * This is used when confirming permissions, since we can't fully stage the 646 * session inside an ASEC before confirming with user. 647 */ 648 @GuardedBy("mLock") 649 private File mResolvedBaseFile; 650 651 @GuardedBy("mLock") 652 private final List<File> mResolvedStagedFiles = new ArrayList<>(); 653 @GuardedBy("mLock") 654 private final List<File> mResolvedInheritedFiles = new ArrayList<>(); 655 @GuardedBy("mLock") 656 private final List<String> mResolvedInstructionSets = new ArrayList<>(); 657 @GuardedBy("mLock") 658 private final List<String> mResolvedNativeLibPaths = new ArrayList<>(); 659 @GuardedBy("mLock") 660 private File mInheritedFilesBase; 661 @GuardedBy("mLock") 662 private boolean mVerityFoundForApks; 663 664 /** 665 * Both flags should be guarded with mLock whenever changes need to be in lockstep. 666 * Ok to check without mLock in case the proper check is done later, e.g. status callbacks 667 * for DataLoaders with deferred processing. 668 */ 669 private volatile boolean mDestroyed = false; 670 private volatile boolean mDataLoaderFinished = false; 671 672 @GuardedBy("mLock") 673 private IncrementalFileStorages mIncrementalFileStorages; 674 675 @GuardedBy("mLock") 676 private PackageLite mPackageLite; 677 678 /** 679 * Keep the target sdk of a validated apk. 680 */ 681 @GuardedBy("mLock") 682 private int mValidatedTargetSdk = INVALID_TARGET_SDK_VERSION; 683 684 private static final FileFilter sAddedApkFilter = new FileFilter() { 685 @Override 686 public boolean accept(File file) { 687 // Installers can't stage directories, so it's fine to ignore 688 // entries like "lost+found". 689 if (file.isDirectory()) return false; 690 if (file.getName().endsWith(REMOVE_MARKER_EXTENSION)) return false; 691 if (DexMetadataHelper.isDexMetadataFile(file)) return false; 692 if (VerityUtils.isFsveritySignatureFile(file)) return false; 693 if (ApkChecksums.isDigestOrDigestSignatureFile(file)) return false; 694 return true; 695 } 696 }; 697 private static final FileFilter sAddedFilter = new FileFilter() { 698 @Override 699 public boolean accept(File file) { 700 // Installers can't stage directories, so it's fine to ignore 701 // entries like "lost+found". 702 if (file.isDirectory()) return false; 703 if (file.getName().endsWith(REMOVE_MARKER_EXTENSION)) return false; 704 return true; 705 } 706 }; 707 private static final FileFilter sRemovedFilter = new FileFilter() { 708 @Override 709 public boolean accept(File file) { 710 if (file.isDirectory()) return false; 711 if (!file.getName().endsWith(REMOVE_MARKER_EXTENSION)) return false; 712 return true; 713 } 714 }; 715 isDataLoaderInstallation(SessionParams params)716 static boolean isDataLoaderInstallation(SessionParams params) { 717 return params.dataLoaderParams != null; 718 } 719 isSystemDataLoaderInstallation(SessionParams params)720 static boolean isSystemDataLoaderInstallation(SessionParams params) { 721 if (!isDataLoaderInstallation(params)) { 722 return false; 723 } 724 return SYSTEM_DATA_LOADER_PACKAGE.equals( 725 params.dataLoaderParams.getComponentName().getPackageName()); 726 } 727 728 private final Handler.Callback mHandlerCallback = new Handler.Callback() { 729 @Override 730 public boolean handleMessage(Message msg) { 731 switch (msg.what) { 732 case MSG_ON_SESSION_SEALED: 733 handleSessionSealed(); 734 break; 735 case MSG_STREAM_VALIDATE_AND_COMMIT: 736 handleStreamValidateAndCommit(); 737 break; 738 case MSG_INSTALL: 739 handleInstall(); 740 break; 741 case MSG_ON_PACKAGE_INSTALLED: 742 final SomeArgs args = (SomeArgs) msg.obj; 743 final String packageName = (String) args.arg1; 744 final String message = (String) args.arg2; 745 final Bundle extras = (Bundle) args.arg3; 746 final IntentSender statusReceiver = (IntentSender) args.arg4; 747 final int returnCode = args.argi1; 748 args.recycle(); 749 750 sendOnPackageInstalled(mContext, statusReceiver, sessionId, 751 isInstallerDeviceOwnerOrAffiliatedProfileOwner(), userId, 752 packageName, returnCode, message, extras); 753 754 break; 755 case MSG_SESSION_VALIDATION_FAILURE: 756 final int error = msg.arg1; 757 final String detailMessage = (String) msg.obj; 758 onSessionValidationFailure(error, detailMessage); 759 break; 760 } 761 762 return true; 763 } 764 }; 765 isDataLoaderInstallation()766 private boolean isDataLoaderInstallation() { 767 return isDataLoaderInstallation(this.params); 768 } 769 isStreamingInstallation()770 private boolean isStreamingInstallation() { 771 return isDataLoaderInstallation() && params.dataLoaderParams.getType() == STREAMING; 772 } 773 isIncrementalInstallation()774 private boolean isIncrementalInstallation() { 775 return isDataLoaderInstallation() && params.dataLoaderParams.getType() == INCREMENTAL; 776 } 777 isSystemDataLoaderInstallation()778 private boolean isSystemDataLoaderInstallation() { 779 return isSystemDataLoaderInstallation(this.params); 780 } 781 782 /** 783 * @return {@code true} iff the installing is app an device owner or affiliated profile owner. 784 */ isInstallerDeviceOwnerOrAffiliatedProfileOwner()785 private boolean isInstallerDeviceOwnerOrAffiliatedProfileOwner() { 786 assertNotLocked("isInstallerDeviceOwnerOrAffiliatedProfileOwner"); 787 // It is safe to access mInstallerUid and mInstallSource without lock 788 // because they are immutable after sealing. 789 assertSealed("isInstallerDeviceOwnerOrAffiliatedProfileOwner"); 790 if (userId != UserHandle.getUserId(mInstallerUid)) { 791 return false; 792 } 793 DevicePolicyManagerInternal dpmi = 794 LocalServices.getService(DevicePolicyManagerInternal.class); 795 // It may wait for a long time to finish {@code dpmi.canSilentlyInstallPackage}. 796 // Please don't acquire mLock before calling {@code dpmi.canSilentlyInstallPackage}. 797 return dpmi != null && dpmi.canSilentlyInstallPackage( 798 getInstallSource().installerPackageName, mInstallerUid); 799 } 800 801 private static final int USER_ACTION_NOT_NEEDED = 0; 802 private static final int USER_ACTION_REQUIRED = 1; 803 private static final int USER_ACTION_PENDING_APK_PARSING = 2; 804 805 @IntDef({USER_ACTION_NOT_NEEDED, USER_ACTION_REQUIRED, USER_ACTION_PENDING_APK_PARSING}) 806 @interface 807 UserActionRequirement {} 808 809 /** 810 * Checks if the permissions still need to be confirmed. 811 * 812 * <p>This is dependant on the identity of the installer, hence this cannot be cached if the 813 * installer might still {@link #transfer(String) change}. 814 * 815 * @return {@code true} iff we need to ask to confirm the permissions? 816 */ 817 @UserActionRequirement computeUserActionRequirement()818 private int computeUserActionRequirement() { 819 final String packageName; 820 final boolean hasDeviceAdminReceiver; 821 synchronized (mLock) { 822 if (mPermissionsManuallyAccepted) { 823 return USER_ACTION_NOT_NEEDED; 824 } 825 packageName = mPackageName; 826 hasDeviceAdminReceiver = mHasDeviceAdminReceiver; 827 } 828 829 final boolean forcePermissionPrompt = 830 (params.installFlags & PackageManager.INSTALL_FORCE_PERMISSION_PROMPT) != 0 831 || params.requireUserAction == SessionParams.USER_ACTION_REQUIRED; 832 if (forcePermissionPrompt) { 833 return USER_ACTION_REQUIRED; 834 } 835 // It is safe to access mInstallerUid and mInstallSource without lock 836 // because they are immutable after sealing. 837 final Computer snapshot = mPm.snapshotComputer(); 838 final boolean isInstallPermissionGranted = 839 (snapshot.checkUidPermission(android.Manifest.permission.INSTALL_PACKAGES, 840 mInstallerUid) == PackageManager.PERMISSION_GRANTED); 841 final boolean isSelfUpdatePermissionGranted = 842 (snapshot.checkUidPermission(android.Manifest.permission.INSTALL_SELF_UPDATES, 843 mInstallerUid) == PackageManager.PERMISSION_GRANTED); 844 final boolean isUpdatePermissionGranted = 845 (snapshot.checkUidPermission(android.Manifest.permission.INSTALL_PACKAGE_UPDATES, 846 mInstallerUid) == PackageManager.PERMISSION_GRANTED); 847 final boolean isUpdateWithoutUserActionPermissionGranted = (snapshot.checkUidPermission( 848 android.Manifest.permission.UPDATE_PACKAGES_WITHOUT_USER_ACTION, mInstallerUid) 849 == PackageManager.PERMISSION_GRANTED); 850 final boolean isInstallDpcPackagesPermissionGranted = (snapshot.checkUidPermission( 851 android.Manifest.permission.INSTALL_DPC_PACKAGES, mInstallerUid) 852 == PackageManager.PERMISSION_GRANTED); 853 final int targetPackageUid = snapshot.getPackageUid(packageName, 0, userId); 854 final boolean isUpdate = targetPackageUid != -1 || isApexSession(); 855 final InstallSourceInfo existingInstallSourceInfo = isUpdate 856 ? snapshot.getInstallSourceInfo(packageName) 857 : null; 858 final String existingInstallerPackageName = existingInstallSourceInfo != null 859 ? existingInstallSourceInfo.getInstallingPackageName() 860 : null; 861 final boolean isInstallerOfRecord = isUpdate 862 && Objects.equals(existingInstallerPackageName, getInstallerPackageName()); 863 final boolean isSelfUpdate = targetPackageUid == mInstallerUid; 864 final boolean isPermissionGranted = isInstallPermissionGranted 865 || (isUpdatePermissionGranted && isUpdate) 866 || (isSelfUpdatePermissionGranted && isSelfUpdate) 867 || (isInstallDpcPackagesPermissionGranted && hasDeviceAdminReceiver); 868 final boolean isInstallerRoot = (mInstallerUid == Process.ROOT_UID); 869 final boolean isInstallerSystem = (mInstallerUid == Process.SYSTEM_UID); 870 871 // Device owners and affiliated profile owners are allowed to silently install packages, so 872 // the permission check is waived if the installer is the device owner. 873 final boolean noUserActionNecessary = isPermissionGranted || isInstallerRoot 874 || isInstallerSystem || isInstallerDeviceOwnerOrAffiliatedProfileOwner(); 875 876 if (noUserActionNecessary) { 877 return USER_ACTION_NOT_NEEDED; 878 } 879 880 if (snapshot.isInstallDisabledForPackage(getInstallerPackageName(), mInstallerUid, 881 userId)) { 882 // show the installer to account for device poslicy or unknown sources use cases 883 return USER_ACTION_REQUIRED; 884 } 885 886 if (params.requireUserAction == SessionParams.USER_ACTION_NOT_REQUIRED 887 && isUpdateWithoutUserActionPermissionGranted 888 && (isInstallerOfRecord || isSelfUpdate)) { 889 return USER_ACTION_PENDING_APK_PARSING; 890 } 891 892 return USER_ACTION_REQUIRED; 893 } 894 895 @SuppressWarnings("GuardedBy" /*mPm.mInstaller is {@code final} field*/) PackageInstallerSession(PackageInstallerService.InternalCallback callback, Context context, PackageManagerService pm, PackageSessionProvider sessionProvider, SilentUpdatePolicy silentUpdatePolicy, Looper looper, StagingManager stagingManager, int sessionId, int userId, int installerUid, @NonNull InstallSource installSource, SessionParams params, long createdMillis, long committedMillis, File stageDir, String stageCid, InstallationFile[] files, ArrayMap<String, PerFileChecksum> checksums, boolean prepared, boolean committed, boolean destroyed, boolean sealed, @Nullable int[] childSessionIds, int parentSessionId, boolean isReady, boolean isFailed, boolean isApplied, int sessionErrorCode, String sessionErrorMessage)896 public PackageInstallerSession(PackageInstallerService.InternalCallback callback, 897 Context context, PackageManagerService pm, 898 PackageSessionProvider sessionProvider, 899 SilentUpdatePolicy silentUpdatePolicy, Looper looper, StagingManager stagingManager, 900 int sessionId, int userId, int installerUid, @NonNull InstallSource installSource, 901 SessionParams params, long createdMillis, long committedMillis, 902 File stageDir, String stageCid, InstallationFile[] files, 903 ArrayMap<String, PerFileChecksum> checksums, 904 boolean prepared, boolean committed, boolean destroyed, boolean sealed, 905 @Nullable int[] childSessionIds, int parentSessionId, boolean isReady, 906 boolean isFailed, boolean isApplied, int sessionErrorCode, 907 String sessionErrorMessage) { 908 mCallback = callback; 909 mContext = context; 910 mPm = pm; 911 mInstaller = (mPm != null) ? mPm.mInstaller : null; 912 mSessionProvider = sessionProvider; 913 mSilentUpdatePolicy = silentUpdatePolicy; 914 mHandler = new Handler(looper, mHandlerCallback); 915 mStagingManager = stagingManager; 916 917 this.sessionId = sessionId; 918 this.userId = userId; 919 mOriginalInstallerUid = installerUid; 920 mInstallerUid = installerUid; 921 mInstallSource = Objects.requireNonNull(installSource); 922 mOriginalInstallerPackageName = mInstallSource.installerPackageName; 923 this.params = params; 924 this.createdMillis = createdMillis; 925 this.updatedMillis = createdMillis; 926 this.committedMillis = committedMillis; 927 this.stageDir = stageDir; 928 this.stageCid = stageCid; 929 this.mShouldBeSealed = sealed; 930 if (childSessionIds != null) { 931 for (int childSessionId : childSessionIds) { 932 // Null values will be resolved to actual object references in 933 // #onAfterSessionRead later. 934 mChildSessions.put(childSessionId, null); 935 } 936 } 937 this.mParentSessionId = parentSessionId; 938 939 if (files != null) { 940 mFiles.ensureCapacity(files.length); 941 for (int i = 0, size = files.length; i < size; ++i) { 942 InstallationFile file = files[i]; 943 if (!mFiles.add(new FileEntry(i, file))) { 944 throw new IllegalArgumentException( 945 "Trying to add a duplicate installation file"); 946 } 947 } 948 } 949 950 if (checksums != null) { 951 mChecksums.putAll(checksums); 952 } 953 954 if (!params.isMultiPackage && (stageDir == null) == (stageCid == null)) { 955 throw new IllegalArgumentException( 956 "Exactly one of stageDir or stageCid stage must be set"); 957 } 958 959 mPrepared = prepared; 960 mCommitted.set(committed); 961 mDestroyed = destroyed; 962 mSessionReady = isReady; 963 mSessionApplied = isApplied; 964 mSessionFailed = isFailed; 965 mSessionErrorCode = sessionErrorCode; 966 mSessionErrorMessage = 967 sessionErrorMessage != null ? sessionErrorMessage : ""; 968 mStagedSession = params.isStaged ? new StagedSession() : null; 969 970 if (isDataLoaderInstallation()) { 971 if (isApexSession()) { 972 throw new IllegalArgumentException( 973 "DataLoader installation of APEX modules is not allowed."); 974 } 975 976 if (isSystemDataLoaderInstallation() && mContext.checkCallingOrSelfPermission( 977 Manifest.permission.USE_SYSTEM_DATA_LOADERS) 978 != PackageManager.PERMISSION_GRANTED) { 979 throw new SecurityException("You need the " 980 + "com.android.permission.USE_SYSTEM_DATA_LOADERS permission " 981 + "to use system data loaders"); 982 } 983 } 984 985 if (isIncrementalInstallation() && !IncrementalManager.isAllowed()) { 986 throw new IllegalArgumentException("Incremental installation not allowed."); 987 } 988 } 989 990 /** 991 * Returns {@code true} if the {@link SessionInfo} object should be produced with potentially 992 * sensitive data scrubbed from its fields. 993 * 994 * @param callingUid the uid of the caller; the recipient of the {@link SessionInfo} that may 995 * need to be scrubbed 996 */ shouldScrubData(int callingUid)997 private boolean shouldScrubData(int callingUid) { 998 return !(callingUid < Process.FIRST_APPLICATION_UID || getInstallerUid() == callingUid); 999 } 1000 1001 /** 1002 * Generates a {@link SessionInfo} object for the provided uid. This may result in some fields 1003 * that may contain sensitive info being filtered. 1004 * 1005 * @param includeIcon true if the icon should be included in the object 1006 * @param callingUid the uid of the caller; the recipient of the {@link SessionInfo} that may 1007 * need to be scrubbed 1008 * @see #shouldScrubData(int) 1009 */ generateInfoForCaller(boolean includeIcon, int callingUid)1010 public SessionInfo generateInfoForCaller(boolean includeIcon, int callingUid) { 1011 return generateInfoInternal(includeIcon, shouldScrubData(callingUid)); 1012 } 1013 1014 /** 1015 * Generates a {@link SessionInfo} object to ensure proper hiding of sensitive fields. 1016 * 1017 * @param includeIcon true if the icon should be included in the object 1018 * @see #generateInfoForCaller(boolean, int) 1019 */ generateInfoScrubbed(boolean includeIcon)1020 public SessionInfo generateInfoScrubbed(boolean includeIcon) { 1021 return generateInfoInternal(includeIcon, true /*scrubData*/); 1022 } 1023 generateInfoInternal(boolean includeIcon, boolean scrubData)1024 private SessionInfo generateInfoInternal(boolean includeIcon, boolean scrubData) { 1025 final SessionInfo info = new SessionInfo(); 1026 final float progress; 1027 synchronized (mProgressLock) { 1028 progress = mProgress; 1029 } 1030 synchronized (mLock) { 1031 info.sessionId = sessionId; 1032 info.userId = userId; 1033 info.installerPackageName = mInstallSource.installerPackageName; 1034 info.installerAttributionTag = mInstallSource.installerAttributionTag; 1035 info.resolvedBaseCodePath = (mResolvedBaseFile != null) ? 1036 mResolvedBaseFile.getAbsolutePath() : null; 1037 info.progress = progress; 1038 info.sealed = mSealed; 1039 info.isCommitted = mCommitted.get(); 1040 info.active = mActiveCount.get() > 0; 1041 1042 info.mode = params.mode; 1043 info.installReason = params.installReason; 1044 info.installScenario = params.installScenario; 1045 info.sizeBytes = params.sizeBytes; 1046 info.appPackageName = mPackageName != null ? mPackageName : params.appPackageName; 1047 if (includeIcon) { 1048 info.appIcon = params.appIcon; 1049 } 1050 info.appLabel = params.appLabel; 1051 1052 info.installLocation = params.installLocation; 1053 if (!scrubData) { 1054 info.originatingUri = params.originatingUri; 1055 } 1056 info.originatingUid = params.originatingUid; 1057 if (!scrubData) { 1058 info.referrerUri = params.referrerUri; 1059 } 1060 info.grantedRuntimePermissions = params.grantedRuntimePermissions; 1061 info.whitelistedRestrictedPermissions = params.whitelistedRestrictedPermissions; 1062 info.autoRevokePermissionsMode = params.autoRevokePermissionsMode; 1063 info.installFlags = params.installFlags; 1064 info.isMultiPackage = params.isMultiPackage; 1065 info.isStaged = params.isStaged; 1066 info.rollbackDataPolicy = params.rollbackDataPolicy; 1067 info.parentSessionId = mParentSessionId; 1068 info.childSessionIds = getChildSessionIdsLocked(); 1069 info.isSessionApplied = mSessionApplied; 1070 info.isSessionReady = mSessionReady; 1071 info.isSessionFailed = mSessionFailed; 1072 info.setSessionErrorCode(mSessionErrorCode, mSessionErrorMessage); 1073 info.createdMillis = createdMillis; 1074 info.updatedMillis = updatedMillis; 1075 info.requireUserAction = params.requireUserAction; 1076 info.installerUid = mInstallerUid; 1077 info.packageSource = params.packageSource; 1078 } 1079 return info; 1080 } 1081 isPrepared()1082 public boolean isPrepared() { 1083 synchronized (mLock) { 1084 return mPrepared; 1085 } 1086 } 1087 isSealed()1088 public boolean isSealed() { 1089 synchronized (mLock) { 1090 return mSealed; 1091 } 1092 } 1093 1094 /** {@hide} */ isCommitted()1095 boolean isCommitted() { 1096 return mCommitted.get(); 1097 } 1098 1099 /** {@hide} */ isDestroyed()1100 boolean isDestroyed() { 1101 synchronized (mLock) { 1102 return mDestroyed; 1103 } 1104 } 1105 isInTerminalState()1106 private boolean isInTerminalState() { 1107 synchronized (mLock) { 1108 return mSessionApplied || mSessionFailed; 1109 } 1110 } 1111 1112 /** Returns true if a staged session has reached a final state and can be forgotten about */ isStagedAndInTerminalState()1113 public boolean isStagedAndInTerminalState() { 1114 return params.isStaged && isInTerminalState(); 1115 } 1116 assertNotLocked(String cookie)1117 private void assertNotLocked(String cookie) { 1118 if (Thread.holdsLock(mLock)) { 1119 throw new IllegalStateException(cookie + " is holding mLock"); 1120 } 1121 } 1122 assertSealed(String cookie)1123 private void assertSealed(String cookie) { 1124 if (!isSealed()) { 1125 throw new IllegalStateException(cookie + " before sealing"); 1126 } 1127 } 1128 1129 @GuardedBy("mLock") assertPreparedAndNotSealedLocked(String cookie)1130 private void assertPreparedAndNotSealedLocked(String cookie) { 1131 assertPreparedAndNotCommittedOrDestroyedLocked(cookie); 1132 if (mSealed) { 1133 throw new SecurityException(cookie + " not allowed after sealing"); 1134 } 1135 } 1136 1137 @GuardedBy("mLock") assertPreparedAndNotCommittedOrDestroyedLocked(String cookie)1138 private void assertPreparedAndNotCommittedOrDestroyedLocked(String cookie) { 1139 assertPreparedAndNotDestroyedLocked(cookie); 1140 if (mCommitted.get()) { 1141 throw new SecurityException(cookie + " not allowed after commit"); 1142 } 1143 } 1144 1145 @GuardedBy("mLock") assertPreparedAndNotDestroyedLocked(String cookie)1146 private void assertPreparedAndNotDestroyedLocked(String cookie) { 1147 if (!mPrepared) { 1148 throw new IllegalStateException(cookie + " before prepared"); 1149 } 1150 if (mDestroyed) { 1151 throw new SecurityException(cookie + " not allowed after destruction"); 1152 } 1153 } 1154 1155 @GuardedBy("mProgressLock") setClientProgressLocked(float progress)1156 private void setClientProgressLocked(float progress) { 1157 // Always publish first staging movement 1158 final boolean forcePublish = (mClientProgress == 0); 1159 mClientProgress = progress; 1160 computeProgressLocked(forcePublish); 1161 } 1162 1163 @Override setClientProgress(float progress)1164 public void setClientProgress(float progress) { 1165 assertCallerIsOwnerOrRoot(); 1166 synchronized (mProgressLock) { 1167 setClientProgressLocked(progress); 1168 } 1169 } 1170 1171 @Override addClientProgress(float progress)1172 public void addClientProgress(float progress) { 1173 assertCallerIsOwnerOrRoot(); 1174 synchronized (mProgressLock) { 1175 setClientProgressLocked(mClientProgress + progress); 1176 } 1177 } 1178 1179 @GuardedBy("mProgressLock") computeProgressLocked(boolean forcePublish)1180 private void computeProgressLocked(boolean forcePublish) { 1181 if (!isIncrementalInstallation() || !mCommitted.get()) { 1182 mProgress = MathUtils.constrain(mClientProgress * 0.8f, 0f, 0.8f) 1183 + MathUtils.constrain(mInternalProgress * 0.2f, 0f, 0.2f); 1184 } else { 1185 // For incremental, publish regular install progress before the session is committed, 1186 // but publish incremental progress afterwards. 1187 if (mIncrementalProgress - mProgress >= 0.01) { 1188 // It takes some time for data loader to write to incremental file system, so at the 1189 // beginning of the commit, the incremental progress might be very small. 1190 // Wait till the incremental progress is larger than what's already displayed. 1191 // This way we don't see the progress ring going backwards. 1192 mProgress = mIncrementalProgress; 1193 } 1194 } 1195 1196 // Only publish meaningful progress changes. 1197 if (forcePublish || (mProgress - mReportedProgress) >= 0.01) { 1198 mReportedProgress = mProgress; 1199 mCallback.onSessionProgressChanged(this, mProgress); 1200 } 1201 } 1202 1203 @Override getNames()1204 public String[] getNames() { 1205 assertCallerIsOwnerRootOrVerifier(); 1206 synchronized (mLock) { 1207 assertPreparedAndNotDestroyedLocked("getNames"); 1208 if (!mCommitted.get()) { 1209 return getNamesLocked(); 1210 } else { 1211 return getStageDirContentsLocked(); 1212 } 1213 } 1214 } 1215 1216 @GuardedBy("mLock") getStageDirContentsLocked()1217 private String[] getStageDirContentsLocked() { 1218 String[] result = stageDir.list(); 1219 if (result == null) { 1220 result = EmptyArray.STRING; 1221 } 1222 return result; 1223 } 1224 1225 @GuardedBy("mLock") getNamesLocked()1226 private String[] getNamesLocked() { 1227 if (!isDataLoaderInstallation()) { 1228 return getStageDirContentsLocked(); 1229 } 1230 1231 InstallationFile[] files = getInstallationFilesLocked(); 1232 String[] result = new String[files.length]; 1233 for (int i = 0, size = files.length; i < size; ++i) { 1234 result[i] = files[i].getName(); 1235 } 1236 return result; 1237 } 1238 1239 @GuardedBy("mLock") getInstallationFilesLocked()1240 private InstallationFile[] getInstallationFilesLocked() { 1241 final InstallationFile[] result = new InstallationFile[mFiles.size()]; 1242 for (FileEntry fileEntry : mFiles) { 1243 result[fileEntry.getIndex()] = fileEntry.getFile(); 1244 } 1245 return result; 1246 } 1247 filterFiles(File parent, String[] names, FileFilter filter)1248 private static ArrayList<File> filterFiles(File parent, String[] names, FileFilter filter) { 1249 ArrayList<File> result = new ArrayList<>(names.length); 1250 for (String name : names) { 1251 File file = new File(parent, name); 1252 if (filter.accept(file)) { 1253 result.add(file); 1254 } 1255 } 1256 return result; 1257 } 1258 1259 @GuardedBy("mLock") getAddedApksLocked()1260 private List<File> getAddedApksLocked() { 1261 String[] names = getNamesLocked(); 1262 return filterFiles(stageDir, names, sAddedApkFilter); 1263 } 1264 1265 @GuardedBy("mLock") getRemovedFilesLocked()1266 private List<File> getRemovedFilesLocked() { 1267 String[] names = getNamesLocked(); 1268 return filterFiles(stageDir, names, sRemovedFilter); 1269 } 1270 1271 @Override setChecksums(String name, @NonNull Checksum[] checksums, @Nullable byte[] signature)1272 public void setChecksums(String name, @NonNull Checksum[] checksums, 1273 @Nullable byte[] signature) { 1274 if (checksums.length == 0) { 1275 return; 1276 } 1277 1278 final String installerPackageName; 1279 if (!TextUtils.isEmpty(getInstallSource().initiatingPackageName)) { 1280 installerPackageName = getInstallSource().initiatingPackageName; 1281 } else { 1282 installerPackageName = getInstallSource().installerPackageName; 1283 } 1284 if (TextUtils.isEmpty(installerPackageName)) { 1285 throw new IllegalStateException("Installer package is empty."); 1286 } 1287 1288 final AppOpsManager appOps = mContext.getSystemService(AppOpsManager.class); 1289 appOps.checkPackage(Binder.getCallingUid(), installerPackageName); 1290 1291 final PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class); 1292 final AndroidPackage callingInstaller = pmi.getPackage(installerPackageName); 1293 if (callingInstaller == null) { 1294 throw new IllegalStateException("Can't obtain calling installer's package."); 1295 } 1296 1297 if (signature != null && signature.length != 0) { 1298 try { 1299 Certificate[] ignored = ApkChecksums.verifySignature(checksums, signature); 1300 } catch (IOException | NoSuchAlgorithmException | SignatureException e) { 1301 throw new IllegalArgumentException("Can't verify signature", e); 1302 } 1303 } 1304 1305 assertCallerIsOwnerOrRoot(); 1306 synchronized (mLock) { 1307 assertPreparedAndNotCommittedOrDestroyedLocked("addChecksums"); 1308 1309 if (mChecksums.containsKey(name)) { 1310 throw new IllegalStateException("Duplicate checksums."); 1311 } 1312 1313 mChecksums.put(name, new PerFileChecksum(checksums, signature)); 1314 } 1315 } 1316 1317 @Override requestChecksums(@onNull String name, @Checksum.TypeMask int optional, @Checksum.TypeMask int required, @Nullable List trustedInstallers, @NonNull IOnChecksumsReadyListener onChecksumsReadyListener)1318 public void requestChecksums(@NonNull String name, @Checksum.TypeMask int optional, 1319 @Checksum.TypeMask int required, @Nullable List trustedInstallers, 1320 @NonNull IOnChecksumsReadyListener onChecksumsReadyListener) { 1321 assertCallerIsOwnerRootOrVerifier(); 1322 final File file = new File(stageDir, name); 1323 final String installerPackageName = getInstallSource().initiatingPackageName; 1324 try { 1325 mPm.requestFileChecksums(file, installerPackageName, optional, required, 1326 trustedInstallers, onChecksumsReadyListener); 1327 } catch (FileNotFoundException e) { 1328 throw new ParcelableException(e); 1329 } 1330 } 1331 1332 @Override removeSplit(String splitName)1333 public void removeSplit(String splitName) { 1334 if (isDataLoaderInstallation()) { 1335 throw new IllegalStateException( 1336 "Cannot remove splits in a data loader installation session."); 1337 } 1338 if (TextUtils.isEmpty(params.appPackageName)) { 1339 throw new IllegalStateException("Must specify package name to remove a split"); 1340 } 1341 1342 assertCallerIsOwnerOrRoot(); 1343 synchronized (mLock) { 1344 assertPreparedAndNotCommittedOrDestroyedLocked("removeSplit"); 1345 1346 try { 1347 createRemoveSplitMarkerLocked(splitName); 1348 } catch (IOException e) { 1349 throw ExceptionUtils.wrap(e); 1350 } 1351 } 1352 } 1353 getRemoveMarkerName(String name)1354 private static String getRemoveMarkerName(String name) { 1355 final String markerName = name + REMOVE_MARKER_EXTENSION; 1356 if (!FileUtils.isValidExtFilename(markerName)) { 1357 throw new IllegalArgumentException("Invalid marker: " + markerName); 1358 } 1359 return markerName; 1360 } 1361 1362 @GuardedBy("mLock") createRemoveSplitMarkerLocked(String splitName)1363 private void createRemoveSplitMarkerLocked(String splitName) throws IOException { 1364 try { 1365 final File target = new File(stageDir, getRemoveMarkerName(splitName)); 1366 target.createNewFile(); 1367 Os.chmod(target.getAbsolutePath(), 0 /*mode*/); 1368 } catch (ErrnoException e) { 1369 throw e.rethrowAsIOException(); 1370 } 1371 } 1372 assertShellOrSystemCalling(String operation)1373 private void assertShellOrSystemCalling(String operation) { 1374 switch (Binder.getCallingUid()) { 1375 case android.os.Process.SHELL_UID: 1376 case android.os.Process.ROOT_UID: 1377 case android.os.Process.SYSTEM_UID: 1378 break; 1379 default: 1380 throw new SecurityException(operation + " only supported from shell or system"); 1381 } 1382 } 1383 assertCanWrite(boolean reverseMode)1384 private void assertCanWrite(boolean reverseMode) { 1385 if (isDataLoaderInstallation()) { 1386 throw new IllegalStateException( 1387 "Cannot write regular files in a data loader installation session."); 1388 } 1389 assertCallerIsOwnerOrRoot(); 1390 synchronized (mLock) { 1391 assertPreparedAndNotSealedLocked("assertCanWrite"); 1392 } 1393 if (reverseMode) { 1394 assertShellOrSystemCalling("Reverse mode"); 1395 } 1396 } 1397 1398 @Override openWrite(String name, long offsetBytes, long lengthBytes)1399 public ParcelFileDescriptor openWrite(String name, long offsetBytes, long lengthBytes) { 1400 assertCanWrite(false); 1401 try { 1402 return doWriteInternal(name, offsetBytes, lengthBytes, null); 1403 } catch (IOException e) { 1404 throw ExceptionUtils.wrap(e); 1405 } 1406 } 1407 1408 @Override write(String name, long offsetBytes, long lengthBytes, ParcelFileDescriptor fd)1409 public void write(String name, long offsetBytes, long lengthBytes, 1410 ParcelFileDescriptor fd) { 1411 assertCanWrite(fd != null); 1412 try { 1413 doWriteInternal(name, offsetBytes, lengthBytes, fd); 1414 } catch (IOException e) { 1415 throw ExceptionUtils.wrap(e); 1416 } 1417 } 1418 1419 @Override stageViaHardLink(String path)1420 public void stageViaHardLink(String path) { 1421 final int callingUid = Binder.getCallingUid(); 1422 if (callingUid != Process.SYSTEM_UID) { 1423 throw new SecurityException("link() can only be run by the system"); 1424 } 1425 1426 try { 1427 final File target = new File(path); 1428 final File source = new File(stageDir, target.getName()); 1429 try { 1430 Os.link(path, source.getAbsolutePath()); 1431 // Grant READ access for APK to be read successfully 1432 Os.chmod(source.getAbsolutePath(), 0644); 1433 } catch (ErrnoException e) { 1434 e.rethrowAsIOException(); 1435 } 1436 if (!SELinux.restorecon(source)) { 1437 throw new IOException("Can't relabel file: " + source); 1438 } 1439 } catch (IOException e) { 1440 throw ExceptionUtils.wrap(e); 1441 } 1442 } 1443 openTargetInternal(String path, int flags, int mode)1444 private ParcelFileDescriptor openTargetInternal(String path, int flags, int mode) 1445 throws IOException, ErrnoException { 1446 // TODO: this should delegate to DCS so the system process avoids 1447 // holding open FDs into containers. 1448 final FileDescriptor fd = Os.open(path, flags, mode); 1449 return new ParcelFileDescriptor(fd); 1450 } 1451 createRevocableFdInternal(RevocableFileDescriptor fd, ParcelFileDescriptor pfd)1452 private ParcelFileDescriptor createRevocableFdInternal(RevocableFileDescriptor fd, 1453 ParcelFileDescriptor pfd) throws IOException { 1454 int releasedFdInt = pfd.detachFd(); 1455 FileDescriptor releasedFd = new FileDescriptor(); 1456 releasedFd.setInt$(releasedFdInt); 1457 fd.init(mContext, releasedFd); 1458 return fd.getRevocableFileDescriptor(); 1459 } 1460 doWriteInternal(String name, long offsetBytes, long lengthBytes, ParcelFileDescriptor incomingFd)1461 private ParcelFileDescriptor doWriteInternal(String name, long offsetBytes, long lengthBytes, 1462 ParcelFileDescriptor incomingFd) throws IOException { 1463 // Quick validity check of state, and allocate a pipe for ourselves. We 1464 // then do heavy disk allocation outside the lock, but this open pipe 1465 // will block any attempted install transitions. 1466 final RevocableFileDescriptor fd; 1467 final FileBridge bridge; 1468 synchronized (mLock) { 1469 if (PackageInstaller.ENABLE_REVOCABLE_FD) { 1470 fd = new RevocableFileDescriptor(); 1471 bridge = null; 1472 mFds.add(fd); 1473 } else { 1474 fd = null; 1475 bridge = new FileBridge(); 1476 mBridges.add(bridge); 1477 } 1478 } 1479 1480 try { 1481 // Use installer provided name for now; we always rename later 1482 if (!FileUtils.isValidExtFilename(name)) { 1483 throw new IllegalArgumentException("Invalid name: " + name); 1484 } 1485 final File target; 1486 final long identity = Binder.clearCallingIdentity(); 1487 try { 1488 target = new File(stageDir, name); 1489 } finally { 1490 Binder.restoreCallingIdentity(identity); 1491 } 1492 1493 ParcelFileDescriptor targetPfd = openTargetInternal(target.getAbsolutePath(), 1494 O_CREAT | O_WRONLY, 0644); 1495 Os.chmod(target.getAbsolutePath(), 0644); 1496 1497 // If caller specified a total length, allocate it for them. Free up 1498 // cache space to grow, if needed. 1499 if (stageDir != null && lengthBytes > 0) { 1500 mContext.getSystemService(StorageManager.class).allocateBytes( 1501 targetPfd.getFileDescriptor(), lengthBytes, 1502 InstallLocationUtils.translateAllocateFlags(params.installFlags)); 1503 } 1504 1505 if (offsetBytes > 0) { 1506 Os.lseek(targetPfd.getFileDescriptor(), offsetBytes, OsConstants.SEEK_SET); 1507 } 1508 1509 if (incomingFd != null) { 1510 // In "reverse" mode, we're streaming data ourselves from the 1511 // incoming FD, which means we never have to hand out our 1512 // sensitive internal FD. We still rely on a "bridge" being 1513 // inserted above to hold the session active. 1514 try { 1515 final Int64Ref last = new Int64Ref(0); 1516 FileUtils.copy(incomingFd.getFileDescriptor(), targetPfd.getFileDescriptor(), 1517 lengthBytes, null, Runnable::run, 1518 (long progress) -> { 1519 if (params.sizeBytes > 0) { 1520 final long delta = progress - last.value; 1521 last.value = progress; 1522 synchronized (mProgressLock) { 1523 setClientProgressLocked(mClientProgress 1524 + (float) delta / (float) params.sizeBytes); 1525 } 1526 } 1527 }); 1528 } finally { 1529 IoUtils.closeQuietly(targetPfd); 1530 IoUtils.closeQuietly(incomingFd); 1531 1532 // We're done here, so remove the "bridge" that was holding 1533 // the session active. 1534 synchronized (mLock) { 1535 if (PackageInstaller.ENABLE_REVOCABLE_FD) { 1536 mFds.remove(fd); 1537 } else { 1538 bridge.forceClose(); 1539 mBridges.remove(bridge); 1540 } 1541 } 1542 } 1543 return null; 1544 } else if (PackageInstaller.ENABLE_REVOCABLE_FD) { 1545 return createRevocableFdInternal(fd, targetPfd); 1546 } else { 1547 bridge.setTargetFile(targetPfd); 1548 bridge.start(); 1549 return bridge.getClientSocket(); 1550 } 1551 1552 } catch (ErrnoException e) { 1553 throw e.rethrowAsIOException(); 1554 } 1555 } 1556 1557 @Override openRead(String name)1558 public ParcelFileDescriptor openRead(String name) { 1559 if (isDataLoaderInstallation()) { 1560 throw new IllegalStateException( 1561 "Cannot read regular files in a data loader installation session."); 1562 } 1563 assertCallerIsOwnerOrRoot(); 1564 synchronized (mLock) { 1565 assertPreparedAndNotCommittedOrDestroyedLocked("openRead"); 1566 try { 1567 return openReadInternalLocked(name); 1568 } catch (IOException e) { 1569 throw ExceptionUtils.wrap(e); 1570 } 1571 } 1572 } 1573 1574 @GuardedBy("mLock") openReadInternalLocked(String name)1575 private ParcelFileDescriptor openReadInternalLocked(String name) throws IOException { 1576 try { 1577 if (!FileUtils.isValidExtFilename(name)) { 1578 throw new IllegalArgumentException("Invalid name: " + name); 1579 } 1580 final File target = new File(stageDir, name); 1581 final FileDescriptor targetFd = Os.open(target.getAbsolutePath(), O_RDONLY, 0); 1582 return new ParcelFileDescriptor(targetFd); 1583 } catch (ErrnoException e) { 1584 throw e.rethrowAsIOException(); 1585 } 1586 } 1587 1588 /** 1589 * Check if the caller is the owner of this session or a verifier. 1590 * Otherwise throw a {@link SecurityException}. 1591 */ assertCallerIsOwnerRootOrVerifier()1592 private void assertCallerIsOwnerRootOrVerifier() { 1593 final int callingUid = Binder.getCallingUid(); 1594 if (callingUid == Process.ROOT_UID || callingUid == mInstallerUid) { 1595 return; 1596 } 1597 if (isSealed() && mContext.checkCallingOrSelfPermission( 1598 android.Manifest.permission.PACKAGE_VERIFICATION_AGENT) 1599 == PackageManager.PERMISSION_GRANTED) { 1600 return; 1601 } 1602 throw new SecurityException("Session does not belong to uid " + callingUid); 1603 } 1604 1605 /** 1606 * Check if the caller is the owner of this session. Otherwise throw a 1607 * {@link SecurityException}. 1608 */ assertCallerIsOwnerOrRoot()1609 private void assertCallerIsOwnerOrRoot() { 1610 final int callingUid = Binder.getCallingUid(); 1611 if (callingUid != Process.ROOT_UID && callingUid != mInstallerUid) { 1612 throw new SecurityException("Session does not belong to uid " + callingUid); 1613 } 1614 } 1615 1616 /** 1617 * Check if the caller is the owner of this session. Otherwise throw a 1618 * {@link SecurityException}. 1619 */ assertCallerIsOwnerOrRootOrSystem()1620 private void assertCallerIsOwnerOrRootOrSystem() { 1621 final int callingUid = Binder.getCallingUid(); 1622 if (callingUid != Process.ROOT_UID && callingUid != mInstallerUid 1623 && callingUid != Process.SYSTEM_UID) { 1624 throw new SecurityException("Session does not belong to uid " + callingUid); 1625 } 1626 } 1627 1628 /** 1629 * If anybody is reading or writing data of the session, throw an {@link SecurityException}. 1630 */ 1631 @GuardedBy("mLock") assertNoWriteFileTransfersOpenLocked()1632 private void assertNoWriteFileTransfersOpenLocked() { 1633 // Verify that all writers are hands-off 1634 for (RevocableFileDescriptor fd : mFds) { 1635 if (!fd.isRevoked()) { 1636 throw new SecurityException("Files still open"); 1637 } 1638 } 1639 for (FileBridge bridge : mBridges) { 1640 if (!bridge.isClosed()) { 1641 throw new SecurityException("Files still open"); 1642 } 1643 } 1644 } 1645 1646 @Override commit(@onNull IntentSender statusReceiver, boolean forTransfer)1647 public void commit(@NonNull IntentSender statusReceiver, boolean forTransfer) { 1648 if (hasParentSessionId()) { 1649 throw new IllegalStateException( 1650 "Session " + sessionId + " is a child of multi-package session " 1651 + getParentSessionId() + " and may not be committed directly."); 1652 } 1653 1654 if (!markAsSealed(statusReceiver, forTransfer)) { 1655 return; 1656 } 1657 if (isMultiPackage()) { 1658 synchronized (mLock) { 1659 boolean sealFailed = false; 1660 for (int i = mChildSessions.size() - 1; i >= 0; --i) { 1661 // seal all children, regardless if any of them fail; we'll throw/return 1662 // as appropriate once all children have been processed 1663 if (!mChildSessions.valueAt(i).markAsSealed(null, forTransfer)) { 1664 sealFailed = true; 1665 } 1666 } 1667 if (sealFailed) { 1668 return; 1669 } 1670 } 1671 } 1672 1673 dispatchSessionSealed(); 1674 } 1675 1676 /** 1677 * Kicks off the install flow. The first step is to persist 'sealed' flags 1678 * to prevent mutations of hard links created later. 1679 */ dispatchSessionSealed()1680 private void dispatchSessionSealed() { 1681 mHandler.obtainMessage(MSG_ON_SESSION_SEALED).sendToTarget(); 1682 } 1683 handleSessionSealed()1684 private void handleSessionSealed() { 1685 assertSealed("dispatchSessionSealed"); 1686 // Persist the fact that we've sealed ourselves to prevent 1687 // mutations of any hard links we create. 1688 mCallback.onSessionSealedBlocking(this); 1689 dispatchStreamValidateAndCommit(); 1690 } 1691 dispatchStreamValidateAndCommit()1692 private void dispatchStreamValidateAndCommit() { 1693 mHandler.obtainMessage(MSG_STREAM_VALIDATE_AND_COMMIT).sendToTarget(); 1694 } 1695 1696 @WorkerThread handleStreamValidateAndCommit()1697 private void handleStreamValidateAndCommit() { 1698 try { 1699 // This will track whether the session and any children were validated and are ready to 1700 // progress to the next phase of install 1701 boolean allSessionsReady = true; 1702 for (PackageInstallerSession child : getChildSessions()) { 1703 allSessionsReady &= child.streamValidateAndCommit(); 1704 } 1705 if (allSessionsReady && streamValidateAndCommit()) { 1706 mHandler.obtainMessage(MSG_INSTALL).sendToTarget(); 1707 } 1708 } catch (PackageManagerException e) { 1709 destroy(); 1710 String msg = ExceptionUtils.getCompleteMessage(e); 1711 dispatchSessionFinished(e.error, msg, null); 1712 maybeFinishChildSessions(e.error, msg); 1713 } 1714 } 1715 1716 private final class FileSystemConnector extends 1717 IPackageInstallerSessionFileSystemConnector.Stub { 1718 final Set<String> mAddedFiles = new ArraySet<>(); 1719 FileSystemConnector(List<InstallationFileParcel> addedFiles)1720 FileSystemConnector(List<InstallationFileParcel> addedFiles) { 1721 for (InstallationFileParcel file : addedFiles) { 1722 mAddedFiles.add(file.name); 1723 } 1724 } 1725 1726 @Override writeData(String name, long offsetBytes, long lengthBytes, ParcelFileDescriptor incomingFd)1727 public void writeData(String name, long offsetBytes, long lengthBytes, 1728 ParcelFileDescriptor incomingFd) { 1729 if (incomingFd == null) { 1730 throw new IllegalArgumentException("incomingFd can't be null"); 1731 } 1732 if (!mAddedFiles.contains(name)) { 1733 throw new SecurityException("File name is not in the list of added files."); 1734 } 1735 try { 1736 doWriteInternal(name, offsetBytes, lengthBytes, incomingFd); 1737 } catch (IOException e) { 1738 throw ExceptionUtils.wrap(e); 1739 } 1740 } 1741 } 1742 1743 /** 1744 * Returns whether or not a package can be installed while Secure FRP is enabled. 1745 * <p> 1746 * Only callers with the INSTALL_PACKAGES permission are allowed to install. However, 1747 * prevent the package installer from installing anything because, while it has the 1748 * permission, it will allows packages to be installed from anywhere. 1749 */ isSecureFrpInstallAllowed(Context context, int callingUid)1750 private static boolean isSecureFrpInstallAllowed(Context context, int callingUid) { 1751 final PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class); 1752 final String[] systemInstaller = pmi.getKnownPackageNames( 1753 KnownPackages.PACKAGE_INSTALLER, UserHandle.USER_SYSTEM); 1754 final AndroidPackage callingInstaller = pmi.getPackage(callingUid); 1755 if (callingInstaller != null 1756 && ArrayUtils.contains(systemInstaller, callingInstaller.getPackageName())) { 1757 // don't allow the system package installer to install while under secure FRP 1758 return false; 1759 } 1760 1761 // require caller to hold the INSTALL_PACKAGES permission 1762 return context.checkCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES) 1763 == PackageManager.PERMISSION_GRANTED; 1764 } 1765 1766 /** 1767 * Checks if the package can be installed on IncFs. 1768 */ isIncrementalInstallationAllowed(String packageName)1769 private static boolean isIncrementalInstallationAllowed(String packageName) { 1770 final PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class); 1771 final PackageStateInternal existingPkgSetting = pmi.getPackageStateInternal(packageName); 1772 if (existingPkgSetting == null || existingPkgSetting.getPkg() == null) { 1773 return true; 1774 } 1775 1776 return !existingPkgSetting.getPkg().isSystem() 1777 && !existingPkgSetting.getTransientState().isUpdatedSystemApp(); 1778 } 1779 1780 /** 1781 * If this was not already called, the session will be sealed. 1782 * 1783 * This method may be called multiple times to update the status receiver validate caller 1784 * permissions. 1785 */ markAsSealed(@ullable IntentSender statusReceiver, boolean forTransfer)1786 private boolean markAsSealed(@Nullable IntentSender statusReceiver, boolean forTransfer) { 1787 Preconditions.checkState(statusReceiver != null || hasParentSessionId(), 1788 "statusReceiver can't be null for the root session"); 1789 assertCallerIsOwnerOrRoot(); 1790 1791 synchronized (mLock) { 1792 assertPreparedAndNotDestroyedLocked("commit of session " + sessionId); 1793 assertNoWriteFileTransfersOpenLocked(); 1794 1795 final boolean isSecureFrpEnabled = 1796 (Secure.getInt(mContext.getContentResolver(), Secure.SECURE_FRP_MODE, 0) == 1); 1797 if (isSecureFrpEnabled 1798 && !isSecureFrpInstallAllowed(mContext, Binder.getCallingUid())) { 1799 throw new SecurityException("Can't install packages while in secure FRP"); 1800 } 1801 1802 if (forTransfer) { 1803 mContext.enforceCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES, null); 1804 if (mInstallerUid == mOriginalInstallerUid) { 1805 throw new IllegalArgumentException("Session has not been transferred"); 1806 } 1807 } else { 1808 if (mInstallerUid != mOriginalInstallerUid) { 1809 throw new IllegalArgumentException("Session has been transferred"); 1810 } 1811 } 1812 1813 setRemoteStatusReceiver(statusReceiver); 1814 1815 // After updating the observer, we can skip re-sealing. 1816 if (mSealed) { 1817 return true; 1818 } 1819 1820 try { 1821 sealLocked(); 1822 } catch (PackageManagerException e) { 1823 return false; 1824 } 1825 } 1826 1827 return true; 1828 } 1829 1830 /** 1831 * Returns true if the session is successfully validated and committed. Returns false if the 1832 * dataloader could not be prepared. This can be called multiple times so long as no 1833 * exception is thrown. 1834 * @throws PackageManagerException on an unrecoverable error. 1835 */ 1836 @WorkerThread streamValidateAndCommit()1837 private boolean streamValidateAndCommit() throws PackageManagerException { 1838 // TODO(patb): since the work done here for a parent session in a multi-package install is 1839 // mostly superficial, consider splitting this method for the parent and 1840 // single / child sessions. 1841 try { 1842 synchronized (mLock) { 1843 if (mCommitted.get()) { 1844 return true; 1845 } 1846 // Read transfers from the original owner stay open, but as the session's data 1847 // cannot be modified anymore, there is no leak of information. For staged sessions, 1848 // further validation is performed by the staging manager. 1849 if (!params.isMultiPackage) { 1850 if (!prepareDataLoaderLocked()) { 1851 return false; 1852 } 1853 1854 if (isApexSession()) { 1855 validateApexInstallLocked(); 1856 } else { 1857 validateApkInstallLocked(); 1858 } 1859 } 1860 if (mDestroyed) { 1861 throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, 1862 "Session destroyed"); 1863 } 1864 if (!isIncrementalInstallation()) { 1865 synchronized (mProgressLock) { 1866 // For non-incremental installs, client staging is fully done at this point 1867 mClientProgress = 1f; 1868 computeProgressLocked(true); 1869 } 1870 } 1871 1872 // This ongoing commit should keep session active, even though client 1873 // will probably close their end. 1874 mActiveCount.incrementAndGet(); 1875 1876 if (!mCommitted.compareAndSet(false /*expect*/, true /*update*/)) { 1877 throw new PackageManagerException( 1878 INSTALL_FAILED_INTERNAL_ERROR, 1879 TextUtils.formatSimple( 1880 "The mCommitted of session %d should be false originally", 1881 sessionId)); 1882 } 1883 committedMillis = System.currentTimeMillis(); 1884 } 1885 return true; 1886 } catch (PackageManagerException e) { 1887 throw e; 1888 } catch (Throwable e) { 1889 // Convert all exceptions into package manager exceptions as only those are handled 1890 // in the code above. 1891 throw new PackageManagerException(e); 1892 } 1893 } 1894 1895 @GuardedBy("mLock") getChildSessionsLocked()1896 private @NonNull List<PackageInstallerSession> getChildSessionsLocked() { 1897 List<PackageInstallerSession> childSessions = Collections.EMPTY_LIST; 1898 if (isMultiPackage()) { 1899 int size = mChildSessions.size(); 1900 childSessions = new ArrayList<>(size); 1901 for (int i = 0; i < size; ++i) { 1902 childSessions.add(mChildSessions.valueAt(i)); 1903 } 1904 } 1905 return childSessions; 1906 } 1907 getChildSessions()1908 @NonNull List<PackageInstallerSession> getChildSessions() { 1909 synchronized (mLock) { 1910 return getChildSessionsLocked(); 1911 } 1912 } 1913 1914 /** 1915 * Seal the session to prevent further modification. 1916 * 1917 * <p>The session will be sealed after calling this method even if it failed. 1918 * 1919 * @throws PackageManagerException if the session was sealed but something went wrong. If the 1920 * session was sealed this is the only possible exception. 1921 */ 1922 @GuardedBy("mLock") sealLocked()1923 private void sealLocked() 1924 throws PackageManagerException { 1925 try { 1926 assertNoWriteFileTransfersOpenLocked(); 1927 assertPreparedAndNotDestroyedLocked("sealing of session " + sessionId); 1928 mSealed = true; 1929 } catch (Throwable e) { 1930 // Convert all exceptions into package manager exceptions as only those are handled 1931 // in the code above. 1932 throw onSessionValidationFailure(new PackageManagerException(e)); 1933 } 1934 } 1935 onSessionValidationFailure(PackageManagerException e)1936 private PackageManagerException onSessionValidationFailure(PackageManagerException e) { 1937 onSessionValidationFailure(e.error, ExceptionUtils.getCompleteMessage(e)); 1938 return e; 1939 } 1940 onSessionValidationFailure(int error, String detailMessage)1941 private void onSessionValidationFailure(int error, String detailMessage) { 1942 // Session is sealed but could not be validated, we need to destroy it. 1943 destroyInternal(); 1944 // Dispatch message to remove session from PackageInstallerService. 1945 dispatchSessionFinished(error, detailMessage, null); 1946 } 1947 onSessionVerificationFailure(int error, String msg)1948 private void onSessionVerificationFailure(int error, String msg) { 1949 Slog.e(TAG, "Failed to verify session " + sessionId); 1950 // Dispatch message to remove session from PackageInstallerService. 1951 dispatchSessionFinished(error, msg, null); 1952 maybeFinishChildSessions(error, msg); 1953 } 1954 onSystemDataLoaderUnrecoverable()1955 private void onSystemDataLoaderUnrecoverable() { 1956 final DeletePackageHelper deletePackageHelper = new DeletePackageHelper(mPm); 1957 final String packageName = getPackageName(); 1958 if (TextUtils.isEmpty(packageName)) { 1959 // The package has not been installed. 1960 return; 1961 } 1962 mHandler.post(() -> { 1963 if (deletePackageHelper.deletePackageX(packageName, 1964 PackageManager.VERSION_CODE_HIGHEST, UserHandle.USER_SYSTEM, 1965 PackageManager.DELETE_ALL_USERS, true /*removedBySystem*/) 1966 != PackageManager.DELETE_SUCCEEDED) { 1967 Slog.e(TAG, "Failed to uninstall package with failed dataloader: " + packageName); 1968 } 1969 }); 1970 } 1971 1972 /** 1973 * If session should be sealed, then it's sealed to prevent further modification. 1974 * If the session can't be sealed then it's destroyed. 1975 * 1976 * Additionally for staged APEX/APK sessions read+validate the package and populate req'd 1977 * fields. 1978 * 1979 * <p> This is meant to be called after all of the sessions are loaded and added to 1980 * PackageInstallerService 1981 * 1982 * @param allSessions All sessions loaded by PackageInstallerService, guaranteed to be 1983 * immutable by the caller during the method call. Used to resolve child 1984 * sessions Ids to actual object reference. 1985 */ 1986 @AnyThread onAfterSessionRead(SparseArray<PackageInstallerSession> allSessions)1987 void onAfterSessionRead(SparseArray<PackageInstallerSession> allSessions) { 1988 synchronized (mLock) { 1989 // Resolve null values to actual object references 1990 for (int i = mChildSessions.size() - 1; i >= 0; --i) { 1991 int childSessionId = mChildSessions.keyAt(i); 1992 PackageInstallerSession childSession = allSessions.get(childSessionId); 1993 if (childSession != null) { 1994 mChildSessions.setValueAt(i, childSession); 1995 } else { 1996 Slog.e(TAG, "Child session not existed: " + childSessionId); 1997 mChildSessions.removeAt(i); 1998 } 1999 } 2000 2001 if (!mShouldBeSealed || isStagedAndInTerminalState()) { 2002 return; 2003 } 2004 try { 2005 sealLocked(); 2006 2007 // Session that are staged, committed and not multi package will be installed or 2008 // restart verification during this boot. As such, we need populate all the fields 2009 // for successful installation. 2010 if (isMultiPackage() || !isStaged() || !isCommitted()) { 2011 return; 2012 } 2013 final PackageInstallerSession root = hasParentSessionId() 2014 ? allSessions.get(getParentSessionId()) 2015 : this; 2016 if (root != null && !root.isStagedAndInTerminalState()) { 2017 if (isApexSession()) { 2018 validateApexInstallLocked(); 2019 } else { 2020 validateApkInstallLocked(); 2021 } 2022 } 2023 } catch (PackageManagerException e) { 2024 Slog.e(TAG, "Package not valid", e); 2025 } 2026 } 2027 } 2028 2029 /** Update the timestamp of when the staged session last changed state */ markUpdated()2030 public void markUpdated() { 2031 synchronized (mLock) { 2032 this.updatedMillis = System.currentTimeMillis(); 2033 } 2034 } 2035 2036 @Override transfer(String packageName)2037 public void transfer(String packageName) { 2038 Preconditions.checkArgument(!TextUtils.isEmpty(packageName)); 2039 final Computer snapshot = mPm.snapshotComputer(); 2040 ApplicationInfo newOwnerAppInfo = snapshot.getApplicationInfo(packageName, 0, userId); 2041 if (newOwnerAppInfo == null) { 2042 throw new ParcelableException(new PackageManager.NameNotFoundException(packageName)); 2043 } 2044 2045 if (PackageManager.PERMISSION_GRANTED != snapshot.checkUidPermission( 2046 Manifest.permission.INSTALL_PACKAGES, newOwnerAppInfo.uid)) { 2047 throw new SecurityException("Destination package " + packageName + " does not have " 2048 + "the " + Manifest.permission.INSTALL_PACKAGES + " permission"); 2049 } 2050 2051 // Only install flags that can be verified by the app the session is transferred to are 2052 // allowed. The parameters can be read via PackageInstaller.SessionInfo. 2053 if (!params.areHiddenOptionsSet()) { 2054 throw new SecurityException("Can only transfer sessions that use public options"); 2055 } 2056 2057 synchronized (mLock) { 2058 assertCallerIsOwnerOrRoot(); 2059 assertPreparedAndNotSealedLocked("transfer"); 2060 2061 try { 2062 sealLocked(); 2063 } catch (PackageManagerException e) { 2064 throw new IllegalStateException("Package is not valid", e); 2065 } 2066 2067 mInstallerUid = newOwnerAppInfo.uid; 2068 mInstallSource = InstallSource.create(packageName, null, packageName, null, 2069 params.packageSource); 2070 } 2071 } 2072 2073 @WorkerThread checkUserActionRequirement( PackageInstallerSession session, IntentSender target)2074 private static boolean checkUserActionRequirement( 2075 PackageInstallerSession session, IntentSender target) { 2076 if (session.isMultiPackage()) { 2077 return false; 2078 } 2079 2080 @UserActionRequirement int userActionRequirement = USER_ACTION_NOT_NEEDED; 2081 // TODO(b/159331446): Move this to makeSessionActiveForInstall and update javadoc 2082 userActionRequirement = session.computeUserActionRequirement(); 2083 if (userActionRequirement == USER_ACTION_REQUIRED) { 2084 session.sendPendingUserActionIntent(target); 2085 return true; 2086 } 2087 2088 if (!session.isApexSession() && userActionRequirement == USER_ACTION_PENDING_APK_PARSING) { 2089 final int validatedTargetSdk; 2090 synchronized (session.mLock) { 2091 validatedTargetSdk = session.mValidatedTargetSdk; 2092 } 2093 2094 if (validatedTargetSdk != INVALID_TARGET_SDK_VERSION 2095 && validatedTargetSdk < Build.VERSION_CODES.R) { 2096 session.sendPendingUserActionIntent(target); 2097 return true; 2098 } 2099 2100 if (session.params.requireUserAction == SessionParams.USER_ACTION_NOT_REQUIRED) { 2101 if (!session.mSilentUpdatePolicy.isSilentUpdateAllowed( 2102 session.getInstallerPackageName(), session.getPackageName())) { 2103 // Fall back to the non-silent update if a repeated installation is invoked 2104 // within the throttle time. 2105 session.sendPendingUserActionIntent(target); 2106 return true; 2107 } 2108 session.mSilentUpdatePolicy.track(session.getInstallerPackageName(), 2109 session.getPackageName()); 2110 } 2111 } 2112 2113 return false; 2114 } 2115 2116 /** 2117 * Find out any session needs user action. 2118 * 2119 * @return true if the session set requires user action for the installation, otherwise false. 2120 */ 2121 @WorkerThread sendPendingUserActionIntentIfNeeded()2122 private boolean sendPendingUserActionIntentIfNeeded() { 2123 assertNotChild("PackageInstallerSession#sendPendingUserActionIntentIfNeeded"); 2124 2125 final IntentSender statusReceiver = getRemoteStatusReceiver(); 2126 return sessionContains(s -> checkUserActionRequirement(s, statusReceiver)); 2127 } 2128 2129 @WorkerThread handleInstall()2130 private void handleInstall() { 2131 if (isInstallerDeviceOwnerOrAffiliatedProfileOwner()) { 2132 DevicePolicyEventLogger 2133 .createEvent(DevicePolicyEnums.INSTALL_PACKAGE) 2134 .setAdmin(getInstallSource().installerPackageName) 2135 .write(); 2136 } 2137 2138 /** 2139 * Stops the installation of the whole session set if one session needs user action 2140 * in its belong session set. When the user answers the yes, 2141 * {@link #setPermissionsResult(boolean)} is called and then {@link #MSG_INSTALL} is 2142 * handled to come back here to check again. 2143 */ 2144 if (sendPendingUserActionIntentIfNeeded()) { 2145 return; 2146 } 2147 2148 if (mVerificationInProgress) { 2149 Slog.w(TAG, "Verification is already in progress for session " + sessionId); 2150 return; 2151 } 2152 mVerificationInProgress = true; 2153 2154 if (params.isStaged) { 2155 mStagedSession.verifySession(); 2156 } else { 2157 verify(); 2158 } 2159 } 2160 verify()2161 private void verify() { 2162 try { 2163 List<PackageInstallerSession> children = getChildSessions(); 2164 if (isMultiPackage()) { 2165 for (PackageInstallerSession child : children) { 2166 child.prepareInheritedFiles(); 2167 child.parseApkAndExtractNativeLibraries(); 2168 } 2169 } else { 2170 prepareInheritedFiles(); 2171 parseApkAndExtractNativeLibraries(); 2172 } 2173 verifyNonStaged(); 2174 } catch (PackageManagerException e) { 2175 final String completeMsg = ExceptionUtils.getCompleteMessage(e); 2176 final String errorMsg = PackageManager.installStatusToString(e.error, completeMsg); 2177 setSessionFailed(e.error, errorMsg); 2178 onSessionVerificationFailure(e.error, errorMsg); 2179 } 2180 } 2181 getRemoteStatusReceiver()2182 private IntentSender getRemoteStatusReceiver() { 2183 synchronized (mLock) { 2184 return mRemoteStatusReceiver; 2185 } 2186 } 2187 setRemoteStatusReceiver(IntentSender remoteStatusReceiver)2188 private void setRemoteStatusReceiver(IntentSender remoteStatusReceiver) { 2189 synchronized (mLock) { 2190 mRemoteStatusReceiver = remoteStatusReceiver; 2191 } 2192 } 2193 2194 /** 2195 * Prepares staged directory with any inherited APKs. 2196 */ prepareInheritedFiles()2197 private void prepareInheritedFiles() throws PackageManagerException { 2198 if (isApexSession() || params.mode != SessionParams.MODE_INHERIT_EXISTING) { 2199 return; 2200 } 2201 synchronized (mLock) { 2202 if (mStageDirInUse) { 2203 throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, 2204 "Session files in use"); 2205 } 2206 if (mDestroyed) { 2207 throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, 2208 "Session destroyed"); 2209 } 2210 if (!mSealed) { 2211 throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, 2212 "Session not sealed"); 2213 } 2214 // Inherit any packages and native libraries from existing install that 2215 // haven't been overridden. 2216 try { 2217 final List<File> fromFiles = mResolvedInheritedFiles; 2218 final File toDir = stageDir; 2219 final String tempPackageName = toDir.getName(); 2220 2221 if (LOGD) Slog.d(TAG, "Inherited files: " + mResolvedInheritedFiles); 2222 if (!mResolvedInheritedFiles.isEmpty() && mInheritedFilesBase == null) { 2223 throw new IllegalStateException("mInheritedFilesBase == null"); 2224 } 2225 2226 if (isLinkPossible(fromFiles, toDir)) { 2227 if (!mResolvedInstructionSets.isEmpty()) { 2228 final File oatDir = new File(toDir, "oat"); 2229 createOatDirs(tempPackageName, mResolvedInstructionSets, oatDir); 2230 } 2231 // pre-create lib dirs for linking if necessary 2232 if (!mResolvedNativeLibPaths.isEmpty()) { 2233 for (String libPath : mResolvedNativeLibPaths) { 2234 // "/lib/arm64" -> ["lib", "arm64"] 2235 final int splitIndex = libPath.lastIndexOf('/'); 2236 if (splitIndex < 0 || splitIndex >= libPath.length() - 1) { 2237 Slog.e(TAG, 2238 "Skipping native library creation for linking due" 2239 + " to invalid path: " + libPath); 2240 continue; 2241 } 2242 final String libDirPath = libPath.substring(1, splitIndex); 2243 final File libDir = new File(toDir, libDirPath); 2244 if (!libDir.exists()) { 2245 NativeLibraryHelper.createNativeLibrarySubdir(libDir); 2246 } 2247 final String archDirPath = libPath.substring(splitIndex + 1); 2248 NativeLibraryHelper.createNativeLibrarySubdir( 2249 new File(libDir, archDirPath)); 2250 } 2251 } 2252 linkFiles(tempPackageName, fromFiles, toDir, mInheritedFilesBase); 2253 } else { 2254 // TODO: this should delegate to DCS so the system process 2255 // avoids holding open FDs into containers. 2256 copyFiles(fromFiles, toDir); 2257 } 2258 } catch (IOException e) { 2259 throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE, 2260 "Failed to inherit existing install", e); 2261 } 2262 } 2263 } 2264 2265 @GuardedBy("mLock") markStageDirInUseLocked()2266 private void markStageDirInUseLocked() throws PackageManagerException { 2267 if (mDestroyed) { 2268 throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, 2269 "Session destroyed"); 2270 } 2271 // Set this flag to prevent abandon() from deleting staging files when verification or 2272 // installation is about to start. 2273 mStageDirInUse = true; 2274 } 2275 parseApkAndExtractNativeLibraries()2276 private void parseApkAndExtractNativeLibraries() throws PackageManagerException { 2277 synchronized (mLock) { 2278 if (mStageDirInUse) { 2279 throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, 2280 "Session files in use"); 2281 } 2282 if (mDestroyed) { 2283 throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, 2284 "Session destroyed"); 2285 } 2286 if (!mSealed) { 2287 throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, 2288 "Session not sealed"); 2289 } 2290 Objects.requireNonNull(mPackageName); 2291 Objects.requireNonNull(mSigningDetails); 2292 Objects.requireNonNull(mResolvedBaseFile); 2293 final PackageLite result; 2294 if (!isApexSession()) { 2295 // For mode inherit existing, it would link/copy existing files to stage dir in 2296 // prepareInheritedFiles(). Therefore, we need to parse the complete package in 2297 // stage dir here. 2298 // Besides, PackageLite may be null for staged sessions that don't complete 2299 // pre-reboot verification. 2300 result = getOrParsePackageLiteLocked(stageDir, /* flags */ 0); 2301 } else { 2302 result = getOrParsePackageLiteLocked(mResolvedBaseFile, /* flags */ 0); 2303 } 2304 if (result != null) { 2305 mPackageLite = result; 2306 if (!isApexSession()) { 2307 synchronized (mProgressLock) { 2308 mInternalProgress = 0.5f; 2309 computeProgressLocked(true); 2310 } 2311 extractNativeLibraries( 2312 mPackageLite, stageDir, params.abiOverride, mayInheritNativeLibs()); 2313 } 2314 } 2315 } 2316 } 2317 verifyNonStaged()2318 private void verifyNonStaged() 2319 throws PackageManagerException { 2320 synchronized (mLock) { 2321 markStageDirInUseLocked(); 2322 } 2323 mSessionProvider.getSessionVerifier().verify(this, (error, msg) -> { 2324 mHandler.post(() -> { 2325 if (dispatchPendingAbandonCallback()) { 2326 // No need to continue if abandoned 2327 return; 2328 } 2329 if (error == INSTALL_SUCCEEDED) { 2330 onVerificationComplete(); 2331 } else { 2332 onSessionVerificationFailure(error, msg); 2333 } 2334 }); 2335 }); 2336 } 2337 2338 private static class InstallResult { 2339 public final PackageInstallerSession session; 2340 public final Bundle extras; InstallResult(PackageInstallerSession session, Bundle extras)2341 InstallResult(PackageInstallerSession session, Bundle extras) { 2342 this.session = session; 2343 this.extras = extras; 2344 } 2345 } 2346 2347 /** 2348 * Stages installs and do cleanup accordingly depending on whether the installation is 2349 * successful or not. 2350 * 2351 * @return a future that will be completed when the whole process is completed. 2352 */ install()2353 private CompletableFuture<Void> install() { 2354 List<CompletableFuture<InstallResult>> futures = installNonStaged(); 2355 CompletableFuture<InstallResult>[] arr = new CompletableFuture[futures.size()]; 2356 return CompletableFuture.allOf(futures.toArray(arr)).whenComplete((r, t) -> { 2357 if (t == null) { 2358 setSessionApplied(); 2359 for (CompletableFuture<InstallResult> f : futures) { 2360 InstallResult result = f.join(); 2361 result.session.dispatchSessionFinished( 2362 INSTALL_SUCCEEDED, "Session installed", result.extras); 2363 } 2364 } else { 2365 PackageManagerException e = (PackageManagerException) t.getCause(); 2366 setSessionFailed(e.error, 2367 PackageManager.installStatusToString(e.error, e.getMessage())); 2368 dispatchSessionFinished(e.error, e.getMessage(), null); 2369 maybeFinishChildSessions(e.error, e.getMessage()); 2370 } 2371 }); 2372 } 2373 2374 /** 2375 * Stages sessions (including child sessions if any) for install. 2376 * 2377 * @return a list of futures to indicate the install results of each session. 2378 */ 2379 private List<CompletableFuture<InstallResult>> installNonStaged() { 2380 try { 2381 List<CompletableFuture<InstallResult>> futures = new ArrayList<>(); 2382 CompletableFuture<InstallResult> future = new CompletableFuture<>(); 2383 futures.add(future); 2384 final InstallParams installingSession = makeInstallParams(future); 2385 if (isMultiPackage()) { 2386 final List<PackageInstallerSession> childSessions = getChildSessions(); 2387 List<InstallParams> installingChildSessions = new ArrayList<>(childSessions.size()); 2388 for (int i = 0; i < childSessions.size(); ++i) { 2389 final PackageInstallerSession session = childSessions.get(i); 2390 future = new CompletableFuture<>(); 2391 futures.add(future); 2392 final InstallParams installingChildSession = session.makeInstallParams(future); 2393 if (installingChildSession != null) { 2394 installingChildSessions.add(installingChildSession); 2395 } 2396 } 2397 if (!installingChildSessions.isEmpty()) { 2398 installingSession.installStage(installingChildSessions); 2399 } 2400 } else if (installingSession != null) { 2401 installingSession.installStage(); 2402 } 2403 2404 return futures; 2405 } catch (PackageManagerException e) { 2406 List<CompletableFuture<InstallResult>> futures = new ArrayList<>(); 2407 futures.add(CompletableFuture.failedFuture(e)); 2408 return futures; 2409 } 2410 } 2411 2412 private void sendPendingUserActionIntent(IntentSender target) { 2413 // User needs to confirm installation; 2414 // give installer an intent they can use to involve 2415 // user. 2416 final Intent intent = new Intent(PackageInstaller.ACTION_CONFIRM_INSTALL); 2417 intent.setPackage(mPm.getPackageInstallerPackageName()); 2418 intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId); 2419 2420 sendOnUserActionRequired(mContext, target, sessionId, intent); 2421 2422 // Commit was keeping session marked as active until now; release 2423 // that extra refcount so session appears idle. 2424 closeInternal(false); 2425 } 2426 2427 @WorkerThread 2428 private void onVerificationComplete() { 2429 if (isStaged()) { 2430 mStagingManager.commitSession(mStagedSession); 2431 sendUpdateToRemoteStatusReceiver(INSTALL_SUCCEEDED, "Session staged", null); 2432 return; 2433 } 2434 install(); 2435 } 2436 2437 /** 2438 * Stages this session for install and returns a 2439 * {@link InstallParams} representing this new staged state. 2440 * 2441 * @param future a future that will be completed when this session is completed. 2442 */ 2443 @Nullable 2444 private InstallParams makeInstallParams(CompletableFuture<InstallResult> future) 2445 throws PackageManagerException { 2446 synchronized (mLock) { 2447 if (!mSealed) { 2448 throw new PackageManagerException( 2449 INSTALL_FAILED_INTERNAL_ERROR, "Session not sealed"); 2450 } 2451 markStageDirInUseLocked(); 2452 } 2453 2454 if (isMultiPackage()) { 2455 // Always treat parent session as success for it has nothing to install 2456 future.complete(new InstallResult(this, null)); 2457 } else if (isApexSession() && params.isStaged) { 2458 // Staged apex sessions have been handled by apexd 2459 future.complete(new InstallResult(this, null)); 2460 return null; 2461 } 2462 2463 final IPackageInstallObserver2 localObserver = new IPackageInstallObserver2.Stub() { 2464 @Override 2465 public void onUserActionRequired(Intent intent) { 2466 throw new IllegalStateException(); 2467 } 2468 2469 @Override 2470 public void onPackageInstalled(String basePackageName, int returnCode, String msg, 2471 Bundle extras) { 2472 if (returnCode == INSTALL_SUCCEEDED) { 2473 future.complete(new InstallResult(PackageInstallerSession.this, extras)); 2474 } else { 2475 future.completeExceptionally(new PackageManagerException(returnCode, msg)); 2476 } 2477 } 2478 }; 2479 2480 final UserHandle user; 2481 if ((params.installFlags & PackageManager.INSTALL_ALL_USERS) != 0) { 2482 user = UserHandle.ALL; 2483 } else { 2484 user = new UserHandle(userId); 2485 } 2486 2487 if (params.isStaged) { 2488 params.installFlags |= INSTALL_STAGED; 2489 } 2490 2491 if (!isMultiPackage() && !isApexSession()) { 2492 synchronized (mLock) { 2493 // This shouldn't be null, but have this code path just in case. 2494 if (mPackageLite == null) { 2495 Slog.wtf(TAG, "Session: " + sessionId + ". Don't have a valid PackageLite."); 2496 } 2497 mPackageLite = getOrParsePackageLiteLocked(stageDir, /* flags */ 0); 2498 } 2499 } 2500 2501 synchronized (mLock) { 2502 return new InstallParams(stageDir, localObserver, params, mInstallSource, user, 2503 mSigningDetails, mInstallerUid, mPackageLite, mPm); 2504 } 2505 } 2506 2507 @GuardedBy("mLock") 2508 private PackageLite getOrParsePackageLiteLocked(File packageFile, int flags) 2509 throws PackageManagerException { 2510 if (mPackageLite != null) { 2511 return mPackageLite; 2512 } 2513 2514 final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing(); 2515 final ParseResult<PackageLite> result = 2516 ApkLiteParseUtils.parsePackageLite(input, packageFile, flags); 2517 if (result.isError()) { 2518 throw new PackageManagerException(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, 2519 result.getErrorMessage(), result.getException()); 2520 } 2521 return result.getResult(); 2522 } 2523 2524 private static void maybeRenameFile(File from, File to) throws PackageManagerException { 2525 if (!from.equals(to)) { 2526 if (!from.renameTo(to)) { 2527 throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, 2528 "Could not rename file " + from + " to " + to); 2529 } 2530 } 2531 } 2532 2533 private void logDataLoaderInstallationSession(int returnCode) { 2534 // Skip logging the side-loaded app installations, as those are private and aren't reported 2535 // anywhere; app stores already have a record of the installation and that's why reporting 2536 // it here is fine 2537 final String packageName = getPackageName(); 2538 final String packageNameToLog = 2539 (params.installFlags & PackageManager.INSTALL_FROM_ADB) == 0 ? packageName : ""; 2540 final long currentTimestamp = System.currentTimeMillis(); 2541 final int packageUid; 2542 if (returnCode != INSTALL_SUCCEEDED) { 2543 // Package didn't install; no valid uid 2544 packageUid = Process.INVALID_UID; 2545 } else { 2546 packageUid = mPm.snapshotComputer().getPackageUid(packageName, 0, userId); 2547 } 2548 FrameworkStatsLog.write(FrameworkStatsLog.PACKAGE_INSTALLER_V2_REPORTED, 2549 isIncrementalInstallation(), 2550 packageNameToLog, 2551 currentTimestamp - createdMillis, 2552 returnCode, 2553 getApksSize(packageName), 2554 packageUid); 2555 } 2556 2557 private long getApksSize(String packageName) { 2558 final PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class); 2559 final PackageStateInternal ps = pmi.getPackageStateInternal(packageName); 2560 if (ps == null) { 2561 return 0; 2562 } 2563 final File apkDirOrPath = ps.getPath(); 2564 if (apkDirOrPath == null) { 2565 return 0; 2566 } 2567 if (apkDirOrPath.isFile() && apkDirOrPath.getName().toLowerCase().endsWith(".apk")) { 2568 return apkDirOrPath.length(); 2569 } 2570 if (!apkDirOrPath.isDirectory()) { 2571 return 0; 2572 } 2573 final File[] files = apkDirOrPath.listFiles(); 2574 long apksSize = 0; 2575 for (int i = 0; i < files.length; i++) { 2576 if (files[i].getName().toLowerCase().endsWith(".apk")) { 2577 apksSize += files[i].length(); 2578 } 2579 } 2580 return apksSize; 2581 } 2582 2583 /** 2584 * Returns true if the session should attempt to inherit any existing native libraries already 2585 * extracted at the current install location. This is necessary to prevent double loading of 2586 * native libraries already loaded by the running app. 2587 */ 2588 private boolean mayInheritNativeLibs() { 2589 return SystemProperties.getBoolean(PROPERTY_NAME_INHERIT_NATIVE, true) && 2590 params.mode == SessionParams.MODE_INHERIT_EXISTING && 2591 (params.installFlags & PackageManager.DONT_KILL_APP) != 0; 2592 } 2593 2594 /** 2595 * Returns true if the session is installing an APEX package. 2596 */ 2597 boolean isApexSession() { 2598 return (params.installFlags & PackageManager.INSTALL_APEX) != 0; 2599 } 2600 2601 boolean sessionContains(Predicate<PackageInstallerSession> filter) { 2602 if (!isMultiPackage()) { 2603 return filter.test(this); 2604 } 2605 final List<PackageInstallerSession> childSessions; 2606 synchronized (mLock) { 2607 childSessions = getChildSessionsLocked(); 2608 } 2609 for (PackageInstallerSession child: childSessions) { 2610 if (filter.test(child)) { 2611 return true; 2612 } 2613 } 2614 return false; 2615 } 2616 2617 boolean containsApkSession() { 2618 return sessionContains((s) -> !s.isApexSession()); 2619 } 2620 2621 /** 2622 * Validate apex install. 2623 * <p> 2624 * Sets {@link #mResolvedBaseFile} for RollbackManager to use. Sets {@link #mPackageName} for 2625 * StagingManager to use. 2626 */ 2627 @GuardedBy("mLock") 2628 private void validateApexInstallLocked() 2629 throws PackageManagerException { 2630 final List<File> addedFiles = getAddedApksLocked(); 2631 if (addedFiles.isEmpty()) { 2632 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 2633 TextUtils.formatSimple("Session: %d. No packages staged in %s", sessionId, 2634 stageDir.getAbsolutePath())); 2635 } 2636 2637 if (ArrayUtils.size(addedFiles) > 1) { 2638 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 2639 "Too many files for apex install"); 2640 } 2641 2642 File addedFile = addedFiles.get(0); // there is only one file 2643 2644 // Ensure file name has proper suffix 2645 final String sourceName = addedFile.getName(); 2646 final String targetName = sourceName.endsWith(APEX_FILE_EXTENSION) 2647 ? sourceName 2648 : sourceName + APEX_FILE_EXTENSION; 2649 if (!FileUtils.isValidExtFilename(targetName)) { 2650 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 2651 "Invalid filename: " + targetName); 2652 } 2653 2654 final File targetFile = new File(stageDir, targetName); 2655 resolveAndStageFileLocked(addedFile, targetFile, null); 2656 mResolvedBaseFile = targetFile; 2657 2658 // Populate package name of the apex session 2659 mPackageName = null; 2660 final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing(); 2661 final ParseResult<ApkLite> ret = ApkLiteParseUtils.parseApkLite(input.reset(), 2662 mResolvedBaseFile, ParsingPackageUtils.PARSE_COLLECT_CERTIFICATES); 2663 if (ret.isError()) { 2664 throw new PackageManagerException(ret.getErrorCode(), ret.getErrorMessage(), 2665 ret.getException()); 2666 } 2667 final ApkLite apk = ret.getResult(); 2668 2669 if (mPackageName == null) { 2670 mPackageName = apk.getPackageName(); 2671 mVersionCode = apk.getLongVersionCode(); 2672 } 2673 2674 mSigningDetails = apk.getSigningDetails(); 2675 mHasDeviceAdminReceiver = apk.isHasDeviceAdminReceiver(); 2676 } 2677 2678 /** 2679 * Validate install by confirming that all application packages are have 2680 * consistent package name, version code, and signing certificates. 2681 * <p> 2682 * Clears and populates {@link #mResolvedBaseFile}, 2683 * {@link #mResolvedStagedFiles}, and {@link #mResolvedInheritedFiles}. 2684 * <p> 2685 * Renames package files in stage to match split names defined inside. 2686 * <p> 2687 * Note that upgrade compatibility is still performed by 2688 * {@link PackageManagerService}. 2689 * @return a {@link PackageLite} representation of the validated APK(s). 2690 */ 2691 @GuardedBy("mLock") 2692 private PackageLite validateApkInstallLocked() throws PackageManagerException { 2693 ApkLite baseApk = null; 2694 final PackageLite packageLite; 2695 mPackageLite = null; 2696 mPackageName = null; 2697 mVersionCode = -1; 2698 mSigningDetails = SigningDetails.UNKNOWN; 2699 2700 mResolvedBaseFile = null; 2701 mResolvedStagedFiles.clear(); 2702 mResolvedInheritedFiles.clear(); 2703 2704 final PackageInfo pkgInfo = mPm.snapshotComputer().getPackageInfo( 2705 params.appPackageName, PackageManager.GET_SIGNATURES 2706 | PackageManager.MATCH_STATIC_SHARED_AND_SDK_LIBRARIES /*flags*/, userId); 2707 2708 // Partial installs must be consistent with existing install 2709 if (params.mode == SessionParams.MODE_INHERIT_EXISTING 2710 && (pkgInfo == null || pkgInfo.applicationInfo == null)) { 2711 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 2712 "Missing existing base package"); 2713 } 2714 2715 // Default to require only if existing base apk has fs-verity. 2716 mVerityFoundForApks = PackageManagerServiceUtils.isApkVerityEnabled() 2717 && params.mode == SessionParams.MODE_INHERIT_EXISTING 2718 && VerityUtils.hasFsverity(pkgInfo.applicationInfo.getBaseCodePath()); 2719 2720 final List<File> removedFiles = getRemovedFilesLocked(); 2721 final List<String> removeSplitList = new ArrayList<>(); 2722 if (!removedFiles.isEmpty()) { 2723 for (File removedFile : removedFiles) { 2724 final String fileName = removedFile.getName(); 2725 final String splitName = fileName.substring( 2726 0, fileName.length() - REMOVE_MARKER_EXTENSION.length()); 2727 removeSplitList.add(splitName); 2728 } 2729 } 2730 2731 final List<File> addedFiles = getAddedApksLocked(); 2732 if (addedFiles.isEmpty() && removeSplitList.size() == 0) { 2733 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 2734 TextUtils.formatSimple("Session: %d. No packages staged in %s", sessionId, 2735 stageDir.getAbsolutePath())); 2736 } 2737 2738 // Verify that all staged packages are internally consistent 2739 final ArraySet<String> stagedSplits = new ArraySet<>(); 2740 final ArraySet<String> stagedSplitTypes = new ArraySet<>(); 2741 final ArraySet<String> requiredSplitTypes = new ArraySet<>(); 2742 final ArrayMap<String, ApkLite> splitApks = new ArrayMap<>(); 2743 final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing(); 2744 for (File addedFile : addedFiles) { 2745 final ParseResult<ApkLite> result = ApkLiteParseUtils.parseApkLite(input.reset(), 2746 addedFile, ParsingPackageUtils.PARSE_COLLECT_CERTIFICATES); 2747 if (result.isError()) { 2748 throw new PackageManagerException(result.getErrorCode(), 2749 result.getErrorMessage(), result.getException()); 2750 } 2751 2752 final ApkLite apk = result.getResult(); 2753 if (!stagedSplits.add(apk.getSplitName())) { 2754 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 2755 "Split " + apk.getSplitName() + " was defined multiple times"); 2756 } 2757 2758 // Use first package to define unknown values 2759 if (mPackageName == null) { 2760 mPackageName = apk.getPackageName(); 2761 mVersionCode = apk.getLongVersionCode(); 2762 } 2763 if (mSigningDetails == SigningDetails.UNKNOWN) { 2764 mSigningDetails = apk.getSigningDetails(); 2765 } 2766 mHasDeviceAdminReceiver = apk.isHasDeviceAdminReceiver(); 2767 2768 assertApkConsistentLocked(String.valueOf(addedFile), apk); 2769 2770 // Take this opportunity to enforce uniform naming 2771 final String targetName = ApkLiteParseUtils.splitNameToFileName(apk); 2772 if (!FileUtils.isValidExtFilename(targetName)) { 2773 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 2774 "Invalid filename: " + targetName); 2775 } 2776 2777 // Yell loudly if installers drop attribute installLocation when apps explicitly set. 2778 if (apk.getInstallLocation() != PackageInfo.INSTALL_LOCATION_UNSPECIFIED) { 2779 final String installerPackageName = getInstallerPackageName(); 2780 if (installerPackageName != null 2781 && (params.installLocation != apk.getInstallLocation())) { 2782 Slog.wtf(TAG, installerPackageName 2783 + " drops manifest attribute android:installLocation in " + targetName 2784 + " for " + mPackageName); 2785 } 2786 } 2787 2788 final File targetFile = new File(stageDir, targetName); 2789 resolveAndStageFileLocked(addedFile, targetFile, apk.getSplitName()); 2790 2791 // Base is coming from session 2792 if (apk.getSplitName() == null) { 2793 mResolvedBaseFile = targetFile; 2794 baseApk = apk; 2795 } else { 2796 splitApks.put(apk.getSplitName(), apk); 2797 } 2798 2799 // Collect the requiredSplitTypes and staged splitTypes 2800 CollectionUtils.addAll(requiredSplitTypes, apk.getRequiredSplitTypes()); 2801 CollectionUtils.addAll(stagedSplitTypes, apk.getSplitTypes()); 2802 } 2803 2804 if (removeSplitList.size() > 0) { 2805 if (pkgInfo == null) { 2806 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 2807 "Missing existing base package for " + mPackageName); 2808 } 2809 2810 // validate split names marked for removal 2811 for (String splitName : removeSplitList) { 2812 if (!ArrayUtils.contains(pkgInfo.splitNames, splitName)) { 2813 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 2814 "Split not found: " + splitName); 2815 } 2816 } 2817 2818 // ensure we've got appropriate package name, version code and signatures 2819 if (mPackageName == null) { 2820 mPackageName = pkgInfo.packageName; 2821 mVersionCode = pkgInfo.getLongVersionCode(); 2822 } 2823 if (mSigningDetails == SigningDetails.UNKNOWN) { 2824 mSigningDetails = unsafeGetCertsWithoutVerification( 2825 pkgInfo.applicationInfo.sourceDir); 2826 } 2827 } 2828 2829 if (isIncrementalInstallation() && !isIncrementalInstallationAllowed(mPackageName)) { 2830 throw new PackageManagerException( 2831 PackageManager.INSTALL_FAILED_SESSION_INVALID, 2832 "Incremental installation of this package is not allowed."); 2833 } 2834 2835 if (mInstallerUid != mOriginalInstallerUid) { 2836 // Session has been transferred, check package name. 2837 if (TextUtils.isEmpty(mPackageName) || !mPackageName.equals( 2838 mOriginalInstallerPackageName)) { 2839 throw new PackageManagerException(PackageManager.INSTALL_FAILED_PACKAGE_CHANGED, 2840 "Can only transfer sessions that update the original installer"); 2841 } 2842 } 2843 2844 if (!mChecksums.isEmpty()) { 2845 throw new PackageManagerException( 2846 PackageManager.INSTALL_FAILED_SESSION_INVALID, 2847 "Invalid checksum name(s): " + String.join(",", mChecksums.keySet())); 2848 } 2849 2850 if (params.mode == SessionParams.MODE_FULL_INSTALL) { 2851 // Full installs must include a base package 2852 if (!stagedSplits.contains(null)) { 2853 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 2854 "Full install must include a base package"); 2855 } else if ((params.installFlags & PackageManager.INSTALL_DONT_KILL_APP) != 0) { 2856 EventLog.writeEvent(0x534e4554, "219044664"); 2857 2858 // Installing base.apk. Make sure the app is restarted. 2859 params.setDontKillApp(false); 2860 } 2861 if (baseApk.isSplitRequired() && (stagedSplits.size() <= 1 2862 || !stagedSplitTypes.containsAll(requiredSplitTypes))) { 2863 throw new PackageManagerException(INSTALL_FAILED_MISSING_SPLIT, 2864 "Missing split for " + mPackageName); 2865 } 2866 // For mode full install, we compose package lite for future usage instead of 2867 // re-parsing it again and again. 2868 final ParseResult<PackageLite> pkgLiteResult = 2869 ApkLiteParseUtils.composePackageLiteFromApks(input.reset(), stageDir, baseApk, 2870 splitApks, true); 2871 if (pkgLiteResult.isError()) { 2872 throw new PackageManagerException(pkgLiteResult.getErrorCode(), 2873 pkgLiteResult.getErrorMessage(), pkgLiteResult.getException()); 2874 } 2875 mPackageLite = pkgLiteResult.getResult(); 2876 packageLite = mPackageLite; 2877 } else { 2878 final ApplicationInfo appInfo = pkgInfo.applicationInfo; 2879 ParseResult<PackageLite> pkgLiteResult = ApkLiteParseUtils.parsePackageLite( 2880 input.reset(), new File(appInfo.getCodePath()), 0); 2881 if (pkgLiteResult.isError()) { 2882 throw new PackageManagerException(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, 2883 pkgLiteResult.getErrorMessage(), pkgLiteResult.getException()); 2884 } 2885 final PackageLite existing = pkgLiteResult.getResult(); 2886 packageLite = existing; 2887 assertPackageConsistentLocked("Existing", existing.getPackageName(), 2888 existing.getLongVersionCode()); 2889 final SigningDetails signingDetails = 2890 unsafeGetCertsWithoutVerification(existing.getBaseApkPath()); 2891 if (!mSigningDetails.signaturesMatchExactly(signingDetails)) { 2892 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 2893 "Existing signatures are inconsistent"); 2894 } 2895 2896 // Inherit base if not overridden. 2897 if (mResolvedBaseFile == null) { 2898 mResolvedBaseFile = new File(appInfo.getBaseCodePath()); 2899 inheritFileLocked(mResolvedBaseFile); 2900 // Collect the requiredSplitTypes from base 2901 CollectionUtils.addAll(requiredSplitTypes, existing.getBaseRequiredSplitTypes()); 2902 } else if ((params.installFlags & PackageManager.INSTALL_DONT_KILL_APP) != 0) { 2903 EventLog.writeEvent(0x534e4554, "219044664"); 2904 2905 // Installing base.apk. Make sure the app is restarted. 2906 params.setDontKillApp(false); 2907 } 2908 2909 // Inherit splits if not overridden. 2910 if (!ArrayUtils.isEmpty(existing.getSplitNames())) { 2911 for (int i = 0; i < existing.getSplitNames().length; i++) { 2912 final String splitName = existing.getSplitNames()[i]; 2913 final File splitFile = new File(existing.getSplitApkPaths()[i]); 2914 final boolean splitRemoved = removeSplitList.contains(splitName); 2915 if (!stagedSplits.contains(splitName) && !splitRemoved) { 2916 inheritFileLocked(splitFile); 2917 // Collect the requiredSplitTypes and staged splitTypes from splits 2918 CollectionUtils.addAll(requiredSplitTypes, 2919 existing.getRequiredSplitTypes()[i]); 2920 CollectionUtils.addAll(stagedSplitTypes, existing.getSplitTypes()[i]); 2921 } 2922 } 2923 } 2924 2925 // Inherit compiled oat directory. 2926 final File packageInstallDir = (new File(appInfo.getBaseCodePath())).getParentFile(); 2927 mInheritedFilesBase = packageInstallDir; 2928 final File oatDir = new File(packageInstallDir, "oat"); 2929 if (oatDir.exists()) { 2930 final File[] archSubdirs = oatDir.listFiles(); 2931 2932 // Keep track of all instruction sets we've seen compiled output for. 2933 // If we're linking (and not copying) inherited files, we can recreate the 2934 // instruction set hierarchy and link compiled output. 2935 if (archSubdirs != null && archSubdirs.length > 0) { 2936 final String[] instructionSets = InstructionSets.getAllDexCodeInstructionSets(); 2937 for (File archSubDir : archSubdirs) { 2938 // Skip any directory that isn't an ISA subdir. 2939 if (!ArrayUtils.contains(instructionSets, archSubDir.getName())) { 2940 continue; 2941 } 2942 2943 File[] files = archSubDir.listFiles(); 2944 if (files == null || files.length == 0) { 2945 continue; 2946 } 2947 2948 mResolvedInstructionSets.add(archSubDir.getName()); 2949 mResolvedInheritedFiles.addAll(Arrays.asList(files)); 2950 } 2951 } 2952 } 2953 2954 // Inherit native libraries for DONT_KILL sessions. 2955 if (mayInheritNativeLibs() && removeSplitList.isEmpty()) { 2956 File[] libDirs = new File[]{ 2957 new File(packageInstallDir, NativeLibraryHelper.LIB_DIR_NAME), 2958 new File(packageInstallDir, NativeLibraryHelper.LIB64_DIR_NAME)}; 2959 for (File libDir : libDirs) { 2960 if (!libDir.exists() || !libDir.isDirectory()) { 2961 continue; 2962 } 2963 final List<String> libDirsToInherit = new ArrayList<>(); 2964 final List<File> libFilesToInherit = new ArrayList<>(); 2965 for (File archSubDir : libDir.listFiles()) { 2966 if (!archSubDir.isDirectory()) { 2967 continue; 2968 } 2969 String relLibPath; 2970 try { 2971 relLibPath = getRelativePath(archSubDir, packageInstallDir); 2972 } catch (IOException e) { 2973 Slog.e(TAG, "Skipping linking of native library directory!", e); 2974 // shouldn't be possible, but let's avoid inheriting these to be safe 2975 libDirsToInherit.clear(); 2976 libFilesToInherit.clear(); 2977 break; 2978 } 2979 2980 File[] files = archSubDir.listFiles(); 2981 if (files == null || files.length == 0) { 2982 continue; 2983 } 2984 2985 libDirsToInherit.add(relLibPath); 2986 libFilesToInherit.addAll(Arrays.asList(files)); 2987 } 2988 for (String subDir : libDirsToInherit) { 2989 if (!mResolvedNativeLibPaths.contains(subDir)) { 2990 mResolvedNativeLibPaths.add(subDir); 2991 } 2992 } 2993 mResolvedInheritedFiles.addAll(libFilesToInherit); 2994 } 2995 } 2996 // For the case of split required, failed if no splits existed 2997 if (packageLite.isSplitRequired()) { 2998 final int existingSplits = ArrayUtils.size(existing.getSplitNames()); 2999 final boolean allSplitsRemoved = (existingSplits == removeSplitList.size()); 3000 final boolean onlyBaseFileStaged = (stagedSplits.size() == 1 3001 && stagedSplits.contains(null)); 3002 if ((allSplitsRemoved && (stagedSplits.isEmpty() || onlyBaseFileStaged)) 3003 || !stagedSplitTypes.containsAll(requiredSplitTypes)) { 3004 throw new PackageManagerException(INSTALL_FAILED_MISSING_SPLIT, 3005 "Missing split for " + mPackageName); 3006 } 3007 } 3008 } 3009 if (packageLite.isUseEmbeddedDex()) { 3010 for (File file : mResolvedStagedFiles) { 3011 if (file.getName().endsWith(".apk") 3012 && !DexManager.auditUncompressedDexInApk(file.getPath())) { 3013 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 3014 "Some dex are not uncompressed and aligned correctly for " 3015 + mPackageName); 3016 } 3017 } 3018 } 3019 3020 final boolean isInstallerShell = (mInstallerUid == Process.SHELL_UID); 3021 if (isInstallerShell && isIncrementalInstallation() && mIncrementalFileStorages != null) { 3022 if (!packageLite.isDebuggable() && !packageLite.isProfileableByShell()) { 3023 mIncrementalFileStorages.disallowReadLogs(); 3024 } 3025 } 3026 3027 // {@link #sendPendingUserActionIntentIfNeeded} needs to use 3028 // {@link PackageLite#getTargetSdk()} 3029 mValidatedTargetSdk = packageLite.getTargetSdk(); 3030 3031 return packageLite; 3032 } 3033 3034 @GuardedBy("mLock") 3035 private void stageFileLocked(File origFile, File targetFile) 3036 throws PackageManagerException { 3037 mResolvedStagedFiles.add(targetFile); 3038 maybeRenameFile(origFile, targetFile); 3039 } 3040 3041 @GuardedBy("mLock") 3042 private void maybeStageFsveritySignatureLocked(File origFile, File targetFile, 3043 boolean fsVerityRequired) throws PackageManagerException { 3044 final File originalSignature = new File( 3045 VerityUtils.getFsveritySignatureFilePath(origFile.getPath())); 3046 if (originalSignature.exists()) { 3047 final File stagedSignature = new File( 3048 VerityUtils.getFsveritySignatureFilePath(targetFile.getPath())); 3049 stageFileLocked(originalSignature, stagedSignature); 3050 } else if (fsVerityRequired) { 3051 throw new PackageManagerException(INSTALL_FAILED_BAD_SIGNATURE, 3052 "Missing corresponding fs-verity signature to " + origFile); 3053 } 3054 } 3055 3056 @GuardedBy("mLock") 3057 private void maybeStageDexMetadataLocked(File origFile, File targetFile) 3058 throws PackageManagerException { 3059 final File dexMetadataFile = DexMetadataHelper.findDexMetadataForFile(origFile); 3060 if (dexMetadataFile == null) { 3061 return; 3062 } 3063 3064 if (!FileUtils.isValidExtFilename(dexMetadataFile.getName())) { 3065 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 3066 "Invalid filename: " + dexMetadataFile); 3067 } 3068 final File targetDexMetadataFile = new File(stageDir, 3069 DexMetadataHelper.buildDexMetadataPathForApk(targetFile.getName())); 3070 3071 stageFileLocked(dexMetadataFile, targetDexMetadataFile); 3072 3073 // Also stage .dm.fsv_sig. .dm may be required to install with fs-verity signature on 3074 // supported on older devices. 3075 maybeStageFsveritySignatureLocked(dexMetadataFile, targetDexMetadataFile, 3076 DexMetadataHelper.isFsVerityRequired()); 3077 } 3078 3079 private IncrementalFileStorages getIncrementalFileStorages() { 3080 synchronized (mLock) { 3081 return mIncrementalFileStorages; 3082 } 3083 } 3084 3085 private void storeBytesToInstallationFile(final String localPath, final String absolutePath, 3086 final byte[] bytes) throws IOException { 3087 final IncrementalFileStorages incrementalFileStorages = getIncrementalFileStorages(); 3088 if (!isIncrementalInstallation() || incrementalFileStorages == null) { 3089 FileUtils.bytesToFile(absolutePath, bytes); 3090 } else { 3091 incrementalFileStorages.makeFile(localPath, bytes); 3092 } 3093 } 3094 3095 @GuardedBy("mLock") 3096 private void maybeStageDigestsLocked(File origFile, File targetFile, String splitName) 3097 throws PackageManagerException { 3098 final PerFileChecksum perFileChecksum = mChecksums.get(origFile.getName()); 3099 if (perFileChecksum == null) { 3100 return; 3101 } 3102 mChecksums.remove(origFile.getName()); 3103 3104 final Checksum[] checksums = perFileChecksum.getChecksums(); 3105 if (checksums.length == 0) { 3106 return; 3107 } 3108 3109 final String targetDigestsPath = ApkChecksums.buildDigestsPathForApk(targetFile.getName()); 3110 final File targetDigestsFile = new File(stageDir, targetDigestsPath); 3111 try (ByteArrayOutputStream os = new ByteArrayOutputStream()) { 3112 ApkChecksums.writeChecksums(os, checksums); 3113 3114 final byte[] signature = perFileChecksum.getSignature(); 3115 if (signature != null && signature.length > 0) { 3116 Certificate[] ignored = ApkChecksums.verifySignature(checksums, signature); 3117 } 3118 3119 // Storing and staging checksums. 3120 storeBytesToInstallationFile(targetDigestsPath, targetDigestsFile.getAbsolutePath(), 3121 os.toByteArray()); 3122 stageFileLocked(targetDigestsFile, targetDigestsFile); 3123 3124 // Storing and staging signature. 3125 if (signature == null || signature.length == 0) { 3126 return; 3127 } 3128 3129 final String targetDigestsSignaturePath = ApkChecksums.buildSignaturePathForDigests( 3130 targetDigestsPath); 3131 final File targetDigestsSignatureFile = new File(stageDir, targetDigestsSignaturePath); 3132 storeBytesToInstallationFile(targetDigestsSignaturePath, 3133 targetDigestsSignatureFile.getAbsolutePath(), signature); 3134 stageFileLocked(targetDigestsSignatureFile, targetDigestsSignatureFile); 3135 } catch (IOException e) { 3136 throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE, 3137 "Failed to store digests for " + mPackageName, e); 3138 } catch (NoSuchAlgorithmException | SignatureException e) { 3139 throw new PackageManagerException(INSTALL_PARSE_FAILED_NO_CERTIFICATES, 3140 "Failed to verify digests' signature for " + mPackageName, e); 3141 } 3142 } 3143 3144 @GuardedBy("mLock") 3145 private boolean isFsVerityRequiredForApk(File origFile, File targetFile) 3146 throws PackageManagerException { 3147 if (mVerityFoundForApks) { 3148 return true; 3149 } 3150 3151 // We haven't seen .fsv_sig for any APKs. Treat it as not required until we see one. 3152 final File originalSignature = new File( 3153 VerityUtils.getFsveritySignatureFilePath(origFile.getPath())); 3154 if (!originalSignature.exists()) { 3155 return false; 3156 } 3157 mVerityFoundForApks = true; 3158 3159 // When a signature is found, also check any previous staged APKs since they also need to 3160 // have fs-verity signature consistently. 3161 for (File file : mResolvedStagedFiles) { 3162 if (!file.getName().endsWith(".apk")) { 3163 continue; 3164 } 3165 // Ignore the current targeting file. 3166 if (targetFile.getName().equals(file.getName())) { 3167 continue; 3168 } 3169 throw new PackageManagerException(INSTALL_FAILED_BAD_SIGNATURE, 3170 "Previously staged apk is missing fs-verity signature"); 3171 } 3172 return true; 3173 } 3174 3175 @GuardedBy("mLock") 3176 private void resolveAndStageFileLocked(File origFile, File targetFile, String splitName) 3177 throws PackageManagerException { 3178 stageFileLocked(origFile, targetFile); 3179 3180 // Stage APK's fs-verity signature if present. 3181 maybeStageFsveritySignatureLocked(origFile, targetFile, 3182 isFsVerityRequiredForApk(origFile, targetFile)); 3183 // Stage dex metadata (.dm) and corresponding fs-verity signature if present. 3184 maybeStageDexMetadataLocked(origFile, targetFile); 3185 // Stage checksums (.digests) if present. 3186 maybeStageDigestsLocked(origFile, targetFile, splitName); 3187 } 3188 3189 @GuardedBy("mLock") 3190 private void maybeInheritFsveritySignatureLocked(File origFile) { 3191 // Inherit the fsverity signature file if present. 3192 final File fsveritySignatureFile = new File( 3193 VerityUtils.getFsveritySignatureFilePath(origFile.getPath())); 3194 if (fsveritySignatureFile.exists()) { 3195 mResolvedInheritedFiles.add(fsveritySignatureFile); 3196 } 3197 } 3198 3199 @GuardedBy("mLock") 3200 private void inheritFileLocked(File origFile) { 3201 mResolvedInheritedFiles.add(origFile); 3202 3203 maybeInheritFsveritySignatureLocked(origFile); 3204 3205 // Inherit the dex metadata if present. 3206 final File dexMetadataFile = 3207 DexMetadataHelper.findDexMetadataForFile(origFile); 3208 if (dexMetadataFile != null) { 3209 mResolvedInheritedFiles.add(dexMetadataFile); 3210 maybeInheritFsveritySignatureLocked(dexMetadataFile); 3211 } 3212 // Inherit the digests if present. 3213 final File digestsFile = ApkChecksums.findDigestsForFile(origFile); 3214 if (digestsFile != null) { 3215 mResolvedInheritedFiles.add(digestsFile); 3216 3217 final File signatureFile = ApkChecksums.findSignatureForDigests(digestsFile); 3218 if (signatureFile != null) { 3219 mResolvedInheritedFiles.add(signatureFile); 3220 } 3221 } 3222 } 3223 3224 @GuardedBy("mLock") 3225 private void assertApkConsistentLocked(String tag, ApkLite apk) 3226 throws PackageManagerException { 3227 assertPackageConsistentLocked(tag, apk.getPackageName(), apk.getLongVersionCode()); 3228 if (!mSigningDetails.signaturesMatchExactly(apk.getSigningDetails())) { 3229 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 3230 tag + " signatures are inconsistent"); 3231 } 3232 } 3233 3234 @GuardedBy("mLock") 3235 private void assertPackageConsistentLocked(String tag, String packageName, 3236 long versionCode) throws PackageManagerException { 3237 if (!mPackageName.equals(packageName)) { 3238 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag + " package " 3239 + packageName + " inconsistent with " + mPackageName); 3240 } 3241 if (params.appPackageName != null && !params.appPackageName.equals(packageName)) { 3242 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag 3243 + " specified package " + params.appPackageName 3244 + " inconsistent with " + packageName); 3245 } 3246 if (mVersionCode != versionCode) { 3247 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag 3248 + " version code " + versionCode + " inconsistent with " 3249 + mVersionCode); 3250 } 3251 } 3252 3253 private SigningDetails unsafeGetCertsWithoutVerification(String path) 3254 throws PackageManagerException { 3255 final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing(); 3256 final ParseResult<SigningDetails> result = 3257 ApkSignatureVerifier.unsafeGetCertsWithoutVerification( 3258 input, path, SigningDetails.SignatureSchemeVersion.JAR); 3259 if (result.isError()) { 3260 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 3261 "Couldn't obtain signatures from APK : " + path); 3262 } 3263 return result.getResult(); 3264 } 3265 3266 /** 3267 * Determine if creating hard links between source and destination is 3268 * possible. That is, do they all live on the same underlying device. 3269 */ 3270 private static boolean isLinkPossible(List<File> fromFiles, File toDir) { 3271 try { 3272 final StructStat toStat = Os.stat(toDir.getAbsolutePath()); 3273 for (File fromFile : fromFiles) { 3274 final StructStat fromStat = Os.stat(fromFile.getAbsolutePath()); 3275 if (fromStat.st_dev != toStat.st_dev) { 3276 return false; 3277 } 3278 } 3279 } catch (ErrnoException e) { 3280 Slog.w(TAG, "Failed to detect if linking possible: " + e); 3281 return false; 3282 } 3283 return true; 3284 } 3285 3286 /** 3287 * @return the uid of the owner this session 3288 */ 3289 public int getInstallerUid() { 3290 synchronized (mLock) { 3291 return mInstallerUid; 3292 } 3293 } 3294 3295 /** 3296 * @return the package name of this session 3297 */ 3298 @VisibleForTesting(visibility = PACKAGE) 3299 public String getPackageName() { 3300 synchronized (mLock) { 3301 return mPackageName; 3302 } 3303 } 3304 3305 /** 3306 * @return the timestamp of when this session last changed state 3307 */ 3308 public long getUpdatedMillis() { 3309 synchronized (mLock) { 3310 return updatedMillis; 3311 } 3312 } 3313 3314 long getCommittedMillis() { 3315 synchronized (mLock) { 3316 return committedMillis; 3317 } 3318 } 3319 3320 String getInstallerPackageName() { 3321 return getInstallSource().installerPackageName; 3322 } 3323 3324 String getInstallerAttributionTag() { 3325 return getInstallSource().installerAttributionTag; 3326 } 3327 3328 InstallSource getInstallSource() { 3329 synchronized (mLock) { 3330 return mInstallSource; 3331 } 3332 } 3333 3334 SigningDetails getSigningDetails() { 3335 synchronized (mLock) { 3336 return mSigningDetails; 3337 } 3338 } 3339 3340 PackageLite getPackageLite() { 3341 synchronized (mLock) { 3342 return mPackageLite; 3343 } 3344 } 3345 3346 private static String getRelativePath(File file, File base) throws IOException { 3347 final String pathStr = file.getAbsolutePath(); 3348 final String baseStr = base.getAbsolutePath(); 3349 // Don't allow relative paths. 3350 if (pathStr.contains("/.") ) { 3351 throw new IOException("Invalid path (was relative) : " + pathStr); 3352 } 3353 3354 if (pathStr.startsWith(baseStr)) { 3355 return pathStr.substring(baseStr.length()); 3356 } 3357 3358 throw new IOException("File: " + pathStr + " outside base: " + baseStr); 3359 } 3360 3361 private void createOatDirs(String packageName, List<String> instructionSets, File fromDir) 3362 throws PackageManagerException { 3363 for (String instructionSet : instructionSets) { 3364 try { 3365 mInstaller.createOatDir(packageName, fromDir.getAbsolutePath(), instructionSet); 3366 } catch (InstallerException e) { 3367 throw PackageManagerException.from(e); 3368 } 3369 } 3370 } 3371 3372 private void linkFile(String packageName, String relativePath, String fromBase, String toBase) 3373 throws IOException { 3374 try { 3375 // Try 3376 final IncrementalFileStorages incrementalFileStorages = getIncrementalFileStorages(); 3377 if (incrementalFileStorages != null && incrementalFileStorages.makeLink(relativePath, 3378 fromBase, toBase)) { 3379 return; 3380 } 3381 mInstaller.linkFile(packageName, relativePath, fromBase, toBase); 3382 } catch (InstallerException | IOException e) { 3383 throw new IOException("failed linkOrCreateDir(" + relativePath + ", " 3384 + fromBase + ", " + toBase + ")", e); 3385 } 3386 } 3387 3388 private void linkFiles(String packageName, List<File> fromFiles, File toDir, File fromDir) 3389 throws IOException { 3390 for (File fromFile : fromFiles) { 3391 final String relativePath = getRelativePath(fromFile, fromDir); 3392 final String fromBase = fromDir.getAbsolutePath(); 3393 final String toBase = toDir.getAbsolutePath(); 3394 3395 linkFile(packageName, relativePath, fromBase, toBase); 3396 } 3397 3398 Slog.d(TAG, "Linked " + fromFiles.size() + " files into " + toDir); 3399 } 3400 3401 private static void copyFiles(List<File> fromFiles, File toDir) throws IOException { 3402 // Remove any partial files from previous attempt 3403 for (File file : toDir.listFiles()) { 3404 if (file.getName().endsWith(".tmp")) { 3405 file.delete(); 3406 } 3407 } 3408 3409 for (File fromFile : fromFiles) { 3410 final File tmpFile = File.createTempFile("inherit", ".tmp", toDir); 3411 if (LOGD) Slog.d(TAG, "Copying " + fromFile + " to " + tmpFile); 3412 if (!FileUtils.copyFile(fromFile, tmpFile)) { 3413 throw new IOException("Failed to copy " + fromFile + " to " + tmpFile); 3414 } 3415 try { 3416 Os.chmod(tmpFile.getAbsolutePath(), 0644); 3417 } catch (ErrnoException e) { 3418 throw new IOException("Failed to chmod " + tmpFile); 3419 } 3420 final File toFile = new File(toDir, fromFile.getName()); 3421 if (LOGD) Slog.d(TAG, "Renaming " + tmpFile + " to " + toFile); 3422 if (!tmpFile.renameTo(toFile)) { 3423 throw new IOException("Failed to rename " + tmpFile + " to " + toFile); 3424 } 3425 } 3426 Slog.d(TAG, "Copied " + fromFiles.size() + " files into " + toDir); 3427 } 3428 3429 private void extractNativeLibraries(PackageLite packageLite, File packageDir, 3430 String abiOverride, boolean inherit) 3431 throws PackageManagerException { 3432 Objects.requireNonNull(packageLite); 3433 final File libDir = new File(packageDir, NativeLibraryHelper.LIB_DIR_NAME); 3434 if (!inherit) { 3435 // Start from a clean slate 3436 NativeLibraryHelper.removeNativeBinariesFromDirLI(libDir, true); 3437 } 3438 3439 NativeLibraryHelper.Handle handle = null; 3440 try { 3441 handle = NativeLibraryHelper.Handle.create(packageLite); 3442 final int res = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libDir, 3443 abiOverride, isIncrementalInstallation()); 3444 if (res != INSTALL_SUCCEEDED) { 3445 throw new PackageManagerException(res, 3446 "Failed to extract native libraries, res=" + res); 3447 } 3448 } catch (IOException e) { 3449 throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, 3450 "Failed to extract native libraries", e); 3451 } finally { 3452 IoUtils.closeQuietly(handle); 3453 } 3454 } 3455 3456 void setPermissionsResult(boolean accepted) { 3457 if (!isSealed()) { 3458 throw new SecurityException("Must be sealed to accept permissions"); 3459 } 3460 3461 PackageInstallerSession root = hasParentSessionId() 3462 ? mSessionProvider.getSession(getParentSessionId()) : this; 3463 3464 if (accepted) { 3465 // Mark and kick off another install pass 3466 synchronized (mLock) { 3467 mPermissionsManuallyAccepted = true; 3468 } 3469 root.mHandler.obtainMessage(MSG_INSTALL).sendToTarget(); 3470 } else { 3471 root.destroy(); 3472 root.dispatchSessionFinished(INSTALL_FAILED_ABORTED, "User rejected permissions", null); 3473 root.maybeFinishChildSessions(INSTALL_FAILED_ABORTED, "User rejected permissions"); 3474 } 3475 } 3476 3477 public void open() throws IOException { 3478 if (mActiveCount.getAndIncrement() == 0) { 3479 mCallback.onSessionActiveChanged(this, true); 3480 } 3481 3482 boolean wasPrepared; 3483 synchronized (mLock) { 3484 wasPrepared = mPrepared; 3485 if (!mPrepared) { 3486 if (stageDir != null) { 3487 prepareStageDir(stageDir); 3488 } else if (params.isMultiPackage) { 3489 // it's all ok 3490 } else { 3491 throw new IllegalArgumentException("stageDir must be set"); 3492 } 3493 3494 mPrepared = true; 3495 } 3496 } 3497 3498 if (!wasPrepared) { 3499 mCallback.onSessionPrepared(this); 3500 } 3501 } 3502 3503 @Override 3504 public void close() { 3505 closeInternal(true); 3506 } 3507 3508 private void closeInternal(boolean checkCaller) { 3509 int activeCount; 3510 synchronized (mLock) { 3511 if (checkCaller) { 3512 assertCallerIsOwnerOrRoot(); 3513 } 3514 3515 activeCount = mActiveCount.decrementAndGet(); 3516 } 3517 3518 if (activeCount == 0) { 3519 mCallback.onSessionActiveChanged(this, false); 3520 } 3521 } 3522 3523 /** 3524 * Calls dispatchSessionFinished() on all child sessions with the given error code and 3525 * error message to prevent orphaned child sessions. 3526 */ 3527 private void maybeFinishChildSessions(int returnCode, String msg) { 3528 for (PackageInstallerSession child : getChildSessions()) { 3529 child.dispatchSessionFinished(returnCode, msg, null); 3530 } 3531 } 3532 3533 private void assertNotChild(String cookie) { 3534 if (hasParentSessionId()) { 3535 throw new IllegalStateException(cookie + " can't be called on a child session, id=" 3536 + sessionId + " parentId=" + getParentSessionId()); 3537 } 3538 } 3539 3540 /** 3541 * Called when verification has completed. Now it is safe to clean up the session 3542 * if {@link #abandon()} has been called previously. 3543 * 3544 * @return True if this session has been abandoned. 3545 */ 3546 private boolean dispatchPendingAbandonCallback() { 3547 final Runnable callback; 3548 synchronized (mLock) { 3549 Preconditions.checkState(mStageDirInUse); 3550 mStageDirInUse = false; 3551 callback = mPendingAbandonCallback; 3552 mPendingAbandonCallback = null; 3553 } 3554 if (callback != null) { 3555 callback.run(); 3556 return true; 3557 } 3558 return false; 3559 } 3560 3561 @Override 3562 public void abandon() { 3563 final Runnable r; 3564 synchronized (mLock) { 3565 assertNotChild("abandon"); 3566 assertCallerIsOwnerOrRootOrSystem(); 3567 if (isInTerminalState()) { 3568 // Finalized sessions have been properly cleaned up. No need to abandon them. 3569 return; 3570 } 3571 mDestroyed = true; 3572 r = () -> { 3573 assertNotLocked("abandonStaged"); 3574 if (isStaged() && mCommitted.get()) { 3575 mStagingManager.abortCommittedSession(mStagedSession); 3576 } 3577 destroy(); 3578 dispatchSessionFinished(INSTALL_FAILED_ABORTED, "Session was abandoned", null); 3579 maybeFinishChildSessions(INSTALL_FAILED_ABORTED, 3580 "Session was abandoned because the parent session is abandoned"); 3581 }; 3582 if (mStageDirInUse) { 3583 // Verification is ongoing, not safe to clean up the session yet. 3584 mPendingAbandonCallback = r; 3585 mCallback.onSessionChanged(this); 3586 return; 3587 } 3588 } 3589 r.run(); 3590 } 3591 3592 @Override 3593 public boolean isMultiPackage() { 3594 return params.isMultiPackage; 3595 } 3596 3597 @Override 3598 public boolean isStaged() { 3599 return params.isStaged; 3600 } 3601 3602 @Override 3603 public int getInstallFlags() { 3604 return params.installFlags; 3605 } 3606 3607 @Override 3608 public DataLoaderParamsParcel getDataLoaderParams() { 3609 mContext.enforceCallingOrSelfPermission(Manifest.permission.USE_INSTALLER_V2, null); 3610 return params.dataLoaderParams != null ? params.dataLoaderParams.getData() : null; 3611 } 3612 3613 @Override 3614 public void addFile(int location, String name, long lengthBytes, byte[] metadata, 3615 byte[] signature) { 3616 mContext.enforceCallingOrSelfPermission(Manifest.permission.USE_INSTALLER_V2, null); 3617 if (!isDataLoaderInstallation()) { 3618 throw new IllegalStateException( 3619 "Cannot add files to non-data loader installation session."); 3620 } 3621 if (isStreamingInstallation()) { 3622 if (location != LOCATION_DATA_APP) { 3623 throw new IllegalArgumentException( 3624 "Non-incremental installation only supports /data/app placement: " + name); 3625 } 3626 } 3627 if (metadata == null) { 3628 throw new IllegalArgumentException( 3629 "DataLoader installation requires valid metadata: " + name); 3630 } 3631 // Use installer provided name for now; we always rename later 3632 if (!FileUtils.isValidExtFilename(name)) { 3633 throw new IllegalArgumentException("Invalid name: " + name); 3634 } 3635 3636 synchronized (mLock) { 3637 assertCallerIsOwnerOrRoot(); 3638 assertPreparedAndNotSealedLocked("addFile"); 3639 3640 if (!mFiles.add(new FileEntry(mFiles.size(), 3641 new InstallationFile(location, name, lengthBytes, metadata, signature)))) { 3642 throw new IllegalArgumentException("File already added: " + name); 3643 } 3644 } 3645 } 3646 3647 @Override 3648 public void removeFile(int location, String name) { 3649 mContext.enforceCallingOrSelfPermission(Manifest.permission.USE_INSTALLER_V2, null); 3650 if (!isDataLoaderInstallation()) { 3651 throw new IllegalStateException( 3652 "Cannot add files to non-data loader installation session."); 3653 } 3654 if (TextUtils.isEmpty(params.appPackageName)) { 3655 throw new IllegalStateException("Must specify package name to remove a split"); 3656 } 3657 3658 synchronized (mLock) { 3659 assertCallerIsOwnerOrRoot(); 3660 assertPreparedAndNotSealedLocked("removeFile"); 3661 3662 if (!mFiles.add(new FileEntry(mFiles.size(), 3663 new InstallationFile(location, getRemoveMarkerName(name), -1, null, null)))) { 3664 throw new IllegalArgumentException("File already removed: " + name); 3665 } 3666 } 3667 } 3668 3669 /** 3670 * Makes sure files are present in staging location. 3671 * @return if the image is ready for installation 3672 */ 3673 @GuardedBy("mLock") 3674 private boolean prepareDataLoaderLocked() 3675 throws PackageManagerException { 3676 if (!isDataLoaderInstallation()) { 3677 return true; 3678 } 3679 if (mDataLoaderFinished) { 3680 return true; 3681 } 3682 3683 final List<InstallationFileParcel> addedFiles = new ArrayList<>(); 3684 final List<String> removedFiles = new ArrayList<>(); 3685 3686 final InstallationFile[] files = getInstallationFilesLocked(); 3687 for (InstallationFile file : files) { 3688 if (sAddedFilter.accept(new File(this.stageDir, file.getName()))) { 3689 addedFiles.add(file.getData()); 3690 continue; 3691 } 3692 if (sRemovedFilter.accept(new File(this.stageDir, file.getName()))) { 3693 String name = file.getName().substring( 3694 0, file.getName().length() - REMOVE_MARKER_EXTENSION.length()); 3695 removedFiles.add(name); 3696 } 3697 } 3698 3699 final DataLoaderParams params = this.params.dataLoaderParams; 3700 final boolean manualStartAndDestroy = !isIncrementalInstallation(); 3701 final boolean systemDataLoader = isSystemDataLoaderInstallation(); 3702 final IDataLoaderStatusListener statusListener = new IDataLoaderStatusListener.Stub() { 3703 @Override 3704 public void onStatusChanged(int dataLoaderId, int status) { 3705 switch (status) { 3706 case IDataLoaderStatusListener.DATA_LOADER_BINDING: 3707 case IDataLoaderStatusListener.DATA_LOADER_STOPPED: 3708 case IDataLoaderStatusListener.DATA_LOADER_DESTROYED: 3709 return; 3710 } 3711 3712 if (mDestroyed || mDataLoaderFinished) { 3713 switch (status) { 3714 case IDataLoaderStatusListener.DATA_LOADER_UNRECOVERABLE: 3715 if (systemDataLoader) { 3716 onSystemDataLoaderUnrecoverable(); 3717 } 3718 return; 3719 } 3720 return; 3721 } 3722 try { 3723 switch (status) { 3724 case IDataLoaderStatusListener.DATA_LOADER_BOUND: { 3725 if (manualStartAndDestroy) { 3726 FileSystemControlParcel control = new FileSystemControlParcel(); 3727 control.callback = new FileSystemConnector(addedFiles); 3728 getDataLoader(dataLoaderId).create(dataLoaderId, params.getData(), 3729 control, this); 3730 } 3731 3732 break; 3733 } 3734 case IDataLoaderStatusListener.DATA_LOADER_CREATED: { 3735 if (manualStartAndDestroy) { 3736 // IncrementalFileStorages will call start after all files are 3737 // created in IncFS. 3738 getDataLoader(dataLoaderId).start(dataLoaderId); 3739 } 3740 break; 3741 } 3742 case IDataLoaderStatusListener.DATA_LOADER_STARTED: { 3743 getDataLoader(dataLoaderId).prepareImage( 3744 dataLoaderId, 3745 addedFiles.toArray( 3746 new InstallationFileParcel[addedFiles.size()]), 3747 removedFiles.toArray(new String[removedFiles.size()])); 3748 break; 3749 } 3750 case IDataLoaderStatusListener.DATA_LOADER_IMAGE_READY: { 3751 mDataLoaderFinished = true; 3752 if (hasParentSessionId()) { 3753 mSessionProvider.getSession( 3754 getParentSessionId()).dispatchSessionSealed(); 3755 } else { 3756 dispatchSessionSealed(); 3757 } 3758 if (manualStartAndDestroy) { 3759 getDataLoader(dataLoaderId).destroy(dataLoaderId); 3760 } 3761 break; 3762 } 3763 case IDataLoaderStatusListener.DATA_LOADER_IMAGE_NOT_READY: { 3764 mDataLoaderFinished = true; 3765 dispatchSessionValidationFailure(INSTALL_FAILED_MEDIA_UNAVAILABLE, 3766 "Failed to prepare image."); 3767 if (manualStartAndDestroy) { 3768 getDataLoader(dataLoaderId).destroy(dataLoaderId); 3769 } 3770 break; 3771 } 3772 case IDataLoaderStatusListener.DATA_LOADER_UNAVAILABLE: { 3773 // Don't fail or commit the session. Allow caller to commit again. 3774 sendPendingStreaming(mContext, getRemoteStatusReceiver(), sessionId, 3775 "DataLoader unavailable"); 3776 break; 3777 } 3778 case IDataLoaderStatusListener.DATA_LOADER_UNRECOVERABLE: 3779 throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE, 3780 "DataLoader reported unrecoverable failure."); 3781 } 3782 } catch (PackageManagerException e) { 3783 mDataLoaderFinished = true; 3784 dispatchSessionValidationFailure(e.error, ExceptionUtils.getCompleteMessage(e)); 3785 } catch (RemoteException e) { 3786 // In case of streaming failure we don't want to fail or commit the session. 3787 // Just return from this method and allow caller to commit again. 3788 sendPendingStreaming(mContext, getRemoteStatusReceiver(), sessionId, 3789 e.getMessage()); 3790 } 3791 } 3792 }; 3793 3794 if (!manualStartAndDestroy) { 3795 final PerUidReadTimeouts[] perUidReadTimeouts = 3796 mPm.getPerUidReadTimeouts(mPm.snapshotComputer()); 3797 3798 final StorageHealthCheckParams healthCheckParams = new StorageHealthCheckParams(); 3799 healthCheckParams.blockedTimeoutMs = INCREMENTAL_STORAGE_BLOCKED_TIMEOUT_MS; 3800 healthCheckParams.unhealthyTimeoutMs = INCREMENTAL_STORAGE_UNHEALTHY_TIMEOUT_MS; 3801 healthCheckParams.unhealthyMonitoringMs = INCREMENTAL_STORAGE_UNHEALTHY_MONITORING_MS; 3802 3803 final IStorageHealthListener healthListener = new IStorageHealthListener.Stub() { 3804 @Override 3805 public void onHealthStatus(int storageId, int status) { 3806 if (mDestroyed || mDataLoaderFinished) { 3807 return; 3808 } 3809 3810 switch (status) { 3811 case IStorageHealthListener.HEALTH_STATUS_OK: 3812 break; 3813 case IStorageHealthListener.HEALTH_STATUS_READS_PENDING: 3814 case IStorageHealthListener.HEALTH_STATUS_BLOCKED: 3815 if (systemDataLoader) { 3816 // It's OK for ADB data loader to wait for pages. 3817 break; 3818 } 3819 // fallthrough 3820 case IStorageHealthListener.HEALTH_STATUS_UNHEALTHY: 3821 // Even ADB installation can't wait for missing pages for too long. 3822 mDataLoaderFinished = true; 3823 dispatchSessionValidationFailure(INSTALL_FAILED_MEDIA_UNAVAILABLE, 3824 "Image is missing pages required for installation."); 3825 break; 3826 } 3827 } 3828 }; 3829 3830 try { 3831 final PackageInfo pkgInfo = mPm.snapshotComputer() 3832 .getPackageInfo(this.params.appPackageName, 0, userId); 3833 final File inheritedDir = 3834 (pkgInfo != null && pkgInfo.applicationInfo != null) ? new File( 3835 pkgInfo.applicationInfo.getCodePath()).getParentFile() : null; 3836 3837 if (mIncrementalFileStorages == null) { 3838 mIncrementalFileStorages = IncrementalFileStorages.initialize(mContext, 3839 stageDir, inheritedDir, params, statusListener, healthCheckParams, 3840 healthListener, addedFiles, perUidReadTimeouts, 3841 new IPackageLoadingProgressCallback.Stub() { 3842 @Override 3843 public void onPackageLoadingProgressChanged(float progress) { 3844 synchronized (mProgressLock) { 3845 mIncrementalProgress = progress; 3846 computeProgressLocked(true); 3847 } 3848 } 3849 }); 3850 } else { 3851 // Retrying commit. 3852 mIncrementalFileStorages.startLoading(params, statusListener, healthCheckParams, 3853 healthListener, perUidReadTimeouts); 3854 } 3855 return false; 3856 } catch (IOException e) { 3857 throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE, e.getMessage(), 3858 e.getCause()); 3859 } 3860 } 3861 3862 final long bindDelayMs = 0; 3863 if (!getDataLoaderManager().bindToDataLoader(sessionId, params.getData(), bindDelayMs, 3864 statusListener)) { 3865 throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE, 3866 "Failed to initialize data loader"); 3867 } 3868 3869 return false; 3870 } 3871 3872 private DataLoaderManager getDataLoaderManager() throws PackageManagerException { 3873 DataLoaderManager dataLoaderManager = mContext.getSystemService(DataLoaderManager.class); 3874 if (dataLoaderManager == null) { 3875 throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE, 3876 "Failed to find data loader manager service"); 3877 } 3878 return dataLoaderManager; 3879 } 3880 3881 private IDataLoader getDataLoader(int dataLoaderId) throws PackageManagerException { 3882 IDataLoader dataLoader = getDataLoaderManager().getDataLoader(dataLoaderId); 3883 if (dataLoader == null) { 3884 throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE, 3885 "Failure to obtain data loader"); 3886 } 3887 return dataLoader; 3888 } 3889 3890 private void dispatchSessionValidationFailure(int error, String detailMessage) { 3891 mHandler.obtainMessage(MSG_SESSION_VALIDATION_FAILURE, error, -1, 3892 detailMessage).sendToTarget(); 3893 } 3894 3895 @GuardedBy("mLock") 3896 private int[] getChildSessionIdsLocked() { 3897 int size = mChildSessions.size(); 3898 if (size == 0) { 3899 return EMPTY_CHILD_SESSION_ARRAY; 3900 } 3901 final int[] childSessionIds = new int[size]; 3902 for (int i = 0; i < size; ++i) { 3903 childSessionIds[i] = mChildSessions.keyAt(i); 3904 } 3905 return childSessionIds; 3906 } 3907 3908 @Override 3909 public int[] getChildSessionIds() { 3910 synchronized (mLock) { 3911 return getChildSessionIdsLocked(); 3912 } 3913 } 3914 3915 private boolean canBeAddedAsChild(int parentCandidate) { 3916 synchronized (mLock) { 3917 return (!hasParentSessionId() || mParentSessionId == parentCandidate) 3918 && !mCommitted.get() 3919 && !mDestroyed; 3920 } 3921 } 3922 3923 private void acquireTransactionLock() { 3924 if (!mTransactionLock.compareAndSet(false, true)) { 3925 throw new UnsupportedOperationException("Concurrent access not supported"); 3926 } 3927 } 3928 3929 private void releaseTransactionLock() { 3930 mTransactionLock.compareAndSet(true, false); 3931 } 3932 3933 @Override 3934 public void addChildSessionId(int childSessionId) { 3935 if (!params.isMultiPackage) { 3936 throw new IllegalStateException("Single-session " + sessionId + " can't have child."); 3937 } 3938 3939 final PackageInstallerSession childSession = mSessionProvider.getSession(childSessionId); 3940 if (childSession == null) { 3941 throw new IllegalStateException("Unable to add child session " + childSessionId 3942 + " as it does not exist."); 3943 } 3944 if (childSession.params.isMultiPackage) { 3945 throw new IllegalStateException("Multi-session " + childSessionId 3946 + " can't be a child."); 3947 } 3948 if (params.isStaged != childSession.params.isStaged) { 3949 throw new IllegalStateException("Multipackage Inconsistency: session " 3950 + childSession.sessionId + " and session " + sessionId 3951 + " have inconsistent staged settings"); 3952 } 3953 if (params.getEnableRollback() != childSession.params.getEnableRollback()) { 3954 throw new IllegalStateException("Multipackage Inconsistency: session " 3955 + childSession.sessionId + " and session " + sessionId 3956 + " have inconsistent rollback settings"); 3957 } 3958 boolean hasAPK = containsApkSession() || !childSession.isApexSession(); 3959 boolean hasAPEX = sessionContains(s -> s.isApexSession()) || childSession.isApexSession(); 3960 if (!params.isStaged && hasAPK && hasAPEX) { 3961 throw new IllegalStateException("Mix of APK and APEX is not supported for " 3962 + "non-staged multi-package session"); 3963 } 3964 3965 try { 3966 acquireTransactionLock(); 3967 childSession.acquireTransactionLock(); 3968 3969 if (!childSession.canBeAddedAsChild(sessionId)) { 3970 throw new IllegalStateException("Unable to add child session " + childSessionId 3971 + " as it is in an invalid state."); 3972 } 3973 synchronized (mLock) { 3974 assertCallerIsOwnerOrRoot(); 3975 assertPreparedAndNotSealedLocked("addChildSessionId"); 3976 3977 final int indexOfSession = mChildSessions.indexOfKey(childSessionId); 3978 if (indexOfSession >= 0) { 3979 return; 3980 } 3981 childSession.setParentSessionId(this.sessionId); 3982 mChildSessions.put(childSessionId, childSession); 3983 } 3984 } finally { 3985 releaseTransactionLock(); 3986 childSession.releaseTransactionLock(); 3987 } 3988 } 3989 3990 @Override 3991 public void removeChildSessionId(int sessionId) { 3992 synchronized (mLock) { 3993 assertCallerIsOwnerOrRoot(); 3994 assertPreparedAndNotSealedLocked("removeChildSessionId"); 3995 3996 final int indexOfSession = mChildSessions.indexOfKey(sessionId); 3997 if (indexOfSession < 0) { 3998 // not added in the first place; no-op 3999 return; 4000 } 4001 PackageInstallerSession session = mChildSessions.valueAt(indexOfSession); 4002 try { 4003 acquireTransactionLock(); 4004 session.acquireTransactionLock(); 4005 session.setParentSessionId(SessionInfo.INVALID_ID); 4006 mChildSessions.removeAt(indexOfSession); 4007 } finally { 4008 releaseTransactionLock(); 4009 session.releaseTransactionLock(); 4010 } 4011 } 4012 } 4013 4014 /** 4015 * Sets the parent session ID if not already set. 4016 * If {@link SessionInfo#INVALID_ID} is passed, it will be unset. 4017 */ 4018 void setParentSessionId(int parentSessionId) { 4019 synchronized (mLock) { 4020 if (parentSessionId != SessionInfo.INVALID_ID 4021 && mParentSessionId != SessionInfo.INVALID_ID) { 4022 throw new IllegalStateException("The parent of " + sessionId + " is" + " already" 4023 + "set to " + mParentSessionId); 4024 } 4025 this.mParentSessionId = parentSessionId; 4026 } 4027 } 4028 4029 boolean hasParentSessionId() { 4030 synchronized (mLock) { 4031 return mParentSessionId != SessionInfo.INVALID_ID; 4032 } 4033 } 4034 4035 @Override 4036 public int getParentSessionId() { 4037 synchronized (mLock) { 4038 return mParentSessionId; 4039 } 4040 } 4041 4042 private void dispatchSessionFinished(int returnCode, String msg, Bundle extras) { 4043 sendUpdateToRemoteStatusReceiver(returnCode, msg, extras); 4044 4045 synchronized (mLock) { 4046 mFinalStatus = returnCode; 4047 mFinalMessage = msg; 4048 } 4049 4050 final boolean success = (returnCode == INSTALL_SUCCEEDED); 4051 4052 // Send broadcast to default launcher only if it's a new install 4053 // TODO(b/144270665): Secure the usage of this broadcast. 4054 final boolean isNewInstall = extras == null || !extras.getBoolean(Intent.EXTRA_REPLACING); 4055 if (success && isNewInstall && mPm.mInstallerService.okToSendBroadcasts()) { 4056 mPm.sendSessionCommitBroadcast(generateInfoScrubbed(true /*icon*/), userId); 4057 } 4058 4059 mCallback.onSessionFinished(this, success); 4060 if (isDataLoaderInstallation()) { 4061 logDataLoaderInstallationSession(returnCode); 4062 } 4063 } 4064 4065 private void sendUpdateToRemoteStatusReceiver(int returnCode, String msg, Bundle extras) { 4066 final IntentSender statusReceiver = getRemoteStatusReceiver(); 4067 if (statusReceiver != null) { 4068 // Execute observer.onPackageInstalled on different thread as we don't want callers 4069 // inside the system server have to worry about catching the callbacks while they are 4070 // calling into the session 4071 final SomeArgs args = SomeArgs.obtain(); 4072 args.arg1 = getPackageName(); 4073 args.arg2 = msg; 4074 args.arg3 = extras; 4075 args.arg4 = statusReceiver; 4076 args.argi1 = returnCode; 4077 mHandler.obtainMessage(MSG_ON_PACKAGE_INSTALLED, args).sendToTarget(); 4078 } 4079 } 4080 4081 void setSessionReady() { 4082 synchronized (mLock) { 4083 // Do not allow destroyed/failed session to change state 4084 if (mDestroyed || mSessionFailed) return; 4085 mSessionReady = true; 4086 mSessionApplied = false; 4087 mSessionFailed = false; 4088 mSessionErrorCode = PackageManager.INSTALL_UNKNOWN; 4089 mSessionErrorMessage = ""; 4090 } 4091 mCallback.onSessionChanged(this); 4092 } 4093 4094 void setSessionFailed(int errorCode, String errorMessage) { 4095 synchronized (mLock) { 4096 // Do not allow destroyed/failed session to change state 4097 if (mDestroyed || mSessionFailed) return; 4098 mSessionReady = false; 4099 mSessionApplied = false; 4100 mSessionFailed = true; 4101 mSessionErrorCode = errorCode; 4102 mSessionErrorMessage = errorMessage; 4103 Slog.d(TAG, "Marking session " + sessionId + " as failed: " + errorMessage); 4104 } 4105 destroy(); 4106 mCallback.onSessionChanged(this); 4107 } 4108 4109 private void setSessionApplied() { 4110 synchronized (mLock) { 4111 // Do not allow destroyed/failed session to change state 4112 if (mDestroyed || mSessionFailed) return; 4113 mSessionReady = false; 4114 mSessionApplied = true; 4115 mSessionFailed = false; 4116 mSessionErrorCode = INSTALL_SUCCEEDED; 4117 mSessionErrorMessage = ""; 4118 Slog.d(TAG, "Marking session " + sessionId + " as applied"); 4119 } 4120 destroy(); 4121 mCallback.onSessionChanged(this); 4122 } 4123 4124 /** {@hide} */ 4125 boolean isSessionReady() { 4126 synchronized (mLock) { 4127 return mSessionReady; 4128 } 4129 } 4130 4131 /** {@hide} */ 4132 boolean isSessionApplied() { 4133 synchronized (mLock) { 4134 return mSessionApplied; 4135 } 4136 } 4137 4138 /** {@hide} */ 4139 boolean isSessionFailed() { 4140 synchronized (mLock) { 4141 return mSessionFailed; 4142 } 4143 } 4144 4145 /** {@hide} */ 4146 int getSessionErrorCode() { 4147 synchronized (mLock) { 4148 return mSessionErrorCode; 4149 } 4150 } 4151 4152 /** {@hide} */ 4153 String getSessionErrorMessage() { 4154 synchronized (mLock) { 4155 return mSessionErrorMessage; 4156 } 4157 } 4158 4159 /** 4160 * Free up storage used by this session and its children. 4161 * Must not be called on a child session. 4162 */ 4163 private void destroy() { 4164 // TODO(b/173194203): destroy() is called indirectly by 4165 // PackageInstallerService#restoreAndApplyStagedSessionIfNeeded on an orphan child session. 4166 // Enable this assertion when we figure out a better way to clean up orphan sessions. 4167 // assertNotChild("destroy"); 4168 4169 // TODO(b/173194203): destroyInternal() should be used by destroy() only. 4170 // For the sake of consistency, a session should be destroyed as a whole. The caller 4171 // should always call destroy() for cleanup without knowing it has child sessions or not. 4172 destroyInternal(); 4173 for (PackageInstallerSession child : getChildSessions()) { 4174 child.destroyInternal(); 4175 } 4176 } 4177 4178 /** 4179 * Free up storage used by this session. 4180 */ 4181 private void destroyInternal() { 4182 final IncrementalFileStorages incrementalFileStorages; 4183 synchronized (mLock) { 4184 mSealed = true; 4185 if (!params.isStaged) { 4186 mDestroyed = true; 4187 } 4188 // Force shut down all bridges 4189 for (RevocableFileDescriptor fd : mFds) { 4190 fd.revoke(); 4191 } 4192 for (FileBridge bridge : mBridges) { 4193 bridge.forceClose(); 4194 } 4195 incrementalFileStorages = mIncrementalFileStorages; 4196 mIncrementalFileStorages = null; 4197 } 4198 try { 4199 if (incrementalFileStorages != null) { 4200 incrementalFileStorages.cleanUpAndMarkComplete(); 4201 } 4202 if (stageDir != null) { 4203 final String tempPackageName = stageDir.getName(); 4204 mInstaller.rmPackageDir(tempPackageName, stageDir.getAbsolutePath()); 4205 } 4206 } catch (InstallerException ignored) { 4207 } 4208 } 4209 4210 void dump(IndentingPrintWriter pw) { 4211 synchronized (mLock) { 4212 dumpLocked(pw); 4213 } 4214 } 4215 4216 @GuardedBy("mLock") 4217 private void dumpLocked(IndentingPrintWriter pw) { 4218 pw.println("Session " + sessionId + ":"); 4219 pw.increaseIndent(); 4220 4221 pw.printPair("userId", userId); 4222 pw.printPair("mOriginalInstallerUid", mOriginalInstallerUid); 4223 pw.printPair("mOriginalInstallerPackageName", mOriginalInstallerPackageName); 4224 pw.printPair("installerPackageName", mInstallSource.installerPackageName); 4225 pw.printPair("installInitiatingPackageName", mInstallSource.initiatingPackageName); 4226 pw.printPair("installOriginatingPackageName", mInstallSource.originatingPackageName); 4227 pw.printPair("mInstallerUid", mInstallerUid); 4228 pw.printPair("createdMillis", createdMillis); 4229 pw.printPair("updatedMillis", updatedMillis); 4230 pw.printPair("committedMillis", committedMillis); 4231 pw.printPair("stageDir", stageDir); 4232 pw.printPair("stageCid", stageCid); 4233 pw.println(); 4234 4235 params.dump(pw); 4236 4237 final float clientProgress; 4238 final float progress; 4239 synchronized (mProgressLock) { 4240 clientProgress = mClientProgress; 4241 progress = mProgress; 4242 } 4243 pw.printPair("mClientProgress", clientProgress); 4244 pw.printPair("mProgress", progress); 4245 pw.printPair("mCommitted", mCommitted); 4246 pw.printPair("mSealed", mSealed); 4247 pw.printPair("mPermissionsManuallyAccepted", mPermissionsManuallyAccepted); 4248 pw.printPair("mStageDirInUse", mStageDirInUse); 4249 pw.printPair("mDestroyed", mDestroyed); 4250 pw.printPair("mFds", mFds.size()); 4251 pw.printPair("mBridges", mBridges.size()); 4252 pw.printPair("mFinalStatus", mFinalStatus); 4253 pw.printPair("mFinalMessage", mFinalMessage); 4254 pw.printPair("params.isMultiPackage", params.isMultiPackage); 4255 pw.printPair("params.isStaged", params.isStaged); 4256 pw.printPair("mParentSessionId", mParentSessionId); 4257 pw.printPair("mChildSessionIds", getChildSessionIdsLocked()); 4258 pw.printPair("mSessionApplied", mSessionApplied); 4259 pw.printPair("mSessionFailed", mSessionFailed); 4260 pw.printPair("mSessionReady", mSessionReady); 4261 pw.printPair("mSessionErrorCode", mSessionErrorCode); 4262 pw.printPair("mSessionErrorMessage", mSessionErrorMessage); 4263 pw.println(); 4264 4265 pw.decreaseIndent(); 4266 } 4267 4268 /** 4269 * This method doesn't change internal states and is safe to call outside the lock. 4270 */ 4271 private static void sendOnUserActionRequired(Context context, IntentSender target, 4272 int sessionId, Intent intent) { 4273 final Intent fillIn = new Intent(); 4274 fillIn.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId); 4275 fillIn.putExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_PENDING_USER_ACTION); 4276 fillIn.putExtra(Intent.EXTRA_INTENT, intent); 4277 try { 4278 final BroadcastOptions options = BroadcastOptions.makeBasic(); 4279 options.setPendingIntentBackgroundActivityLaunchAllowed(false); 4280 target.sendIntent(context, 0, fillIn, null /* onFinished */, 4281 null /* handler */, null /* requiredPermission */, options.toBundle()); 4282 } catch (IntentSender.SendIntentException ignored) { 4283 } 4284 } 4285 4286 /** 4287 * This method doesn't change internal states and is safe to call outside the lock. 4288 */ 4289 private static void sendOnPackageInstalled(Context context, IntentSender target, int sessionId, 4290 boolean showNotification, int userId, String basePackageName, int returnCode, 4291 String msg, Bundle extras) { 4292 if (INSTALL_SUCCEEDED == returnCode && showNotification) { 4293 boolean update = (extras != null) && extras.getBoolean(Intent.EXTRA_REPLACING); 4294 Notification notification = PackageInstallerService.buildSuccessNotification(context, 4295 getDeviceOwnerInstalledPackageMsg(context, update), 4296 basePackageName, 4297 userId); 4298 if (notification != null) { 4299 NotificationManager notificationManager = (NotificationManager) 4300 context.getSystemService(Context.NOTIFICATION_SERVICE); 4301 notificationManager.notify(basePackageName, 4302 SystemMessageProto.SystemMessage.NOTE_PACKAGE_STATE, 4303 notification); 4304 } 4305 } 4306 final Intent fillIn = new Intent(); 4307 fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, basePackageName); 4308 fillIn.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId); 4309 fillIn.putExtra(PackageInstaller.EXTRA_STATUS, 4310 PackageManager.installStatusToPublicStatus(returnCode)); 4311 fillIn.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE, 4312 PackageManager.installStatusToString(returnCode, msg)); 4313 fillIn.putExtra(PackageInstaller.EXTRA_LEGACY_STATUS, returnCode); 4314 if (extras != null) { 4315 final String existing = extras.getString( 4316 PackageManager.EXTRA_FAILURE_EXISTING_PACKAGE); 4317 if (!TextUtils.isEmpty(existing)) { 4318 fillIn.putExtra(PackageInstaller.EXTRA_OTHER_PACKAGE_NAME, existing); 4319 } 4320 } 4321 try { 4322 final BroadcastOptions options = BroadcastOptions.makeBasic(); 4323 options.setPendingIntentBackgroundActivityLaunchAllowed(false); 4324 target.sendIntent(context, 0, fillIn, null /* onFinished */, 4325 null /* handler */, null /* requiredPermission */, options.toBundle()); 4326 } catch (IntentSender.SendIntentException ignored) { 4327 } 4328 } 4329 4330 private static String getDeviceOwnerInstalledPackageMsg(Context context, boolean update) { 4331 DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class); 4332 return update 4333 ? dpm.getResources().getString(PACKAGE_UPDATED_BY_DO, 4334 () -> context.getString(R.string.package_updated_device_owner)) 4335 : dpm.getResources().getString(PACKAGE_INSTALLED_BY_DO, 4336 () -> context.getString(R.string.package_installed_device_owner)); 4337 } 4338 4339 /** 4340 * This method doesn't change internal states and is safe to call outside the lock. 4341 */ 4342 private static void sendPendingStreaming(Context context, IntentSender target, int sessionId, 4343 @Nullable String cause) { 4344 if (target == null) { 4345 Slog.e(TAG, "Missing receiver for pending streaming status."); 4346 return; 4347 } 4348 4349 final Intent intent = new Intent(); 4350 intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId); 4351 intent.putExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_PENDING_STREAMING); 4352 if (!TextUtils.isEmpty(cause)) { 4353 intent.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE, 4354 "Staging Image Not Ready [" + cause + "]"); 4355 } else { 4356 intent.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE, "Staging Image Not Ready"); 4357 } 4358 try { 4359 final BroadcastOptions options = BroadcastOptions.makeBasic(); 4360 options.setPendingIntentBackgroundActivityLaunchAllowed(false); 4361 target.sendIntent(context, 0, intent, null /* onFinished */, 4362 null /* handler */, null /* requiredPermission */, options.toBundle()); 4363 } catch (IntentSender.SendIntentException ignored) { 4364 } 4365 } 4366 4367 private static void writeGrantedRuntimePermissionsLocked(TypedXmlSerializer out, 4368 String[] grantedRuntimePermissions) throws IOException { 4369 if (grantedRuntimePermissions != null) { 4370 for (String permission : grantedRuntimePermissions) { 4371 out.startTag(null, TAG_GRANTED_RUNTIME_PERMISSION); 4372 writeStringAttribute(out, ATTR_NAME, permission); 4373 out.endTag(null, TAG_GRANTED_RUNTIME_PERMISSION); 4374 } 4375 } 4376 } 4377 4378 private static void writeWhitelistedRestrictedPermissionsLocked(@NonNull TypedXmlSerializer out, 4379 @Nullable List<String> whitelistedRestrictedPermissions) throws IOException { 4380 if (whitelistedRestrictedPermissions != null) { 4381 final int permissionCount = whitelistedRestrictedPermissions.size(); 4382 for (int i = 0; i < permissionCount; i++) { 4383 out.startTag(null, TAG_WHITELISTED_RESTRICTED_PERMISSION); 4384 writeStringAttribute(out, ATTR_NAME, whitelistedRestrictedPermissions.get(i)); 4385 out.endTag(null, TAG_WHITELISTED_RESTRICTED_PERMISSION); 4386 } 4387 } 4388 } 4389 4390 private static void writeAutoRevokePermissionsMode(@NonNull TypedXmlSerializer out, int mode) 4391 throws IOException { 4392 out.startTag(null, TAG_AUTO_REVOKE_PERMISSIONS_MODE); 4393 out.attributeInt(null, ATTR_MODE, mode); 4394 out.endTag(null, TAG_AUTO_REVOKE_PERMISSIONS_MODE); 4395 } 4396 4397 4398 private static File buildAppIconFile(int sessionId, @NonNull File sessionsDir) { 4399 return new File(sessionsDir, "app_icon." + sessionId + ".png"); 4400 } 4401 4402 /** 4403 * Write this session to a {@link TypedXmlSerializer}. 4404 * 4405 * @param out Where to write the session to 4406 * @param sessionsDir The directory containing the sessions 4407 */ 4408 void write(@NonNull TypedXmlSerializer out, @NonNull File sessionsDir) throws IOException { 4409 synchronized (mLock) { 4410 if (mDestroyed && !params.isStaged) { 4411 return; 4412 } 4413 4414 out.startTag(null, TAG_SESSION); 4415 4416 out.attributeInt(null, ATTR_SESSION_ID, sessionId); 4417 out.attributeInt(null, ATTR_USER_ID, userId); 4418 writeStringAttribute(out, ATTR_INSTALLER_PACKAGE_NAME, 4419 mInstallSource.installerPackageName); 4420 writeStringAttribute(out, ATTR_INSTALLER_ATTRIBUTION_TAG, 4421 mInstallSource.installerAttributionTag); 4422 out.attributeInt(null, ATTR_INSTALLER_UID, mInstallerUid); 4423 writeStringAttribute(out, ATTR_INITIATING_PACKAGE_NAME, 4424 mInstallSource.initiatingPackageName); 4425 writeStringAttribute(out, ATTR_ORIGINATING_PACKAGE_NAME, 4426 mInstallSource.originatingPackageName); 4427 out.attributeLong(null, ATTR_CREATED_MILLIS, createdMillis); 4428 out.attributeLong(null, ATTR_UPDATED_MILLIS, updatedMillis); 4429 out.attributeLong(null, ATTR_COMMITTED_MILLIS, committedMillis); 4430 if (stageDir != null) { 4431 writeStringAttribute(out, ATTR_SESSION_STAGE_DIR, 4432 stageDir.getAbsolutePath()); 4433 } 4434 if (stageCid != null) { 4435 writeStringAttribute(out, ATTR_SESSION_STAGE_CID, stageCid); 4436 } 4437 writeBooleanAttribute(out, ATTR_PREPARED, mPrepared); 4438 writeBooleanAttribute(out, ATTR_COMMITTED, mCommitted.get()); 4439 writeBooleanAttribute(out, ATTR_DESTROYED, mDestroyed); 4440 writeBooleanAttribute(out, ATTR_SEALED, mSealed); 4441 4442 writeBooleanAttribute(out, ATTR_MULTI_PACKAGE, params.isMultiPackage); 4443 writeBooleanAttribute(out, ATTR_STAGED_SESSION, params.isStaged); 4444 writeBooleanAttribute(out, ATTR_IS_READY, mSessionReady); 4445 writeBooleanAttribute(out, ATTR_IS_FAILED, mSessionFailed); 4446 writeBooleanAttribute(out, ATTR_IS_APPLIED, mSessionApplied); 4447 out.attributeInt(null, ATTR_PACKAGE_SOURCE, params.packageSource); 4448 out.attributeInt(null, ATTR_SESSION_ERROR_CODE, mSessionErrorCode); 4449 writeStringAttribute(out, ATTR_SESSION_ERROR_MESSAGE, mSessionErrorMessage); 4450 // TODO(patb,109941548): avoid writing to xml and instead infer / validate this after 4451 // we've read all sessions. 4452 out.attributeInt(null, ATTR_PARENT_SESSION_ID, mParentSessionId); 4453 out.attributeInt(null, ATTR_MODE, params.mode); 4454 out.attributeInt(null, ATTR_INSTALL_FLAGS, params.installFlags); 4455 out.attributeInt(null, ATTR_INSTALL_LOCATION, params.installLocation); 4456 out.attributeLong(null, ATTR_SIZE_BYTES, params.sizeBytes); 4457 writeStringAttribute(out, ATTR_APP_PACKAGE_NAME, params.appPackageName); 4458 writeStringAttribute(out, ATTR_APP_LABEL, params.appLabel); 4459 writeUriAttribute(out, ATTR_ORIGINATING_URI, params.originatingUri); 4460 out.attributeInt(null, ATTR_ORIGINATING_UID, params.originatingUid); 4461 writeUriAttribute(out, ATTR_REFERRER_URI, params.referrerUri); 4462 writeStringAttribute(out, ATTR_ABI_OVERRIDE, params.abiOverride); 4463 writeStringAttribute(out, ATTR_VOLUME_UUID, params.volumeUuid); 4464 out.attributeInt(null, ATTR_INSTALL_REASON, params.installReason); 4465 4466 final boolean isDataLoader = params.dataLoaderParams != null; 4467 writeBooleanAttribute(out, ATTR_IS_DATALOADER, isDataLoader); 4468 if (isDataLoader) { 4469 out.attributeInt(null, ATTR_DATALOADER_TYPE, params.dataLoaderParams.getType()); 4470 writeStringAttribute(out, ATTR_DATALOADER_PACKAGE_NAME, 4471 params.dataLoaderParams.getComponentName().getPackageName()); 4472 writeStringAttribute(out, ATTR_DATALOADER_CLASS_NAME, 4473 params.dataLoaderParams.getComponentName().getClassName()); 4474 writeStringAttribute(out, ATTR_DATALOADER_ARGUMENTS, 4475 params.dataLoaderParams.getArguments()); 4476 } 4477 4478 writeGrantedRuntimePermissionsLocked(out, params.grantedRuntimePermissions); 4479 writeWhitelistedRestrictedPermissionsLocked(out, 4480 params.whitelistedRestrictedPermissions); 4481 writeAutoRevokePermissionsMode(out, params.autoRevokePermissionsMode); 4482 4483 // Persist app icon if changed since last written 4484 File appIconFile = buildAppIconFile(sessionId, sessionsDir); 4485 if (params.appIcon == null && appIconFile.exists()) { 4486 appIconFile.delete(); 4487 } else if (params.appIcon != null 4488 && appIconFile.lastModified() != params.appIconLastModified) { 4489 if (LOGD) Slog.w(TAG, "Writing changed icon " + appIconFile); 4490 FileOutputStream os = null; 4491 try { 4492 os = new FileOutputStream(appIconFile); 4493 params.appIcon.compress(Bitmap.CompressFormat.PNG, 90, os); 4494 } catch (IOException e) { 4495 Slog.w(TAG, "Failed to write icon " + appIconFile + ": " + e.getMessage()); 4496 } finally { 4497 IoUtils.closeQuietly(os); 4498 } 4499 4500 params.appIconLastModified = appIconFile.lastModified(); 4501 } 4502 final int[] childSessionIds = getChildSessionIdsLocked(); 4503 for (int childSessionId : childSessionIds) { 4504 out.startTag(null, TAG_CHILD_SESSION); 4505 out.attributeInt(null, ATTR_SESSION_ID, childSessionId); 4506 out.endTag(null, TAG_CHILD_SESSION); 4507 } 4508 4509 final InstallationFile[] files = getInstallationFilesLocked(); 4510 for (InstallationFile file : files) { 4511 out.startTag(null, TAG_SESSION_FILE); 4512 out.attributeInt(null, ATTR_LOCATION, file.getLocation()); 4513 writeStringAttribute(out, ATTR_NAME, file.getName()); 4514 out.attributeLong(null, ATTR_LENGTH_BYTES, file.getLengthBytes()); 4515 writeByteArrayAttribute(out, ATTR_METADATA, file.getMetadata()); 4516 writeByteArrayAttribute(out, ATTR_SIGNATURE, file.getSignature()); 4517 out.endTag(null, TAG_SESSION_FILE); 4518 } 4519 4520 for (int i = 0, isize = mChecksums.size(); i < isize; ++i) { 4521 final String fileName = mChecksums.keyAt(i); 4522 final PerFileChecksum perFileChecksum = mChecksums.valueAt(i); 4523 final Checksum[] checksums = perFileChecksum.getChecksums(); 4524 for (Checksum checksum : checksums) { 4525 out.startTag(null, TAG_SESSION_CHECKSUM); 4526 writeStringAttribute(out, ATTR_NAME, fileName); 4527 out.attributeInt(null, ATTR_CHECKSUM_KIND, checksum.getType()); 4528 writeByteArrayAttribute(out, ATTR_CHECKSUM_VALUE, checksum.getValue()); 4529 out.endTag(null, TAG_SESSION_CHECKSUM); 4530 } 4531 } 4532 for (int i = 0, isize = mChecksums.size(); i < isize; ++i) { 4533 final String fileName = mChecksums.keyAt(i); 4534 final PerFileChecksum perFileChecksum = mChecksums.valueAt(i); 4535 final byte[] signature = perFileChecksum.getSignature(); 4536 if (signature == null || signature.length == 0) { 4537 continue; 4538 } 4539 out.startTag(null, TAG_SESSION_CHECKSUM_SIGNATURE); 4540 writeStringAttribute(out, ATTR_NAME, fileName); 4541 writeByteArrayAttribute(out, ATTR_SIGNATURE, signature); 4542 out.endTag(null, TAG_SESSION_CHECKSUM_SIGNATURE); 4543 } 4544 4545 } 4546 4547 out.endTag(null, TAG_SESSION); 4548 } 4549 4550 // Validity check to be performed when the session is restored from an external file. Only one 4551 // of the session states should be true, or none of them. 4552 private static boolean isStagedSessionStateValid(boolean isReady, boolean isApplied, 4553 boolean isFailed) { 4554 return (!isReady && !isApplied && !isFailed) 4555 || (isReady && !isApplied && !isFailed) 4556 || (!isReady && isApplied && !isFailed) 4557 || (!isReady && !isApplied && isFailed); 4558 } 4559 4560 /** 4561 * Read new session from a {@link TypedXmlPullParser xml description} and create it. 4562 * 4563 * @param in The source of the description 4564 * @param callback Callback the session uses to notify about changes of it's state 4565 * @param context Context to be used by the session 4566 * @param pm PackageManager to use by the session 4567 * @param installerThread Thread to be used for callbacks of this session 4568 * @param sessionsDir The directory the sessions are stored in 4569 * 4570 * @param sessionProvider to get the other PackageInstallerSession instance by sessionId. 4571 * @return The newly created session 4572 */ 4573 public static PackageInstallerSession readFromXml(@NonNull TypedXmlPullParser in, 4574 @NonNull PackageInstallerService.InternalCallback callback, @NonNull Context context, 4575 @NonNull PackageManagerService pm, Looper installerThread, 4576 @NonNull StagingManager stagingManager, @NonNull File sessionsDir, 4577 @NonNull PackageSessionProvider sessionProvider, 4578 @NonNull SilentUpdatePolicy silentUpdatePolicy) 4579 throws IOException, XmlPullParserException { 4580 final int sessionId = in.getAttributeInt(null, ATTR_SESSION_ID); 4581 final int userId = in.getAttributeInt(null, ATTR_USER_ID); 4582 final String installerPackageName = readStringAttribute(in, ATTR_INSTALLER_PACKAGE_NAME); 4583 final String installerAttributionTag = readStringAttribute(in, 4584 ATTR_INSTALLER_ATTRIBUTION_TAG); 4585 final int installerUid = in.getAttributeInt(null, ATTR_INSTALLER_UID, pm.snapshotComputer() 4586 .getPackageUid(installerPackageName, PackageManager.MATCH_UNINSTALLED_PACKAGES, 4587 userId)); 4588 final String installInitiatingPackageName = 4589 readStringAttribute(in, ATTR_INITIATING_PACKAGE_NAME); 4590 final String installOriginatingPackageName = 4591 readStringAttribute(in, ATTR_ORIGINATING_PACKAGE_NAME); 4592 final long createdMillis = in.getAttributeLong(null, ATTR_CREATED_MILLIS); 4593 long updatedMillis = in.getAttributeLong(null, ATTR_UPDATED_MILLIS); 4594 final long committedMillis = in.getAttributeLong(null, ATTR_COMMITTED_MILLIS, 0L); 4595 final String stageDirRaw = readStringAttribute(in, ATTR_SESSION_STAGE_DIR); 4596 final File stageDir = (stageDirRaw != null) ? new File(stageDirRaw) : null; 4597 final String stageCid = readStringAttribute(in, ATTR_SESSION_STAGE_CID); 4598 final boolean prepared = in.getAttributeBoolean(null, ATTR_PREPARED, true); 4599 final boolean committed = in.getAttributeBoolean(null, ATTR_COMMITTED, false); 4600 final boolean destroyed = in.getAttributeBoolean(null, ATTR_DESTROYED, false); 4601 final boolean sealed = in.getAttributeBoolean(null, ATTR_SEALED, false); 4602 final int parentSessionId = in.getAttributeInt(null, ATTR_PARENT_SESSION_ID, 4603 SessionInfo.INVALID_ID); 4604 4605 final SessionParams params = new SessionParams( 4606 SessionParams.MODE_INVALID); 4607 params.isMultiPackage = in.getAttributeBoolean(null, ATTR_MULTI_PACKAGE, false); 4608 params.isStaged = in.getAttributeBoolean(null, ATTR_STAGED_SESSION, false); 4609 params.mode = in.getAttributeInt(null, ATTR_MODE); 4610 params.installFlags = in.getAttributeInt(null, ATTR_INSTALL_FLAGS); 4611 params.installLocation = in.getAttributeInt(null, ATTR_INSTALL_LOCATION); 4612 params.sizeBytes = in.getAttributeLong(null, ATTR_SIZE_BYTES); 4613 params.appPackageName = readStringAttribute(in, ATTR_APP_PACKAGE_NAME); 4614 params.appIcon = readBitmapAttribute(in, ATTR_APP_ICON); 4615 params.appLabel = readStringAttribute(in, ATTR_APP_LABEL); 4616 params.originatingUri = readUriAttribute(in, ATTR_ORIGINATING_URI); 4617 params.originatingUid = 4618 in.getAttributeInt(null, ATTR_ORIGINATING_UID, SessionParams.UID_UNKNOWN); 4619 params.referrerUri = readUriAttribute(in, ATTR_REFERRER_URI); 4620 params.abiOverride = readStringAttribute(in, ATTR_ABI_OVERRIDE); 4621 params.volumeUuid = readStringAttribute(in, ATTR_VOLUME_UUID); 4622 params.installReason = in.getAttributeInt(null, ATTR_INSTALL_REASON); 4623 params.packageSource = in.getAttributeInt(null, ATTR_PACKAGE_SOURCE); 4624 4625 if (in.getAttributeBoolean(null, ATTR_IS_DATALOADER, false)) { 4626 params.dataLoaderParams = new DataLoaderParams( 4627 in.getAttributeInt(null, ATTR_DATALOADER_TYPE), 4628 new ComponentName( 4629 readStringAttribute(in, ATTR_DATALOADER_PACKAGE_NAME), 4630 readStringAttribute(in, ATTR_DATALOADER_CLASS_NAME)), 4631 readStringAttribute(in, ATTR_DATALOADER_ARGUMENTS)); 4632 } 4633 4634 final File appIconFile = buildAppIconFile(sessionId, sessionsDir); 4635 if (appIconFile.exists()) { 4636 params.appIcon = BitmapFactory.decodeFile(appIconFile.getAbsolutePath()); 4637 params.appIconLastModified = appIconFile.lastModified(); 4638 } 4639 final boolean isReady = in.getAttributeBoolean(null, ATTR_IS_READY, false); 4640 final boolean isFailed = in.getAttributeBoolean(null, ATTR_IS_FAILED, false); 4641 final boolean isApplied = in.getAttributeBoolean(null, ATTR_IS_APPLIED, false); 4642 final int sessionErrorCode = in.getAttributeInt(null, ATTR_SESSION_ERROR_CODE, 4643 PackageManager.INSTALL_UNKNOWN); 4644 final String sessionErrorMessage = readStringAttribute(in, ATTR_SESSION_ERROR_MESSAGE); 4645 4646 if (!isStagedSessionStateValid(isReady, isApplied, isFailed)) { 4647 throw new IllegalArgumentException("Can't restore staged session with invalid state."); 4648 } 4649 4650 // Parse sub tags of this session, typically used for repeated values / arrays. 4651 // Sub tags can come in any order, therefore we need to keep track of what we find while 4652 // parsing and only set the right values at the end. 4653 4654 // Store the current depth. We should stop parsing when we reach an end tag at the same 4655 // depth. 4656 List<String> grantedRuntimePermissions = new ArrayList<>(); 4657 List<String> whitelistedRestrictedPermissions = new ArrayList<>(); 4658 int autoRevokePermissionsMode = MODE_DEFAULT; 4659 IntArray childSessionIds = new IntArray(); 4660 List<InstallationFile> files = new ArrayList<>(); 4661 ArrayMap<String, List<Checksum>> checksums = new ArrayMap<>(); 4662 ArrayMap<String, byte[]> signatures = new ArrayMap<>(); 4663 int outerDepth = in.getDepth(); 4664 int type; 4665 while ((type = in.next()) != XmlPullParser.END_DOCUMENT 4666 && (type != XmlPullParser.END_TAG || in.getDepth() > outerDepth)) { 4667 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { 4668 continue; 4669 } 4670 if (TAG_GRANTED_RUNTIME_PERMISSION.equals(in.getName())) { 4671 grantedRuntimePermissions.add(readStringAttribute(in, ATTR_NAME)); 4672 } 4673 if (TAG_WHITELISTED_RESTRICTED_PERMISSION.equals(in.getName())) { 4674 whitelistedRestrictedPermissions.add(readStringAttribute(in, ATTR_NAME)); 4675 4676 } 4677 if (TAG_AUTO_REVOKE_PERMISSIONS_MODE.equals(in.getName())) { 4678 autoRevokePermissionsMode = in.getAttributeInt(null, ATTR_MODE); 4679 } 4680 if (TAG_CHILD_SESSION.equals(in.getName())) { 4681 childSessionIds.add(in.getAttributeInt(null, ATTR_SESSION_ID, 4682 SessionInfo.INVALID_ID)); 4683 } 4684 if (TAG_SESSION_FILE.equals(in.getName())) { 4685 files.add(new InstallationFile( 4686 in.getAttributeInt(null, ATTR_LOCATION, 0), 4687 readStringAttribute(in, ATTR_NAME), 4688 in.getAttributeLong(null, ATTR_LENGTH_BYTES, -1), 4689 readByteArrayAttribute(in, ATTR_METADATA), 4690 readByteArrayAttribute(in, ATTR_SIGNATURE))); 4691 } 4692 if (TAG_SESSION_CHECKSUM.equals(in.getName())) { 4693 final String fileName = readStringAttribute(in, ATTR_NAME); 4694 final Checksum checksum = new Checksum( 4695 in.getAttributeInt(null, ATTR_CHECKSUM_KIND, 0), 4696 readByteArrayAttribute(in, ATTR_CHECKSUM_VALUE)); 4697 4698 List<Checksum> fileChecksums = checksums.get(fileName); 4699 if (fileChecksums == null) { 4700 fileChecksums = new ArrayList<>(); 4701 checksums.put(fileName, fileChecksums); 4702 } 4703 fileChecksums.add(checksum); 4704 } 4705 if (TAG_SESSION_CHECKSUM_SIGNATURE.equals(in.getName())) { 4706 final String fileName = readStringAttribute(in, ATTR_NAME); 4707 final byte[] signature = readByteArrayAttribute(in, ATTR_SIGNATURE); 4708 signatures.put(fileName, signature); 4709 } 4710 } 4711 4712 if (grantedRuntimePermissions.size() > 0) { 4713 params.grantedRuntimePermissions = 4714 grantedRuntimePermissions.toArray(EmptyArray.STRING); 4715 } 4716 4717 if (whitelistedRestrictedPermissions.size() > 0) { 4718 params.whitelistedRestrictedPermissions = whitelistedRestrictedPermissions; 4719 } 4720 4721 params.autoRevokePermissionsMode = autoRevokePermissionsMode; 4722 4723 int[] childSessionIdsArray; 4724 if (childSessionIds.size() > 0) { 4725 childSessionIdsArray = new int[childSessionIds.size()]; 4726 for (int i = 0, size = childSessionIds.size(); i < size; ++i) { 4727 childSessionIdsArray[i] = childSessionIds.get(i); 4728 } 4729 } else { 4730 childSessionIdsArray = EMPTY_CHILD_SESSION_ARRAY; 4731 } 4732 4733 InstallationFile[] fileArray = null; 4734 if (!files.isEmpty()) { 4735 fileArray = files.toArray(EMPTY_INSTALLATION_FILE_ARRAY); 4736 } 4737 4738 ArrayMap<String, PerFileChecksum> checksumsMap = null; 4739 if (!checksums.isEmpty()) { 4740 checksumsMap = new ArrayMap<>(checksums.size()); 4741 for (int i = 0, isize = checksums.size(); i < isize; ++i) { 4742 final String fileName = checksums.keyAt(i); 4743 final List<Checksum> perFileChecksum = checksums.valueAt(i); 4744 final byte[] perFileSignature = signatures.get(fileName); 4745 checksumsMap.put(fileName, new PerFileChecksum( 4746 perFileChecksum.toArray(new Checksum[perFileChecksum.size()]), 4747 perFileSignature)); 4748 } 4749 } 4750 4751 InstallSource installSource = InstallSource.create(installInitiatingPackageName, 4752 installOriginatingPackageName, installerPackageName, installerAttributionTag, 4753 params.packageSource); 4754 return new PackageInstallerSession(callback, context, pm, sessionProvider, 4755 silentUpdatePolicy, installerThread, stagingManager, sessionId, userId, 4756 installerUid, installSource, params, createdMillis, committedMillis, stageDir, 4757 stageCid, fileArray, checksumsMap, prepared, committed, destroyed, sealed, 4758 childSessionIdsArray, parentSessionId, isReady, isFailed, isApplied, 4759 sessionErrorCode, sessionErrorMessage); 4760 } 4761 } 4762