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