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