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