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