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.content.pm.PackageManager.INSTALL_FAILED_ABORTED; 20 import static android.content.pm.PackageManager.INSTALL_FAILED_BAD_SIGNATURE; 21 import static android.content.pm.PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; 22 import static android.content.pm.PackageManager.INSTALL_FAILED_INTERNAL_ERROR; 23 import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK; 24 import static android.content.pm.PackageManager.INSTALL_FAILED_MISSING_SPLIT; 25 import static android.content.pm.PackageParser.APEX_FILE_EXTENSION; 26 import static android.content.pm.PackageParser.APK_FILE_EXTENSION; 27 import static android.system.OsConstants.O_CREAT; 28 import static android.system.OsConstants.O_RDONLY; 29 import static android.system.OsConstants.O_WRONLY; 30 31 import static com.android.internal.util.XmlUtils.readBitmapAttribute; 32 import static com.android.internal.util.XmlUtils.readBooleanAttribute; 33 import static com.android.internal.util.XmlUtils.readIntAttribute; 34 import static com.android.internal.util.XmlUtils.readLongAttribute; 35 import static com.android.internal.util.XmlUtils.readStringAttribute; 36 import static com.android.internal.util.XmlUtils.readUriAttribute; 37 import static com.android.internal.util.XmlUtils.writeBooleanAttribute; 38 import static com.android.internal.util.XmlUtils.writeIntAttribute; 39 import static com.android.internal.util.XmlUtils.writeLongAttribute; 40 import static com.android.internal.util.XmlUtils.writeStringAttribute; 41 import static com.android.internal.util.XmlUtils.writeUriAttribute; 42 import static com.android.server.pm.PackageInstallerService.prepareStageDir; 43 44 import android.Manifest; 45 import android.annotation.NonNull; 46 import android.annotation.Nullable; 47 import android.app.admin.DevicePolicyEventLogger; 48 import android.app.admin.DevicePolicyManagerInternal; 49 import android.content.Context; 50 import android.content.IIntentReceiver; 51 import android.content.IIntentSender; 52 import android.content.Intent; 53 import android.content.IntentSender; 54 import android.content.pm.ApplicationInfo; 55 import android.content.pm.IPackageInstallObserver2; 56 import android.content.pm.IPackageInstallerSession; 57 import android.content.pm.PackageInfo; 58 import android.content.pm.PackageInstaller; 59 import android.content.pm.PackageInstaller.SessionInfo; 60 import android.content.pm.PackageInstaller.SessionInfo.StagedSessionErrorCode; 61 import android.content.pm.PackageInstaller.SessionParams; 62 import android.content.pm.PackageManager; 63 import android.content.pm.PackageParser; 64 import android.content.pm.PackageParser.ApkLite; 65 import android.content.pm.PackageParser.PackageLite; 66 import android.content.pm.PackageParser.PackageParserException; 67 import android.content.pm.dex.DexMetadataHelper; 68 import android.graphics.Bitmap; 69 import android.graphics.BitmapFactory; 70 import android.os.Binder; 71 import android.os.Bundle; 72 import android.os.FileBridge; 73 import android.os.FileUtils; 74 import android.os.Handler; 75 import android.os.IBinder; 76 import android.os.Looper; 77 import android.os.Message; 78 import android.os.ParcelFileDescriptor; 79 import android.os.ParcelableException; 80 import android.os.Process; 81 import android.os.RevocableFileDescriptor; 82 import android.os.SystemProperties; 83 import android.os.UserHandle; 84 import android.os.storage.StorageManager; 85 import android.stats.devicepolicy.DevicePolicyEnums; 86 import android.system.ErrnoException; 87 import android.system.Int64Ref; 88 import android.system.Os; 89 import android.system.OsConstants; 90 import android.system.StructStat; 91 import android.text.TextUtils; 92 import android.util.ArraySet; 93 import android.util.ExceptionUtils; 94 import android.util.MathUtils; 95 import android.util.Slog; 96 import android.util.SparseIntArray; 97 import android.util.apk.ApkSignatureVerifier; 98 99 import com.android.internal.annotations.GuardedBy; 100 import com.android.internal.content.NativeLibraryHelper; 101 import com.android.internal.content.PackageHelper; 102 import com.android.internal.os.SomeArgs; 103 import com.android.internal.util.ArrayUtils; 104 import com.android.internal.util.IndentingPrintWriter; 105 import com.android.internal.util.Preconditions; 106 import com.android.server.LocalServices; 107 import com.android.server.pm.Installer.InstallerException; 108 import com.android.server.pm.dex.DexManager; 109 import com.android.server.security.VerityUtils; 110 111 import libcore.io.IoUtils; 112 113 import org.xmlpull.v1.XmlPullParser; 114 import org.xmlpull.v1.XmlPullParserException; 115 import org.xmlpull.v1.XmlSerializer; 116 117 import java.io.File; 118 import java.io.FileDescriptor; 119 import java.io.FileFilter; 120 import java.io.FileOutputStream; 121 import java.io.IOException; 122 import java.util.ArrayList; 123 import java.util.Arrays; 124 import java.util.LinkedList; 125 import java.util.List; 126 import java.util.concurrent.atomic.AtomicInteger; 127 128 public class PackageInstallerSession extends IPackageInstallerSession.Stub { 129 private static final String TAG = "PackageInstallerSession"; 130 private static final boolean LOGD = true; 131 private static final String REMOVE_MARKER_EXTENSION = ".removed"; 132 133 private static final int MSG_COMMIT = 1; 134 private static final int MSG_ON_PACKAGE_INSTALLED = 2; 135 136 /** XML constants used for persisting a session */ 137 static final String TAG_SESSION = "session"; 138 static final String TAG_CHILD_SESSION = "childSession"; 139 private static final String TAG_GRANTED_RUNTIME_PERMISSION = "granted-runtime-permission"; 140 private static final String TAG_WHITELISTED_RESTRICTED_PERMISSION = 141 "whitelisted-restricted-permission"; 142 private static final String ATTR_SESSION_ID = "sessionId"; 143 private static final String ATTR_USER_ID = "userId"; 144 private static final String ATTR_INSTALLER_PACKAGE_NAME = "installerPackageName"; 145 private static final String ATTR_INSTALLER_UID = "installerUid"; 146 private static final String ATTR_CREATED_MILLIS = "createdMillis"; 147 private static final String ATTR_UPDATED_MILLIS = "updatedMillis"; 148 private static final String ATTR_SESSION_STAGE_DIR = "sessionStageDir"; 149 private static final String ATTR_SESSION_STAGE_CID = "sessionStageCid"; 150 private static final String ATTR_PREPARED = "prepared"; 151 private static final String ATTR_COMMITTED = "committed"; 152 private static final String ATTR_SEALED = "sealed"; 153 private static final String ATTR_MULTI_PACKAGE = "multiPackage"; 154 private static final String ATTR_PARENT_SESSION_ID = "parentSessionId"; 155 private static final String ATTR_STAGED_SESSION = "stagedSession"; 156 private static final String ATTR_IS_READY = "isReady"; 157 private static final String ATTR_IS_FAILED = "isFailed"; 158 private static final String ATTR_IS_APPLIED = "isApplied"; 159 private static final String ATTR_STAGED_SESSION_ERROR_CODE = "errorCode"; 160 private static final String ATTR_STAGED_SESSION_ERROR_MESSAGE = "errorMessage"; 161 private static final String ATTR_MODE = "mode"; 162 private static final String ATTR_INSTALL_FLAGS = "installFlags"; 163 private static final String ATTR_INSTALL_LOCATION = "installLocation"; 164 private static final String ATTR_SIZE_BYTES = "sizeBytes"; 165 private static final String ATTR_APP_PACKAGE_NAME = "appPackageName"; 166 @Deprecated 167 private static final String ATTR_APP_ICON = "appIcon"; 168 private static final String ATTR_APP_LABEL = "appLabel"; 169 private static final String ATTR_ORIGINATING_URI = "originatingUri"; 170 private static final String ATTR_ORIGINATING_UID = "originatingUid"; 171 private static final String ATTR_REFERRER_URI = "referrerUri"; 172 private static final String ATTR_ABI_OVERRIDE = "abiOverride"; 173 private static final String ATTR_VOLUME_UUID = "volumeUuid"; 174 private static final String ATTR_NAME = "name"; 175 private static final String ATTR_INSTALL_REASON = "installRason"; 176 177 private static final String PROPERTY_NAME_INHERIT_NATIVE = "pi.inherit_native_on_dont_kill"; 178 private static final int[] EMPTY_CHILD_SESSION_ARRAY = {}; 179 180 // TODO: enforce INSTALL_ALLOW_TEST 181 // TODO: enforce INSTALL_ALLOW_DOWNGRADE 182 183 private final PackageInstallerService.InternalCallback mCallback; 184 private final Context mContext; 185 private final PackageManagerService mPm; 186 private final Handler mHandler; 187 private final PackageSessionProvider mSessionProvider; 188 private final StagingManager mStagingManager; 189 190 final int sessionId; 191 final int userId; 192 final SessionParams params; 193 final long createdMillis; 194 195 /** Staging location where client data is written. */ 196 final File stageDir; 197 final String stageCid; 198 199 private final AtomicInteger mActiveCount = new AtomicInteger(); 200 201 private final Object mLock = new Object(); 202 203 /** Timestamp of the last time this session changed state */ 204 @GuardedBy("mLock") 205 private long updatedMillis; 206 207 /** Uid of the creator of this session. */ 208 private final int mOriginalInstallerUid; 209 210 /** Package of the owner of the installer session */ 211 @GuardedBy("mLock") 212 private @Nullable String mInstallerPackageName; 213 214 /** Uid of the owner of the installer session */ 215 @GuardedBy("mLock") 216 private int mInstallerUid; 217 218 @GuardedBy("mLock") 219 private float mClientProgress = 0; 220 @GuardedBy("mLock") 221 private float mInternalProgress = 0; 222 223 @GuardedBy("mLock") 224 private float mProgress = 0; 225 @GuardedBy("mLock") 226 private float mReportedProgress = -1; 227 228 /** State of the session. */ 229 @GuardedBy("mLock") 230 private boolean mPrepared = false; 231 @GuardedBy("mLock") 232 private boolean mSealed = false; 233 @GuardedBy("mLock") 234 private boolean mShouldBeSealed = false; 235 @GuardedBy("mLock") 236 private boolean mCommitted = false; 237 @GuardedBy("mLock") 238 private boolean mRelinquished = false; 239 @GuardedBy("mLock") 240 private boolean mDestroyed = false; 241 242 /** Permissions have been accepted by the user (see {@link #setPermissionsResult}) */ 243 @GuardedBy("mLock") 244 private boolean mPermissionsManuallyAccepted = false; 245 246 @GuardedBy("mLock") 247 private int mFinalStatus; 248 @GuardedBy("mLock") 249 private String mFinalMessage; 250 251 @GuardedBy("mLock") 252 private final ArrayList<RevocableFileDescriptor> mFds = new ArrayList<>(); 253 @GuardedBy("mLock") 254 private final ArrayList<FileBridge> mBridges = new ArrayList<>(); 255 256 @GuardedBy("mLock") 257 private IntentSender mRemoteStatusReceiver; 258 259 /** Fields derived from commit parsing */ 260 @GuardedBy("mLock") 261 private String mPackageName; 262 @GuardedBy("mLock") 263 private long mVersionCode; 264 @GuardedBy("mLock") 265 private PackageParser.SigningDetails mSigningDetails; 266 @GuardedBy("mLock") 267 private SparseIntArray mChildSessionIds = new SparseIntArray(); 268 @GuardedBy("mLock") 269 private int mParentSessionId; 270 271 @GuardedBy("mLock") 272 private boolean mStagedSessionApplied; 273 @GuardedBy("mLock") 274 private boolean mStagedSessionReady; 275 @GuardedBy("mLock") 276 private boolean mStagedSessionFailed; 277 @GuardedBy("mLock") 278 private int mStagedSessionErrorCode = SessionInfo.STAGED_SESSION_NO_ERROR; 279 @GuardedBy("mLock") 280 private String mStagedSessionErrorMessage; 281 282 /** 283 * Path to the validated base APK for this session, which may point at an 284 * APK inside the session (when the session defines the base), or it may 285 * point at the existing base APK (when adding splits to an existing app). 286 * <p> 287 * This is used when confirming permissions, since we can't fully stage the 288 * session inside an ASEC before confirming with user. 289 */ 290 @GuardedBy("mLock") 291 private File mResolvedBaseFile; 292 293 @GuardedBy("mLock") 294 private final List<File> mResolvedStagedFiles = new ArrayList<>(); 295 @GuardedBy("mLock") 296 private final List<File> mResolvedInheritedFiles = new ArrayList<>(); 297 @GuardedBy("mLock") 298 private final List<String> mResolvedInstructionSets = new ArrayList<>(); 299 @GuardedBy("mLock") 300 private final List<String> mResolvedNativeLibPaths = new ArrayList<>(); 301 @GuardedBy("mLock") 302 private File mInheritedFilesBase; 303 @GuardedBy("mLock") 304 private boolean mVerityFound; 305 306 private static final FileFilter sAddedFilter = new FileFilter() { 307 @Override 308 public boolean accept(File file) { 309 // Installers can't stage directories, so it's fine to ignore 310 // entries like "lost+found". 311 if (file.isDirectory()) return false; 312 if (file.getName().endsWith(REMOVE_MARKER_EXTENSION)) return false; 313 if (DexMetadataHelper.isDexMetadataFile(file)) return false; 314 if (VerityUtils.isFsveritySignatureFile(file)) return false; 315 return true; 316 } 317 }; 318 private static final FileFilter sRemovedFilter = new FileFilter() { 319 @Override 320 public boolean accept(File file) { 321 if (file.isDirectory()) return false; 322 if (!file.getName().endsWith(REMOVE_MARKER_EXTENSION)) return false; 323 return true; 324 } 325 }; 326 327 private final Handler.Callback mHandlerCallback = new Handler.Callback() { 328 @Override 329 public boolean handleMessage(Message msg) { 330 switch (msg.what) { 331 case MSG_COMMIT: 332 handleCommit(); 333 break; 334 case MSG_ON_PACKAGE_INSTALLED: 335 final SomeArgs args = (SomeArgs) msg.obj; 336 final String packageName = (String) args.arg1; 337 final String message = (String) args.arg2; 338 final Bundle extras = (Bundle) args.arg3; 339 final IntentSender statusReceiver = (IntentSender) args.arg4; 340 final int returnCode = args.argi1; 341 args.recycle(); 342 343 PackageInstallerService.sendOnPackageInstalled(mContext, 344 statusReceiver, sessionId, 345 isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked(), userId, 346 packageName, returnCode, message, extras); 347 348 break; 349 } 350 351 return true; 352 } 353 }; 354 355 /** 356 * @return {@code true} iff the installing is app an device owner or affiliated profile owner. 357 */ 358 @GuardedBy("mLock") isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked()359 private boolean isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked() { 360 if (userId != UserHandle.getUserId(mInstallerUid)) { 361 return false; 362 } 363 DevicePolicyManagerInternal dpmi = 364 LocalServices.getService(DevicePolicyManagerInternal.class); 365 return dpmi != null && dpmi.canSilentlyInstallPackage(mInstallerPackageName, mInstallerUid); 366 } 367 368 /** 369 * Checks if the permissions still need to be confirmed. 370 * 371 * <p>This is dependant on the identity of the installer, hence this cannot be cached if the 372 * installer might still {@link #transfer(String) change}. 373 * 374 * @return {@code true} iff we need to ask to confirm the permissions? 375 */ 376 @GuardedBy("mLock") needToAskForPermissionsLocked()377 private boolean needToAskForPermissionsLocked() { 378 if (mPermissionsManuallyAccepted) { 379 return false; 380 } 381 382 final boolean isInstallPermissionGranted = 383 (mPm.checkUidPermission(android.Manifest.permission.INSTALL_PACKAGES, 384 mInstallerUid) == PackageManager.PERMISSION_GRANTED); 385 final boolean isSelfUpdatePermissionGranted = 386 (mPm.checkUidPermission(android.Manifest.permission.INSTALL_SELF_UPDATES, 387 mInstallerUid) == PackageManager.PERMISSION_GRANTED); 388 final boolean isUpdatePermissionGranted = 389 (mPm.checkUidPermission(android.Manifest.permission.INSTALL_PACKAGE_UPDATES, 390 mInstallerUid) == PackageManager.PERMISSION_GRANTED); 391 final int targetPackageUid = mPm.getPackageUid(mPackageName, 0, userId); 392 final boolean isPermissionGranted = isInstallPermissionGranted 393 || (isUpdatePermissionGranted && targetPackageUid != -1) 394 || (isSelfUpdatePermissionGranted && targetPackageUid == mInstallerUid); 395 final boolean isInstallerRoot = (mInstallerUid == Process.ROOT_UID); 396 final boolean isInstallerSystem = (mInstallerUid == Process.SYSTEM_UID); 397 final boolean forcePermissionPrompt = 398 (params.installFlags & PackageManager.INSTALL_FORCE_PERMISSION_PROMPT) != 0; 399 400 // Device owners and affiliated profile owners are allowed to silently install packages, so 401 // the permission check is waived if the installer is the device owner. 402 return forcePermissionPrompt || !(isPermissionGranted || isInstallerRoot 403 || isInstallerSystem || isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked()); 404 } 405 PackageInstallerSession(PackageInstallerService.InternalCallback callback, Context context, PackageManagerService pm, PackageSessionProvider sessionProvider, Looper looper, StagingManager stagingManager, int sessionId, int userId, String installerPackageName, int installerUid, SessionParams params, long createdMillis, File stageDir, String stageCid, boolean prepared, boolean committed, boolean sealed, @Nullable int[] childSessionIds, int parentSessionId, boolean isReady, boolean isFailed, boolean isApplied, int stagedSessionErrorCode, String stagedSessionErrorMessage)406 public PackageInstallerSession(PackageInstallerService.InternalCallback callback, 407 Context context, PackageManagerService pm, 408 PackageSessionProvider sessionProvider, Looper looper, StagingManager stagingManager, 409 int sessionId, int userId, 410 String installerPackageName, int installerUid, SessionParams params, long createdMillis, 411 File stageDir, String stageCid, boolean prepared, boolean committed, boolean sealed, 412 @Nullable int[] childSessionIds, int parentSessionId, boolean isReady, 413 boolean isFailed, boolean isApplied, int stagedSessionErrorCode, 414 String stagedSessionErrorMessage) { 415 mCallback = callback; 416 mContext = context; 417 mPm = pm; 418 mSessionProvider = sessionProvider; 419 mHandler = new Handler(looper, mHandlerCallback); 420 mStagingManager = stagingManager; 421 422 this.sessionId = sessionId; 423 this.userId = userId; 424 mOriginalInstallerUid = installerUid; 425 mInstallerPackageName = installerPackageName; 426 mInstallerUid = installerUid; 427 this.params = params; 428 this.createdMillis = createdMillis; 429 this.updatedMillis = createdMillis; 430 this.stageDir = stageDir; 431 this.stageCid = stageCid; 432 this.mShouldBeSealed = sealed; 433 if (childSessionIds != null) { 434 for (int childSessionId : childSessionIds) { 435 mChildSessionIds.put(childSessionId, 0); 436 } 437 } 438 this.mParentSessionId = parentSessionId; 439 440 if (!params.isMultiPackage && (stageDir == null) == (stageCid == null)) { 441 throw new IllegalArgumentException( 442 "Exactly one of stageDir or stageCid stage must be set"); 443 } 444 445 mPrepared = prepared; 446 mCommitted = committed; 447 mStagedSessionReady = isReady; 448 mStagedSessionFailed = isFailed; 449 mStagedSessionApplied = isApplied; 450 mStagedSessionErrorCode = stagedSessionErrorCode; 451 mStagedSessionErrorMessage = 452 stagedSessionErrorMessage != null ? stagedSessionErrorMessage : ""; 453 } 454 generateInfo()455 public SessionInfo generateInfo() { 456 return generateInfo(true); 457 } 458 generateInfo(boolean includeIcon)459 public SessionInfo generateInfo(boolean includeIcon) { 460 final SessionInfo info = new SessionInfo(); 461 synchronized (mLock) { 462 info.sessionId = sessionId; 463 info.userId = userId; 464 info.installerPackageName = mInstallerPackageName; 465 info.resolvedBaseCodePath = (mResolvedBaseFile != null) ? 466 mResolvedBaseFile.getAbsolutePath() : null; 467 info.progress = mProgress; 468 info.sealed = mSealed; 469 info.isCommitted = mCommitted; 470 info.active = mActiveCount.get() > 0; 471 472 info.mode = params.mode; 473 info.installReason = params.installReason; 474 info.sizeBytes = params.sizeBytes; 475 info.appPackageName = params.appPackageName; 476 if (includeIcon) { 477 info.appIcon = params.appIcon; 478 } 479 info.appLabel = params.appLabel; 480 481 info.installLocation = params.installLocation; 482 info.originatingUri = params.originatingUri; 483 info.originatingUid = params.originatingUid; 484 info.referrerUri = params.referrerUri; 485 info.grantedRuntimePermissions = params.grantedRuntimePermissions; 486 info.whitelistedRestrictedPermissions = params.whitelistedRestrictedPermissions; 487 info.installFlags = params.installFlags; 488 info.isMultiPackage = params.isMultiPackage; 489 info.isStaged = params.isStaged; 490 info.parentSessionId = mParentSessionId; 491 info.childSessionIds = mChildSessionIds.copyKeys(); 492 if (info.childSessionIds == null) { 493 info.childSessionIds = EMPTY_CHILD_SESSION_ARRAY; 494 } 495 info.isStagedSessionApplied = mStagedSessionApplied; 496 info.isStagedSessionReady = mStagedSessionReady; 497 info.isStagedSessionFailed = mStagedSessionFailed; 498 info.setStagedSessionErrorCode(mStagedSessionErrorCode, mStagedSessionErrorMessage); 499 info.updatedMillis = updatedMillis; 500 } 501 return info; 502 } 503 isPrepared()504 public boolean isPrepared() { 505 synchronized (mLock) { 506 return mPrepared; 507 } 508 } 509 isSealed()510 public boolean isSealed() { 511 synchronized (mLock) { 512 return mSealed; 513 } 514 } 515 516 /** {@hide} */ isCommitted()517 boolean isCommitted() { 518 synchronized (mLock) { 519 return mCommitted; 520 } 521 } 522 523 /** Returns true if a staged session has reached a final state and can be forgotten about */ isStagedAndInTerminalState()524 public boolean isStagedAndInTerminalState() { 525 synchronized (mLock) { 526 return params.isStaged && (mStagedSessionApplied || mStagedSessionFailed); 527 } 528 } 529 530 @GuardedBy("mLock") assertPreparedAndNotSealedLocked(String cookie)531 private void assertPreparedAndNotSealedLocked(String cookie) { 532 assertPreparedAndNotCommittedOrDestroyedLocked(cookie); 533 if (mSealed) { 534 throw new SecurityException(cookie + " not allowed after sealing"); 535 } 536 } 537 538 @GuardedBy("mLock") assertPreparedAndNotCommittedOrDestroyedLocked(String cookie)539 private void assertPreparedAndNotCommittedOrDestroyedLocked(String cookie) { 540 assertPreparedAndNotDestroyedLocked(cookie); 541 if (mCommitted) { 542 throw new SecurityException(cookie + " not allowed after commit"); 543 } 544 } 545 546 @GuardedBy("mLock") assertPreparedAndNotDestroyedLocked(String cookie)547 private void assertPreparedAndNotDestroyedLocked(String cookie) { 548 if (!mPrepared) { 549 throw new IllegalStateException(cookie + " before prepared"); 550 } 551 if (mDestroyed) { 552 throw new SecurityException(cookie + " not allowed after destruction"); 553 } 554 } 555 556 @Override setClientProgress(float progress)557 public void setClientProgress(float progress) { 558 synchronized (mLock) { 559 assertCallerIsOwnerOrRootLocked(); 560 561 // Always publish first staging movement 562 final boolean forcePublish = (mClientProgress == 0); 563 mClientProgress = progress; 564 computeProgressLocked(forcePublish); 565 } 566 } 567 568 @Override addClientProgress(float progress)569 public void addClientProgress(float progress) { 570 synchronized (mLock) { 571 assertCallerIsOwnerOrRootLocked(); 572 573 setClientProgress(mClientProgress + progress); 574 } 575 } 576 577 @GuardedBy("mLock") computeProgressLocked(boolean forcePublish)578 private void computeProgressLocked(boolean forcePublish) { 579 mProgress = MathUtils.constrain(mClientProgress * 0.8f, 0f, 0.8f) 580 + MathUtils.constrain(mInternalProgress * 0.2f, 0f, 0.2f); 581 582 // Only publish when meaningful change 583 if (forcePublish || Math.abs(mProgress - mReportedProgress) >= 0.01) { 584 mReportedProgress = mProgress; 585 mCallback.onSessionProgressChanged(this, mProgress); 586 } 587 } 588 589 @Override getNames()590 public String[] getNames() { 591 synchronized (mLock) { 592 assertCallerIsOwnerOrRootLocked(); 593 assertPreparedAndNotCommittedOrDestroyedLocked("getNames"); 594 595 return getNamesLocked(); 596 } 597 } 598 599 @GuardedBy("mLock") getNamesLocked()600 private String[] getNamesLocked() { 601 return stageDir.list(); 602 } 603 filterFiles(File parent, String[] names, FileFilter filter)604 private static File[] filterFiles(File parent, String[] names, FileFilter filter) { 605 return Arrays.stream(names).map(name -> new File(parent, name)).filter( 606 file -> filter.accept(file)).toArray(File[]::new); 607 } 608 609 @GuardedBy("mLock") getAddedFilesLocked()610 private File[] getAddedFilesLocked() { 611 String[] names = getNamesLocked(); 612 return filterFiles(stageDir, names, sAddedFilter); 613 } 614 615 @GuardedBy("mLock") getRemovedFilesLocked()616 private File[] getRemovedFilesLocked() { 617 String[] names = getNamesLocked(); 618 return filterFiles(stageDir, names, sRemovedFilter); 619 } 620 621 @Override removeSplit(String splitName)622 public void removeSplit(String splitName) { 623 if (TextUtils.isEmpty(params.appPackageName)) { 624 throw new IllegalStateException("Must specify package name to remove a split"); 625 } 626 627 synchronized (mLock) { 628 assertCallerIsOwnerOrRootLocked(); 629 assertPreparedAndNotCommittedOrDestroyedLocked("removeSplit"); 630 631 try { 632 createRemoveSplitMarkerLocked(splitName); 633 } catch (IOException e) { 634 throw ExceptionUtils.wrap(e); 635 } 636 } 637 } 638 getRemoveMarkerName(String name)639 private static String getRemoveMarkerName(String name) { 640 final String markerName = name + REMOVE_MARKER_EXTENSION; 641 if (!FileUtils.isValidExtFilename(markerName)) { 642 throw new IllegalArgumentException("Invalid marker: " + markerName); 643 } 644 return markerName; 645 } 646 createRemoveSplitMarkerLocked(String splitName)647 private void createRemoveSplitMarkerLocked(String splitName) throws IOException { 648 try { 649 final File target = new File(stageDir, getRemoveMarkerName(splitName)); 650 target.createNewFile(); 651 Os.chmod(target.getAbsolutePath(), 0 /*mode*/); 652 } catch (ErrnoException e) { 653 throw e.rethrowAsIOException(); 654 } 655 } 656 657 @Override openWrite(String name, long offsetBytes, long lengthBytes)658 public ParcelFileDescriptor openWrite(String name, long offsetBytes, long lengthBytes) { 659 try { 660 return doWriteInternal(name, offsetBytes, lengthBytes, null); 661 } catch (IOException e) { 662 throw ExceptionUtils.wrap(e); 663 } 664 } 665 666 @Override write(String name, long offsetBytes, long lengthBytes, ParcelFileDescriptor fd)667 public void write(String name, long offsetBytes, long lengthBytes, 668 ParcelFileDescriptor fd) { 669 try { 670 doWriteInternal(name, offsetBytes, lengthBytes, fd); 671 } catch (IOException e) { 672 throw ExceptionUtils.wrap(e); 673 } 674 } 675 doWriteInternal(String name, long offsetBytes, long lengthBytes, ParcelFileDescriptor incomingFd)676 private ParcelFileDescriptor doWriteInternal(String name, long offsetBytes, long lengthBytes, 677 ParcelFileDescriptor incomingFd) throws IOException { 678 // Quick sanity check of state, and allocate a pipe for ourselves. We 679 // then do heavy disk allocation outside the lock, but this open pipe 680 // will block any attempted install transitions. 681 final RevocableFileDescriptor fd; 682 final FileBridge bridge; 683 synchronized (mLock) { 684 assertCallerIsOwnerOrRootLocked(); 685 assertPreparedAndNotSealedLocked("openWrite"); 686 687 if (PackageInstaller.ENABLE_REVOCABLE_FD) { 688 fd = new RevocableFileDescriptor(); 689 bridge = null; 690 mFds.add(fd); 691 } else { 692 fd = null; 693 bridge = new FileBridge(); 694 mBridges.add(bridge); 695 } 696 } 697 698 try { 699 // Use installer provided name for now; we always rename later 700 if (!FileUtils.isValidExtFilename(name)) { 701 throw new IllegalArgumentException("Invalid name: " + name); 702 } 703 final File target; 704 final long identity = Binder.clearCallingIdentity(); 705 try { 706 target = new File(stageDir, name); 707 } finally { 708 Binder.restoreCallingIdentity(identity); 709 } 710 711 // TODO: this should delegate to DCS so the system process avoids 712 // holding open FDs into containers. 713 final FileDescriptor targetFd = Os.open(target.getAbsolutePath(), 714 O_CREAT | O_WRONLY, 0644); 715 Os.chmod(target.getAbsolutePath(), 0644); 716 717 // If caller specified a total length, allocate it for them. Free up 718 // cache space to grow, if needed. 719 if (stageDir != null && lengthBytes > 0) { 720 mContext.getSystemService(StorageManager.class).allocateBytes(targetFd, lengthBytes, 721 PackageHelper.translateAllocateFlags(params.installFlags)); 722 } 723 724 if (offsetBytes > 0) { 725 Os.lseek(targetFd, offsetBytes, OsConstants.SEEK_SET); 726 } 727 728 if (incomingFd != null) { 729 switch (Binder.getCallingUid()) { 730 case android.os.Process.SHELL_UID: 731 case android.os.Process.ROOT_UID: 732 case android.os.Process.SYSTEM_UID: 733 break; 734 default: 735 throw new SecurityException( 736 "Reverse mode only supported from shell or system"); 737 } 738 739 // In "reverse" mode, we're streaming data ourselves from the 740 // incoming FD, which means we never have to hand out our 741 // sensitive internal FD. We still rely on a "bridge" being 742 // inserted above to hold the session active. 743 try { 744 final Int64Ref last = new Int64Ref(0); 745 FileUtils.copy(incomingFd.getFileDescriptor(), targetFd, lengthBytes, null, 746 Runnable::run, (long progress) -> { 747 if (params.sizeBytes > 0) { 748 final long delta = progress - last.value; 749 last.value = progress; 750 addClientProgress((float) delta / (float) params.sizeBytes); 751 } 752 }); 753 } finally { 754 IoUtils.closeQuietly(targetFd); 755 IoUtils.closeQuietly(incomingFd); 756 757 // We're done here, so remove the "bridge" that was holding 758 // the session active. 759 synchronized (mLock) { 760 if (PackageInstaller.ENABLE_REVOCABLE_FD) { 761 mFds.remove(fd); 762 } else { 763 bridge.forceClose(); 764 mBridges.remove(bridge); 765 } 766 } 767 } 768 return null; 769 } else if (PackageInstaller.ENABLE_REVOCABLE_FD) { 770 fd.init(mContext, targetFd); 771 return fd.getRevocableFileDescriptor(); 772 } else { 773 bridge.setTargetFile(targetFd); 774 bridge.start(); 775 return new ParcelFileDescriptor(bridge.getClientSocket()); 776 } 777 778 } catch (ErrnoException e) { 779 throw e.rethrowAsIOException(); 780 } 781 } 782 783 @Override openRead(String name)784 public ParcelFileDescriptor openRead(String name) { 785 synchronized (mLock) { 786 assertCallerIsOwnerOrRootLocked(); 787 assertPreparedAndNotCommittedOrDestroyedLocked("openRead"); 788 try { 789 return openReadInternalLocked(name); 790 } catch (IOException e) { 791 throw ExceptionUtils.wrap(e); 792 } 793 } 794 } 795 openReadInternalLocked(String name)796 private ParcelFileDescriptor openReadInternalLocked(String name) throws IOException { 797 try { 798 if (!FileUtils.isValidExtFilename(name)) { 799 throw new IllegalArgumentException("Invalid name: " + name); 800 } 801 final File target = new File(stageDir, name); 802 final FileDescriptor targetFd = Os.open(target.getAbsolutePath(), O_RDONLY, 0); 803 return new ParcelFileDescriptor(targetFd); 804 } catch (ErrnoException e) { 805 throw e.rethrowAsIOException(); 806 } 807 } 808 809 /** 810 * Check if the caller is the owner of this session. Otherwise throw a 811 * {@link SecurityException}. 812 */ 813 @GuardedBy("mLock") assertCallerIsOwnerOrRootLocked()814 private void assertCallerIsOwnerOrRootLocked() { 815 final int callingUid = Binder.getCallingUid(); 816 if (callingUid != Process.ROOT_UID && callingUid != mInstallerUid) { 817 throw new SecurityException("Session does not belong to uid " + callingUid); 818 } 819 } 820 821 /** 822 * If anybody is reading or writing data of the session, throw an {@link SecurityException}. 823 */ 824 @GuardedBy("mLock") assertNoWriteFileTransfersOpenLocked()825 private void assertNoWriteFileTransfersOpenLocked() { 826 // Verify that all writers are hands-off 827 for (RevocableFileDescriptor fd : mFds) { 828 if (!fd.isRevoked()) { 829 throw new SecurityException("Files still open"); 830 } 831 } 832 for (FileBridge bridge : mBridges) { 833 if (!bridge.isClosed()) { 834 throw new SecurityException("Files still open"); 835 } 836 } 837 } 838 839 @Override commit(@onNull IntentSender statusReceiver, boolean forTransfer)840 public void commit(@NonNull IntentSender statusReceiver, boolean forTransfer) { 841 if (hasParentSessionId()) { 842 throw new IllegalStateException( 843 "Session " + sessionId + " is a child of multi-package session " 844 + mParentSessionId + " and may not be committed directly."); 845 } 846 if (!markAsCommitted(statusReceiver, forTransfer)) { 847 return; 848 } 849 if (isMultiPackage()) { 850 final SparseIntArray remainingSessions = mChildSessionIds.clone(); 851 final IntentSender childIntentSender = 852 new ChildStatusIntentReceiver(remainingSessions, statusReceiver) 853 .getIntentSender(); 854 RuntimeException commitException = null; 855 boolean commitFailed = false; 856 for (int i = mChildSessionIds.size() - 1; i >= 0; --i) { 857 final int childSessionId = mChildSessionIds.keyAt(i); 858 try { 859 // commit all children, regardless if any of them fail; we'll throw/return 860 // as appropriate once all children have been processed 861 if (!mSessionProvider.getSession(childSessionId) 862 .markAsCommitted(childIntentSender, forTransfer)) { 863 commitFailed = true; 864 } 865 } catch (RuntimeException e) { 866 commitException = e; 867 } 868 } 869 if (commitException != null) { 870 throw commitException; 871 } 872 if (commitFailed) { 873 return; 874 } 875 } 876 mHandler.obtainMessage(MSG_COMMIT).sendToTarget(); 877 } 878 879 private class ChildStatusIntentReceiver { 880 private final SparseIntArray mChildSessionsRemaining; 881 private final IntentSender mStatusReceiver; 882 private final IIntentSender.Stub mLocalSender = new IIntentSender.Stub() { 883 @Override 884 public void send(int code, Intent intent, String resolvedType, IBinder whitelistToken, 885 IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) { 886 statusUpdate(intent); 887 } 888 }; 889 ChildStatusIntentReceiver(SparseIntArray remainingSessions, IntentSender statusReceiver)890 private ChildStatusIntentReceiver(SparseIntArray remainingSessions, 891 IntentSender statusReceiver) { 892 this.mChildSessionsRemaining = remainingSessions; 893 this.mStatusReceiver = statusReceiver; 894 } 895 getIntentSender()896 public IntentSender getIntentSender() { 897 return new IntentSender((IIntentSender) mLocalSender); 898 } 899 statusUpdate(Intent intent)900 public void statusUpdate(Intent intent) { 901 mHandler.post(() -> { 902 if (mChildSessionsRemaining.size() == 0) { 903 return; 904 } 905 final int sessionId = intent.getIntExtra( 906 PackageInstaller.EXTRA_SESSION_ID, 0); 907 final int status = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, 908 PackageInstaller.STATUS_FAILURE); 909 final int sessionIndex = mChildSessionsRemaining.indexOfKey(sessionId); 910 if (PackageInstaller.STATUS_SUCCESS == status) { 911 mChildSessionsRemaining.removeAt(sessionIndex); 912 if (mChildSessionsRemaining.size() == 0) { 913 try { 914 intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, 915 PackageInstallerSession.this.sessionId); 916 mStatusReceiver.sendIntent(mContext, 0, intent, null, null); 917 } catch (IntentSender.SendIntentException ignore) { 918 } 919 } 920 } else if (PackageInstaller.STATUS_PENDING_USER_ACTION == status) { 921 try { 922 mStatusReceiver.sendIntent(mContext, 0, intent, null, null); 923 } catch (IntentSender.SendIntentException ignore) { 924 } 925 } else { 926 intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, 927 PackageInstallerSession.this.sessionId); 928 mChildSessionsRemaining.clear(); // we're done. Don't send any more. 929 try { 930 mStatusReceiver.sendIntent(mContext, 0, intent, null, null); 931 } catch (IntentSender.SendIntentException ignore) { 932 } 933 } 934 }); 935 } 936 } 937 938 939 /** 940 * Do everything but actually commit the session. If this was not already called, the session 941 * will be sealed and marked as committed. The caller of this method is responsible for 942 * subsequently submitting this session for processing. 943 * 944 * This method may be called multiple times to update the status receiver validate caller 945 * permissions. 946 */ markAsCommitted( @onNull IntentSender statusReceiver, boolean forTransfer)947 private boolean markAsCommitted( 948 @NonNull IntentSender statusReceiver, boolean forTransfer) { 949 Preconditions.checkNotNull(statusReceiver); 950 951 List<PackageInstallerSession> childSessions = getChildSessions(); 952 953 final boolean wasSealed; 954 synchronized (mLock) { 955 assertCallerIsOwnerOrRootLocked(); 956 assertPreparedAndNotDestroyedLocked("commit"); 957 958 mRemoteStatusReceiver = statusReceiver; 959 960 if (forTransfer) { 961 mContext.enforceCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES, null); 962 963 if (mInstallerUid == mOriginalInstallerUid) { 964 throw new IllegalArgumentException("Session has not been transferred"); 965 } 966 } else { 967 if (mInstallerUid != mOriginalInstallerUid) { 968 throw new IllegalArgumentException("Session has been transferred"); 969 } 970 } 971 972 // After validations and updating the observer, we can skip re-sealing, etc. because we 973 // have already marked ourselves as committed. 974 if (mCommitted) { 975 return true; 976 } 977 978 wasSealed = mSealed; 979 if (!mSealed) { 980 try { 981 sealAndValidateLocked(childSessions); 982 } catch (PackageManagerException e) { 983 return false; 984 } 985 } 986 987 // Client staging is fully done at this point 988 mClientProgress = 1f; 989 computeProgressLocked(true); 990 991 // This ongoing commit should keep session active, even though client 992 // will probably close their end. 993 mActiveCount.incrementAndGet(); 994 995 mCommitted = true; 996 } 997 998 if (!wasSealed) { 999 // Persist the fact that we've sealed ourselves to prevent 1000 // mutations of any hard links we create. We do this without holding 1001 // the session lock, since otherwise it's a lock inversion. 1002 mCallback.onSessionSealedBlocking(this); 1003 } 1004 return true; 1005 } 1006 1007 /** Return a list of child sessions or null if the session is not multipackage 1008 * 1009 * <p> This method is handy to prevent potential deadlocks (b/123391593) 1010 */ getChildSessions()1011 private @Nullable List<PackageInstallerSession> getChildSessions() { 1012 List<PackageInstallerSession> childSessions = null; 1013 if (isMultiPackage()) { 1014 final int[] childSessionIds = getChildSessionIds(); 1015 childSessions = new ArrayList<>(childSessionIds.length); 1016 for (int childSessionId : childSessionIds) { 1017 childSessions.add(mSessionProvider.getSession(childSessionId)); 1018 } 1019 } 1020 return childSessions; 1021 } 1022 1023 /** 1024 * Assert multipackage install has consistent sessions. 1025 * 1026 * @throws PackageManagerException if child sessions don't match parent session 1027 * in respect to staged and enable rollback parameters. 1028 */ 1029 @GuardedBy("mLock") assertMultiPackageConsistencyLocked( @onNull List<PackageInstallerSession> childSessions)1030 private void assertMultiPackageConsistencyLocked( 1031 @NonNull List<PackageInstallerSession> childSessions) throws PackageManagerException { 1032 for (PackageInstallerSession childSession : childSessions) { 1033 // It might be that the parent session is loaded before all of it's child sessions are, 1034 // e.g. when reading sessions from XML. Those sessions will be null here, and their 1035 // conformance with the multipackage params will be checked when they're loaded. 1036 if (childSession == null) { 1037 continue; 1038 } 1039 assertConsistencyWithLocked(childSession); 1040 } 1041 } 1042 1043 /** 1044 * Assert consistency with the given session. 1045 * 1046 * @throws PackageManagerException if other sessions doesn't match this session 1047 * in respect to staged and enable rollback parameters. 1048 */ 1049 @GuardedBy("mLock") assertConsistencyWithLocked(PackageInstallerSession other)1050 private void assertConsistencyWithLocked(PackageInstallerSession other) 1051 throws PackageManagerException { 1052 // Session groups must be consistent wrt to isStaged parameter. Non-staging session 1053 // cannot be grouped with staging sessions. 1054 if (this.params.isStaged != other.params.isStaged) { 1055 throw new PackageManagerException( 1056 PackageManager.INSTALL_FAILED_MULTIPACKAGE_INCONSISTENCY, 1057 "Multipackage Inconsistency: session " + other.sessionId 1058 + " and session " + sessionId 1059 + " have inconsistent staged settings"); 1060 } 1061 if (this.params.getEnableRollback() != other.params.getEnableRollback()) { 1062 throw new PackageManagerException( 1063 PackageManager.INSTALL_FAILED_MULTIPACKAGE_INCONSISTENCY, 1064 "Multipackage Inconsistency: session " + other.sessionId 1065 + " and session " + sessionId 1066 + " have inconsistent rollback settings"); 1067 } 1068 } 1069 1070 /** 1071 * Seal the session to prevent further modification and validate the contents of it. 1072 * 1073 * <p>The session will be sealed after calling this method even if it failed. 1074 * 1075 * @param childSessions the child sessions of a multipackage that will be checked for 1076 * consistency. Can be null if session is not multipackage. 1077 * @throws PackageManagerException if the session was sealed but something went wrong. If the 1078 * session was sealed this is the only possible exception. 1079 */ 1080 @GuardedBy("mLock") sealAndValidateLocked(List<PackageInstallerSession> childSessions)1081 private void sealAndValidateLocked(List<PackageInstallerSession> childSessions) 1082 throws PackageManagerException { 1083 try { 1084 assertNoWriteFileTransfersOpenLocked(); 1085 assertPreparedAndNotDestroyedLocked("sealing of session"); 1086 1087 mSealed = true; 1088 1089 if (childSessions != null) { 1090 assertMultiPackageConsistencyLocked(childSessions); 1091 } 1092 1093 // Read transfers from the original owner stay open, but as the session's data 1094 // cannot be modified anymore, there is no leak of information. For staged sessions, 1095 // further validation is performed by the staging manager. 1096 if (!params.isMultiPackage) { 1097 final PackageInfo pkgInfo = mPm.getPackageInfo( 1098 params.appPackageName, PackageManager.GET_SIGNATURES 1099 | PackageManager.MATCH_STATIC_SHARED_LIBRARIES /*flags*/, userId); 1100 1101 try { 1102 if ((params.installFlags & PackageManager.INSTALL_APEX) != 0) { 1103 validateApexInstallLocked(); 1104 } else { 1105 validateApkInstallLocked(pkgInfo); 1106 } 1107 } catch (PackageManagerException e) { 1108 throw e; 1109 } catch (Throwable e) { 1110 // Convert all exceptions into package manager exceptions as only those are 1111 // handled in the code above. 1112 throw new PackageManagerException(e); 1113 } 1114 } 1115 1116 if (params.isStaged) { 1117 mStagingManager.checkNonOverlappingWithStagedSessions(this); 1118 } 1119 } catch (PackageManagerException e) { 1120 // Session is sealed but could not be verified, we need to destroy it. 1121 destroyInternal(); 1122 // Dispatch message to remove session from PackageInstallerService 1123 dispatchSessionFinished( 1124 e.error, ExceptionUtils.getCompleteMessage(e), null); 1125 throw e; 1126 } 1127 } 1128 1129 /** 1130 * If session should be sealed, then it's sealed to prevent further modification 1131 * and then it's validated. 1132 * 1133 * If the session was sealed but something went wrong then it's destroyed. 1134 * 1135 * <p> This is meant to be called after all of the sessions are loaded and added to 1136 * PackageInstallerService 1137 */ sealAndValidateIfNecessary()1138 void sealAndValidateIfNecessary() { 1139 synchronized (mLock) { 1140 if (!mShouldBeSealed || isStagedAndInTerminalState()) { 1141 return; 1142 } 1143 } 1144 List<PackageInstallerSession> childSessions = getChildSessions(); 1145 synchronized (mLock) { 1146 try { 1147 sealAndValidateLocked(childSessions); 1148 } catch (PackageManagerException e) { 1149 Slog.e(TAG, "Package not valid", e); 1150 } 1151 } 1152 } 1153 1154 /** Update the timestamp of when the staged session last changed state */ markUpdated()1155 public void markUpdated() { 1156 synchronized (mLock) { 1157 this.updatedMillis = System.currentTimeMillis(); 1158 } 1159 } 1160 1161 @Override transfer(String packageName)1162 public void transfer(String packageName) { 1163 Preconditions.checkNotNull(packageName); 1164 1165 ApplicationInfo newOwnerAppInfo = mPm.getApplicationInfo(packageName, 0, userId); 1166 if (newOwnerAppInfo == null) { 1167 throw new ParcelableException(new PackageManager.NameNotFoundException(packageName)); 1168 } 1169 1170 if (PackageManager.PERMISSION_GRANTED != mPm.checkUidPermission( 1171 Manifest.permission.INSTALL_PACKAGES, newOwnerAppInfo.uid)) { 1172 throw new SecurityException("Destination package " + packageName + " does not have " 1173 + "the " + Manifest.permission.INSTALL_PACKAGES + " permission"); 1174 } 1175 1176 // Only install flags that can be verified by the app the session is transferred to are 1177 // allowed. The parameters can be read via PackageInstaller.SessionInfo. 1178 if (!params.areHiddenOptionsSet()) { 1179 throw new SecurityException("Can only transfer sessions that use public options"); 1180 } 1181 1182 List<PackageInstallerSession> childSessions = getChildSessions(); 1183 1184 synchronized (mLock) { 1185 assertCallerIsOwnerOrRootLocked(); 1186 assertPreparedAndNotSealedLocked("transfer"); 1187 1188 try { 1189 sealAndValidateLocked(childSessions); 1190 } catch (PackageManagerException e) { 1191 throw new IllegalArgumentException("Package is not valid", e); 1192 } 1193 1194 if (!mPackageName.equals(mInstallerPackageName)) { 1195 throw new SecurityException("Can only transfer sessions that update the original " 1196 + "installer"); 1197 } 1198 1199 mInstallerPackageName = packageName; 1200 mInstallerUid = newOwnerAppInfo.uid; 1201 } 1202 1203 // Persist the fact that we've sealed ourselves to prevent 1204 // mutations of any hard links we create. We do this without holding 1205 // the session lock, since otherwise it's a lock inversion. 1206 mCallback.onSessionSealedBlocking(this); 1207 } 1208 handleCommit()1209 private void handleCommit() { 1210 if (isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked()) { 1211 DevicePolicyEventLogger 1212 .createEvent(DevicePolicyEnums.INSTALL_PACKAGE) 1213 .setAdmin(mInstallerPackageName) 1214 .write(); 1215 } 1216 if (params.isStaged) { 1217 mStagingManager.commitSession(this); 1218 destroyInternal(); 1219 dispatchSessionFinished(PackageManager.INSTALL_SUCCEEDED, "Session staged", null); 1220 return; 1221 } 1222 1223 if ((params.installFlags & PackageManager.INSTALL_APEX) != 0) { 1224 destroyInternal(); 1225 dispatchSessionFinished(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, 1226 "APEX packages can only be installed using staged sessions.", null); 1227 return; 1228 } 1229 1230 // For a multiPackage session, read the child sessions 1231 // outside of the lock, because reading the child 1232 // sessions with the lock held could lead to deadlock 1233 // (b/123391593). 1234 List<PackageInstallerSession> childSessions = getChildSessions(); 1235 1236 try { 1237 synchronized (mLock) { 1238 commitNonStagedLocked(childSessions); 1239 } 1240 } catch (PackageManagerException e) { 1241 final String completeMsg = ExceptionUtils.getCompleteMessage(e); 1242 Slog.e(TAG, "Commit of session " + sessionId + " failed: " + completeMsg); 1243 destroyInternal(); 1244 dispatchSessionFinished(e.error, completeMsg, null); 1245 } 1246 } 1247 1248 @GuardedBy("mLock") commitNonStagedLocked(List<PackageInstallerSession> childSessions)1249 private void commitNonStagedLocked(List<PackageInstallerSession> childSessions) 1250 throws PackageManagerException { 1251 final PackageManagerService.ActiveInstallSession committingSession = 1252 makeSessionActiveLocked(); 1253 if (committingSession == null) { 1254 return; 1255 } 1256 if (isMultiPackage()) { 1257 List<PackageManagerService.ActiveInstallSession> activeChildSessions = 1258 new ArrayList<>(childSessions.size()); 1259 boolean success = true; 1260 PackageManagerException failure = null; 1261 for (int i = 0; i < childSessions.size(); ++i) { 1262 final PackageInstallerSession session = childSessions.get(i); 1263 try { 1264 final PackageManagerService.ActiveInstallSession activeSession = 1265 session.makeSessionActiveLocked(); 1266 if (activeSession != null) { 1267 activeChildSessions.add(activeSession); 1268 } 1269 } catch (PackageManagerException e) { 1270 failure = e; 1271 success = false; 1272 } 1273 } 1274 if (!success) { 1275 PackageInstallerService.sendOnPackageInstalled(mContext, 1276 mRemoteStatusReceiver, sessionId, 1277 isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked(), userId, null, 1278 failure.error, failure.getLocalizedMessage(), null); 1279 return; 1280 } 1281 mPm.installStage(activeChildSessions); 1282 } else { 1283 mPm.installStage(committingSession); 1284 } 1285 } 1286 1287 /** 1288 * Stages this session for install and returns a 1289 * {@link PackageManagerService.ActiveInstallSession} representing this new staged state or null 1290 * in case permissions need to be requested before install can proceed. 1291 */ 1292 @GuardedBy("mLock") makeSessionActiveLocked()1293 private PackageManagerService.ActiveInstallSession makeSessionActiveLocked() 1294 throws PackageManagerException { 1295 if (mRelinquished) { 1296 throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, 1297 "Session relinquished"); 1298 } 1299 if (mDestroyed) { 1300 throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session destroyed"); 1301 } 1302 if (!mSealed) { 1303 throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session not sealed"); 1304 } 1305 1306 final IPackageInstallObserver2 localObserver; 1307 if ((params.installFlags & PackageManager.INSTALL_APEX) != 0) { 1308 localObserver = null; 1309 } else { 1310 if (!params.isMultiPackage) { 1311 Preconditions.checkNotNull(mPackageName); 1312 Preconditions.checkNotNull(mSigningDetails); 1313 Preconditions.checkNotNull(mResolvedBaseFile); 1314 1315 if (needToAskForPermissionsLocked()) { 1316 // User needs to confirm installation; 1317 // give installer an intent they can use to involve 1318 // user. 1319 final Intent intent = new Intent(PackageInstaller.ACTION_CONFIRM_INSTALL); 1320 intent.setPackage(mPm.getPackageInstallerPackageName()); 1321 intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId); 1322 1323 PackageInstallerService.sendOnUserActionRequired(mContext, 1324 mRemoteStatusReceiver, sessionId, intent); 1325 1326 // Commit was keeping session marked as active until now; release 1327 // that extra refcount so session appears idle. 1328 closeInternal(false); 1329 return null; 1330 } 1331 1332 // Inherit any packages and native libraries from existing install that 1333 // haven't been overridden. 1334 if (params.mode == SessionParams.MODE_INHERIT_EXISTING) { 1335 try { 1336 final List<File> fromFiles = mResolvedInheritedFiles; 1337 final File toDir = stageDir; 1338 1339 if (LOGD) Slog.d(TAG, "Inherited files: " + mResolvedInheritedFiles); 1340 if (!mResolvedInheritedFiles.isEmpty() && mInheritedFilesBase == null) { 1341 throw new IllegalStateException("mInheritedFilesBase == null"); 1342 } 1343 1344 if (isLinkPossible(fromFiles, toDir)) { 1345 if (!mResolvedInstructionSets.isEmpty()) { 1346 final File oatDir = new File(toDir, "oat"); 1347 createOatDirs(mResolvedInstructionSets, oatDir); 1348 } 1349 // pre-create lib dirs for linking if necessary 1350 if (!mResolvedNativeLibPaths.isEmpty()) { 1351 for (String libPath : mResolvedNativeLibPaths) { 1352 // "/lib/arm64" -> ["lib", "arm64"] 1353 final int splitIndex = libPath.lastIndexOf('/'); 1354 if (splitIndex < 0 || splitIndex >= libPath.length() - 1) { 1355 Slog.e(TAG, 1356 "Skipping native library creation for linking due" 1357 + " to invalid path: " + libPath); 1358 continue; 1359 } 1360 final String libDirPath = libPath.substring(1, splitIndex); 1361 final File libDir = new File(toDir, libDirPath); 1362 if (!libDir.exists()) { 1363 NativeLibraryHelper.createNativeLibrarySubdir(libDir); 1364 } 1365 final String archDirPath = libPath.substring(splitIndex + 1); 1366 NativeLibraryHelper.createNativeLibrarySubdir( 1367 new File(libDir, archDirPath)); 1368 } 1369 } 1370 linkFiles(fromFiles, toDir, mInheritedFilesBase); 1371 } else { 1372 // TODO: this should delegate to DCS so the system process 1373 // avoids holding open FDs into containers. 1374 copyFiles(fromFiles, toDir); 1375 } 1376 } catch (IOException e) { 1377 throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE, 1378 "Failed to inherit existing install", e); 1379 } 1380 } 1381 1382 // TODO: surface more granular state from dexopt 1383 mInternalProgress = 0.5f; 1384 computeProgressLocked(true); 1385 1386 // Unpack native libraries 1387 extractNativeLibraries(stageDir, params.abiOverride, mayInheritNativeLibs()); 1388 } 1389 1390 // We've reached point of no return; call into PMS to install the stage. 1391 // Regardless of success or failure we always destroy session. 1392 localObserver = new IPackageInstallObserver2.Stub() { 1393 @Override 1394 public void onUserActionRequired(Intent intent) { 1395 throw new IllegalStateException(); 1396 } 1397 1398 @Override 1399 public void onPackageInstalled(String basePackageName, int returnCode, String msg, 1400 Bundle extras) { 1401 destroyInternal(); 1402 dispatchSessionFinished(returnCode, msg, extras); 1403 } 1404 }; 1405 } 1406 1407 final UserHandle user; 1408 if ((params.installFlags & PackageManager.INSTALL_ALL_USERS) != 0) { 1409 user = UserHandle.ALL; 1410 } else { 1411 user = new UserHandle(userId); 1412 } 1413 1414 mRelinquished = true; 1415 return new PackageManagerService.ActiveInstallSession(mPackageName, stageDir, 1416 localObserver, params, mInstallerPackageName, mInstallerUid, user, 1417 mSigningDetails); 1418 } 1419 maybeRenameFile(File from, File to)1420 private static void maybeRenameFile(File from, File to) throws PackageManagerException { 1421 if (!from.equals(to)) { 1422 if (!from.renameTo(to)) { 1423 throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, 1424 "Could not rename file " + from + " to " + to); 1425 } 1426 } 1427 } 1428 1429 /** 1430 * Returns true if the session should attempt to inherit any existing native libraries already 1431 * extracted at the current install location. This is necessary to prevent double loading of 1432 * native libraries already loaded by the running app. 1433 */ mayInheritNativeLibs()1434 private boolean mayInheritNativeLibs() { 1435 return SystemProperties.getBoolean(PROPERTY_NAME_INHERIT_NATIVE, true) && 1436 params.mode == SessionParams.MODE_INHERIT_EXISTING && 1437 (params.installFlags & PackageManager.DONT_KILL_APP) != 0; 1438 } 1439 1440 /** 1441 * Validate apex install. 1442 * <p> 1443 * Sets {@link #mResolvedBaseFile} for RollbackManager to use. Sets {@link #mPackageName} for 1444 * StagingManager to use. 1445 */ 1446 @GuardedBy("mLock") validateApexInstallLocked()1447 private void validateApexInstallLocked() 1448 throws PackageManagerException { 1449 final File[] addedFiles = getAddedFilesLocked(); 1450 if (ArrayUtils.isEmpty(addedFiles)) { 1451 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "No packages staged"); 1452 } 1453 1454 if (ArrayUtils.size(addedFiles) > 1) { 1455 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 1456 "Too many files for apex install"); 1457 } 1458 1459 File addedFile = addedFiles[0]; // there is only one file 1460 1461 // Ensure file name has proper suffix 1462 final String sourceName = addedFile.getName(); 1463 final String targetName = sourceName.endsWith(APEX_FILE_EXTENSION) 1464 ? sourceName 1465 : sourceName + APEX_FILE_EXTENSION; 1466 if (!FileUtils.isValidExtFilename(targetName)) { 1467 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 1468 "Invalid filename: " + targetName); 1469 } 1470 1471 final File targetFile = new File(stageDir, targetName); 1472 resolveAndStageFile(addedFile, targetFile); 1473 mResolvedBaseFile = targetFile; 1474 1475 // Populate package name of the apex session 1476 mPackageName = null; 1477 final ApkLite apk; 1478 try { 1479 apk = PackageParser.parseApkLite( 1480 mResolvedBaseFile, PackageParser.PARSE_COLLECT_CERTIFICATES); 1481 } catch (PackageParserException e) { 1482 throw PackageManagerException.from(e); 1483 } 1484 1485 if (mPackageName == null) { 1486 mPackageName = apk.packageName; 1487 mVersionCode = apk.getLongVersionCode(); 1488 } 1489 } 1490 1491 /** 1492 * Validate install by confirming that all application packages are have 1493 * consistent package name, version code, and signing certificates. 1494 * <p> 1495 * Clears and populates {@link #mResolvedBaseFile}, 1496 * {@link #mResolvedStagedFiles}, and {@link #mResolvedInheritedFiles}. 1497 * <p> 1498 * Renames package files in stage to match split names defined inside. 1499 * <p> 1500 * Note that upgrade compatibility is still performed by 1501 * {@link PackageManagerService}. 1502 */ 1503 @GuardedBy("mLock") validateApkInstallLocked(@ullable PackageInfo pkgInfo)1504 private void validateApkInstallLocked(@Nullable PackageInfo pkgInfo) 1505 throws PackageManagerException { 1506 ApkLite baseApk = null; 1507 mPackageName = null; 1508 mVersionCode = -1; 1509 mSigningDetails = PackageParser.SigningDetails.UNKNOWN; 1510 1511 mResolvedBaseFile = null; 1512 mResolvedStagedFiles.clear(); 1513 mResolvedInheritedFiles.clear(); 1514 1515 // Partial installs must be consistent with existing install 1516 if (params.mode == SessionParams.MODE_INHERIT_EXISTING 1517 && (pkgInfo == null || pkgInfo.applicationInfo == null)) { 1518 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 1519 "Missing existing base package"); 1520 } 1521 // Default to require only if existing base has fs-verity. 1522 mVerityFound = PackageManagerServiceUtils.isApkVerityEnabled() 1523 && params.mode == SessionParams.MODE_INHERIT_EXISTING 1524 && VerityUtils.hasFsverity(pkgInfo.applicationInfo.getBaseCodePath()); 1525 1526 final File[] removedFiles = getRemovedFilesLocked(); 1527 final List<String> removeSplitList = new ArrayList<>(); 1528 if (!ArrayUtils.isEmpty(removedFiles)) { 1529 for (File removedFile : removedFiles) { 1530 final String fileName = removedFile.getName(); 1531 final String splitName = fileName.substring( 1532 0, fileName.length() - REMOVE_MARKER_EXTENSION.length()); 1533 removeSplitList.add(splitName); 1534 } 1535 } 1536 1537 final File[] addedFiles = getAddedFilesLocked(); 1538 if (ArrayUtils.isEmpty(addedFiles) && removeSplitList.size() == 0) { 1539 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "No packages staged"); 1540 } 1541 1542 // Verify that all staged packages are internally consistent 1543 final ArraySet<String> stagedSplits = new ArraySet<>(); 1544 for (File addedFile : addedFiles) { 1545 final ApkLite apk; 1546 try { 1547 apk = PackageParser.parseApkLite( 1548 addedFile, PackageParser.PARSE_COLLECT_CERTIFICATES); 1549 } catch (PackageParserException e) { 1550 throw PackageManagerException.from(e); 1551 } 1552 1553 if (!stagedSplits.add(apk.splitName)) { 1554 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 1555 "Split " + apk.splitName + " was defined multiple times"); 1556 } 1557 1558 // Use first package to define unknown values 1559 if (mPackageName == null) { 1560 mPackageName = apk.packageName; 1561 mVersionCode = apk.getLongVersionCode(); 1562 } 1563 if (mSigningDetails == PackageParser.SigningDetails.UNKNOWN) { 1564 mSigningDetails = apk.signingDetails; 1565 } 1566 1567 assertApkConsistentLocked(String.valueOf(addedFile), apk); 1568 1569 // Take this opportunity to enforce uniform naming 1570 final String targetName; 1571 if (apk.splitName == null) { 1572 targetName = "base" + APK_FILE_EXTENSION; 1573 } else { 1574 targetName = "split_" + apk.splitName + APK_FILE_EXTENSION; 1575 } 1576 if (!FileUtils.isValidExtFilename(targetName)) { 1577 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 1578 "Invalid filename: " + targetName); 1579 } 1580 1581 final File targetFile = new File(stageDir, targetName); 1582 resolveAndStageFile(addedFile, targetFile); 1583 1584 // Base is coming from session 1585 if (apk.splitName == null) { 1586 mResolvedBaseFile = targetFile; 1587 baseApk = apk; 1588 } 1589 1590 final File dexMetadataFile = DexMetadataHelper.findDexMetadataForFile(addedFile); 1591 if (dexMetadataFile != null) { 1592 if (!FileUtils.isValidExtFilename(dexMetadataFile.getName())) { 1593 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 1594 "Invalid filename: " + dexMetadataFile); 1595 } 1596 final File targetDexMetadataFile = new File(stageDir, 1597 DexMetadataHelper.buildDexMetadataPathForApk(targetName)); 1598 resolveAndStageFile(dexMetadataFile, targetDexMetadataFile); 1599 } 1600 } 1601 1602 if (removeSplitList.size() > 0) { 1603 if (pkgInfo == null) { 1604 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 1605 "Missing existing base package for " + mPackageName); 1606 } 1607 1608 // validate split names marked for removal 1609 for (String splitName : removeSplitList) { 1610 if (!ArrayUtils.contains(pkgInfo.splitNames, splitName)) { 1611 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 1612 "Split not found: " + splitName); 1613 } 1614 } 1615 1616 // ensure we've got appropriate package name, version code and signatures 1617 if (mPackageName == null) { 1618 mPackageName = pkgInfo.packageName; 1619 mVersionCode = pkgInfo.getLongVersionCode(); 1620 } 1621 if (mSigningDetails == PackageParser.SigningDetails.UNKNOWN) { 1622 try { 1623 mSigningDetails = ApkSignatureVerifier.unsafeGetCertsWithoutVerification( 1624 pkgInfo.applicationInfo.sourceDir, 1625 PackageParser.SigningDetails.SignatureSchemeVersion.JAR); 1626 } catch (PackageParserException e) { 1627 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 1628 "Couldn't obtain signatures from base APK"); 1629 } 1630 } 1631 } 1632 1633 if (params.mode == SessionParams.MODE_FULL_INSTALL) { 1634 // Full installs must include a base package 1635 if (!stagedSplits.contains(null)) { 1636 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 1637 "Full install must include a base package"); 1638 } 1639 1640 } else { 1641 final PackageLite existing; 1642 final ApkLite existingBase; 1643 ApplicationInfo appInfo = pkgInfo.applicationInfo; 1644 try { 1645 existing = PackageParser.parsePackageLite(new File(appInfo.getCodePath()), 0); 1646 existingBase = PackageParser.parseApkLite(new File(appInfo.getBaseCodePath()), 1647 PackageParser.PARSE_COLLECT_CERTIFICATES); 1648 } catch (PackageParserException e) { 1649 throw PackageManagerException.from(e); 1650 } 1651 1652 assertApkConsistentLocked("Existing base", existingBase); 1653 1654 // Inherit base if not overridden 1655 if (mResolvedBaseFile == null) { 1656 mResolvedBaseFile = new File(appInfo.getBaseCodePath()); 1657 resolveInheritedFile(mResolvedBaseFile); 1658 // Inherit the dex metadata if present. 1659 final File baseDexMetadataFile = 1660 DexMetadataHelper.findDexMetadataForFile(mResolvedBaseFile); 1661 if (baseDexMetadataFile != null) { 1662 resolveInheritedFile(baseDexMetadataFile); 1663 } 1664 baseApk = existingBase; 1665 } 1666 1667 // Inherit splits if not overridden 1668 if (!ArrayUtils.isEmpty(existing.splitNames)) { 1669 for (int i = 0; i < existing.splitNames.length; i++) { 1670 final String splitName = existing.splitNames[i]; 1671 final File splitFile = new File(existing.splitCodePaths[i]); 1672 final boolean splitRemoved = removeSplitList.contains(splitName); 1673 if (!stagedSplits.contains(splitName) && !splitRemoved) { 1674 resolveInheritedFile(splitFile); 1675 // Inherit the dex metadata if present. 1676 final File splitDexMetadataFile = 1677 DexMetadataHelper.findDexMetadataForFile(splitFile); 1678 if (splitDexMetadataFile != null) { 1679 resolveInheritedFile(splitDexMetadataFile); 1680 } 1681 } 1682 } 1683 } 1684 1685 // Inherit compiled oat directory. 1686 final File packageInstallDir = (new File(appInfo.getBaseCodePath())).getParentFile(); 1687 mInheritedFilesBase = packageInstallDir; 1688 final File oatDir = new File(packageInstallDir, "oat"); 1689 if (oatDir.exists()) { 1690 final File[] archSubdirs = oatDir.listFiles(); 1691 1692 // Keep track of all instruction sets we've seen compiled output for. 1693 // If we're linking (and not copying) inherited files, we can recreate the 1694 // instruction set hierarchy and link compiled output. 1695 if (archSubdirs != null && archSubdirs.length > 0) { 1696 final String[] instructionSets = InstructionSets.getAllDexCodeInstructionSets(); 1697 for (File archSubDir : archSubdirs) { 1698 // Skip any directory that isn't an ISA subdir. 1699 if (!ArrayUtils.contains(instructionSets, archSubDir.getName())) { 1700 continue; 1701 } 1702 1703 mResolvedInstructionSets.add(archSubDir.getName()); 1704 List<File> oatFiles = Arrays.asList(archSubDir.listFiles()); 1705 if (!oatFiles.isEmpty()) { 1706 mResolvedInheritedFiles.addAll(oatFiles); 1707 } 1708 } 1709 } 1710 } 1711 1712 // Inherit native libraries for DONT_KILL sessions. 1713 if (mayInheritNativeLibs() && removeSplitList.isEmpty()) { 1714 File[] libDirs = new File[]{ 1715 new File(packageInstallDir, NativeLibraryHelper.LIB_DIR_NAME), 1716 new File(packageInstallDir, NativeLibraryHelper.LIB64_DIR_NAME)}; 1717 for (File libDir : libDirs) { 1718 if (!libDir.exists() || !libDir.isDirectory()) { 1719 continue; 1720 } 1721 final List<File> libDirsToInherit = new LinkedList<>(); 1722 for (File archSubDir : libDir.listFiles()) { 1723 if (!archSubDir.isDirectory()) { 1724 continue; 1725 } 1726 String relLibPath; 1727 try { 1728 relLibPath = getRelativePath(archSubDir, packageInstallDir); 1729 } catch (IOException e) { 1730 Slog.e(TAG, "Skipping linking of native library directory!", e); 1731 // shouldn't be possible, but let's avoid inheriting these to be safe 1732 libDirsToInherit.clear(); 1733 break; 1734 } 1735 if (!mResolvedNativeLibPaths.contains(relLibPath)) { 1736 mResolvedNativeLibPaths.add(relLibPath); 1737 } 1738 libDirsToInherit.addAll(Arrays.asList(archSubDir.listFiles())); 1739 } 1740 mResolvedInheritedFiles.addAll(libDirsToInherit); 1741 } 1742 } 1743 } 1744 if (baseApk.useEmbeddedDex) { 1745 for (File file : mResolvedStagedFiles) { 1746 if (file.getName().endsWith(".apk") 1747 && !DexManager.auditUncompressedDexInApk(file.getPath())) { 1748 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 1749 "Some dex are not uncompressed and aligned correctly for " 1750 + mPackageName); 1751 } 1752 } 1753 } 1754 if (baseApk.isSplitRequired && stagedSplits.size() <= 1) { 1755 throw new PackageManagerException(INSTALL_FAILED_MISSING_SPLIT, 1756 "Missing split for " + mPackageName); 1757 } 1758 } 1759 resolveAndStageFile(File origFile, File targetFile)1760 private void resolveAndStageFile(File origFile, File targetFile) 1761 throws PackageManagerException { 1762 mResolvedStagedFiles.add(targetFile); 1763 maybeRenameFile(origFile, targetFile); 1764 1765 final File originalSignature = new File( 1766 VerityUtils.getFsveritySignatureFilePath(origFile.getPath())); 1767 // Make sure .fsv_sig exists when it should, then resolve and stage it. 1768 if (originalSignature.exists()) { 1769 // mVerityFound can only change from false to true here during the staging loop. Since 1770 // all or none of files should have .fsv_sig, this should only happen in the first time 1771 // (or never), otherwise bail out. 1772 if (!mVerityFound) { 1773 mVerityFound = true; 1774 if (mResolvedStagedFiles.size() > 1) { 1775 throw new PackageManagerException(INSTALL_FAILED_BAD_SIGNATURE, 1776 "Some file is missing fs-verity signature"); 1777 } 1778 } 1779 } else { 1780 if (!mVerityFound) { 1781 return; 1782 } 1783 throw new PackageManagerException(INSTALL_FAILED_BAD_SIGNATURE, 1784 "Missing corresponding fs-verity signature to " + origFile); 1785 } 1786 1787 final File stagedSignature = new File( 1788 VerityUtils.getFsveritySignatureFilePath(targetFile.getPath())); 1789 maybeRenameFile(originalSignature, stagedSignature); 1790 mResolvedStagedFiles.add(stagedSignature); 1791 } 1792 resolveInheritedFile(File origFile)1793 private void resolveInheritedFile(File origFile) { 1794 mResolvedInheritedFiles.add(origFile); 1795 1796 // Inherit the fsverity signature file if present. 1797 final File fsveritySignatureFile = new File( 1798 VerityUtils.getFsveritySignatureFilePath(origFile.getPath())); 1799 if (fsveritySignatureFile.exists()) { 1800 mResolvedInheritedFiles.add(fsveritySignatureFile); 1801 } 1802 } 1803 1804 @GuardedBy("mLock") assertApkConsistentLocked(String tag, ApkLite apk)1805 private void assertApkConsistentLocked(String tag, ApkLite apk) 1806 throws PackageManagerException { 1807 if (!mPackageName.equals(apk.packageName)) { 1808 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag + " package " 1809 + apk.packageName + " inconsistent with " + mPackageName); 1810 } 1811 if (params.appPackageName != null && !params.appPackageName.equals(apk.packageName)) { 1812 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag 1813 + " specified package " + params.appPackageName 1814 + " inconsistent with " + apk.packageName); 1815 } 1816 if (mVersionCode != apk.getLongVersionCode()) { 1817 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag 1818 + " version code " + apk.versionCode + " inconsistent with " 1819 + mVersionCode); 1820 } 1821 if (!mSigningDetails.signaturesMatchExactly(apk.signingDetails)) { 1822 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 1823 tag + " signatures are inconsistent"); 1824 } 1825 } 1826 1827 /** 1828 * Determine if creating hard links between source and destination is 1829 * possible. That is, do they all live on the same underlying device. 1830 */ isLinkPossible(List<File> fromFiles, File toDir)1831 private boolean isLinkPossible(List<File> fromFiles, File toDir) { 1832 try { 1833 final StructStat toStat = Os.stat(toDir.getAbsolutePath()); 1834 for (File fromFile : fromFiles) { 1835 final StructStat fromStat = Os.stat(fromFile.getAbsolutePath()); 1836 if (fromStat.st_dev != toStat.st_dev) { 1837 return false; 1838 } 1839 } 1840 } catch (ErrnoException e) { 1841 Slog.w(TAG, "Failed to detect if linking possible: " + e); 1842 return false; 1843 } 1844 return true; 1845 } 1846 1847 /** 1848 * @return the uid of the owner this session 1849 */ getInstallerUid()1850 public int getInstallerUid() { 1851 synchronized (mLock) { 1852 return mInstallerUid; 1853 } 1854 } 1855 1856 /** 1857 * @return the package name of this session 1858 */ getPackageName()1859 String getPackageName() { 1860 synchronized (mLock) { 1861 return mPackageName; 1862 } 1863 } 1864 1865 /** 1866 * @return the timestamp of when this session last changed state 1867 */ getUpdatedMillis()1868 public long getUpdatedMillis() { 1869 synchronized (mLock) { 1870 return updatedMillis; 1871 } 1872 } 1873 getInstallerPackageName()1874 String getInstallerPackageName() { 1875 synchronized (mLock) { 1876 return mInstallerPackageName; 1877 } 1878 } 1879 getRelativePath(File file, File base)1880 private static String getRelativePath(File file, File base) throws IOException { 1881 final String pathStr = file.getAbsolutePath(); 1882 final String baseStr = base.getAbsolutePath(); 1883 // Don't allow relative paths. 1884 if (pathStr.contains("/.") ) { 1885 throw new IOException("Invalid path (was relative) : " + pathStr); 1886 } 1887 1888 if (pathStr.startsWith(baseStr)) { 1889 return pathStr.substring(baseStr.length()); 1890 } 1891 1892 throw new IOException("File: " + pathStr + " outside base: " + baseStr); 1893 } 1894 createOatDirs(List<String> instructionSets, File fromDir)1895 private void createOatDirs(List<String> instructionSets, File fromDir) 1896 throws PackageManagerException { 1897 for (String instructionSet : instructionSets) { 1898 try { 1899 mPm.mInstaller.createOatDir(fromDir.getAbsolutePath(), instructionSet); 1900 } catch (InstallerException e) { 1901 throw PackageManagerException.from(e); 1902 } 1903 } 1904 } 1905 linkFiles(List<File> fromFiles, File toDir, File fromDir)1906 private void linkFiles(List<File> fromFiles, File toDir, File fromDir) 1907 throws IOException { 1908 for (File fromFile : fromFiles) { 1909 final String relativePath = getRelativePath(fromFile, fromDir); 1910 try { 1911 mPm.mInstaller.linkFile(relativePath, fromDir.getAbsolutePath(), 1912 toDir.getAbsolutePath()); 1913 } catch (InstallerException e) { 1914 throw new IOException("failed linkOrCreateDir(" + relativePath + ", " 1915 + fromDir + ", " + toDir + ")", e); 1916 } 1917 } 1918 1919 Slog.d(TAG, "Linked " + fromFiles.size() + " files into " + toDir); 1920 } 1921 copyFiles(List<File> fromFiles, File toDir)1922 private static void copyFiles(List<File> fromFiles, File toDir) throws IOException { 1923 // Remove any partial files from previous attempt 1924 for (File file : toDir.listFiles()) { 1925 if (file.getName().endsWith(".tmp")) { 1926 file.delete(); 1927 } 1928 } 1929 1930 for (File fromFile : fromFiles) { 1931 final File tmpFile = File.createTempFile("inherit", ".tmp", toDir); 1932 if (LOGD) Slog.d(TAG, "Copying " + fromFile + " to " + tmpFile); 1933 if (!FileUtils.copyFile(fromFile, tmpFile)) { 1934 throw new IOException("Failed to copy " + fromFile + " to " + tmpFile); 1935 } 1936 try { 1937 Os.chmod(tmpFile.getAbsolutePath(), 0644); 1938 } catch (ErrnoException e) { 1939 throw new IOException("Failed to chmod " + tmpFile); 1940 } 1941 final File toFile = new File(toDir, fromFile.getName()); 1942 if (LOGD) Slog.d(TAG, "Renaming " + tmpFile + " to " + toFile); 1943 if (!tmpFile.renameTo(toFile)) { 1944 throw new IOException("Failed to rename " + tmpFile + " to " + toFile); 1945 } 1946 } 1947 Slog.d(TAG, "Copied " + fromFiles.size() + " files into " + toDir); 1948 } 1949 extractNativeLibraries(File packageDir, String abiOverride, boolean inherit)1950 private static void extractNativeLibraries(File packageDir, String abiOverride, boolean inherit) 1951 throws PackageManagerException { 1952 final File libDir = new File(packageDir, NativeLibraryHelper.LIB_DIR_NAME); 1953 if (!inherit) { 1954 // Start from a clean slate 1955 NativeLibraryHelper.removeNativeBinariesFromDirLI(libDir, true); 1956 } 1957 1958 NativeLibraryHelper.Handle handle = null; 1959 try { 1960 handle = NativeLibraryHelper.Handle.create(packageDir); 1961 final int res = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libDir, 1962 abiOverride); 1963 if (res != PackageManager.INSTALL_SUCCEEDED) { 1964 throw new PackageManagerException(res, 1965 "Failed to extract native libraries, res=" + res); 1966 } 1967 } catch (IOException e) { 1968 throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, 1969 "Failed to extract native libraries", e); 1970 } finally { 1971 IoUtils.closeQuietly(handle); 1972 } 1973 } 1974 setPermissionsResult(boolean accepted)1975 void setPermissionsResult(boolean accepted) { 1976 if (!mSealed) { 1977 throw new SecurityException("Must be sealed to accept permissions"); 1978 } 1979 1980 if (accepted) { 1981 // Mark and kick off another install pass 1982 synchronized (mLock) { 1983 mPermissionsManuallyAccepted = true; 1984 mHandler.obtainMessage(MSG_COMMIT).sendToTarget(); 1985 } 1986 } else { 1987 destroyInternal(); 1988 dispatchSessionFinished(INSTALL_FAILED_ABORTED, "User rejected permissions", null); 1989 } 1990 } 1991 1992 /** 1993 * Adds a child session ID without any safety / sanity checks. This should only be used to 1994 * build a session from XML or similar. 1995 */ addChildSessionIdInternal(int sessionId)1996 void addChildSessionIdInternal(int sessionId) { 1997 mChildSessionIds.put(sessionId, 0); 1998 } 1999 open()2000 public void open() throws IOException { 2001 if (mActiveCount.getAndIncrement() == 0) { 2002 mCallback.onSessionActiveChanged(this, true); 2003 } 2004 2005 boolean wasPrepared; 2006 synchronized (mLock) { 2007 wasPrepared = mPrepared; 2008 if (!mPrepared) { 2009 if (stageDir != null) { 2010 prepareStageDir(stageDir); 2011 } else if (params.isMultiPackage) { 2012 // it's all ok 2013 } else { 2014 throw new IllegalArgumentException("stageDir must be set"); 2015 } 2016 2017 mPrepared = true; 2018 } 2019 } 2020 2021 if (!wasPrepared) { 2022 mCallback.onSessionPrepared(this); 2023 } 2024 } 2025 2026 @Override close()2027 public void close() { 2028 closeInternal(true); 2029 } 2030 closeInternal(boolean checkCaller)2031 private void closeInternal(boolean checkCaller) { 2032 int activeCount; 2033 synchronized (mLock) { 2034 if (checkCaller) { 2035 assertCallerIsOwnerOrRootLocked(); 2036 } 2037 2038 activeCount = mActiveCount.decrementAndGet(); 2039 } 2040 2041 if (activeCount == 0) { 2042 mCallback.onSessionActiveChanged(this, false); 2043 } 2044 } 2045 2046 @Override abandon()2047 public void abandon() { 2048 if (hasParentSessionId()) { 2049 throw new IllegalStateException( 2050 "Session " + sessionId + " is a child of multi-package session " 2051 + mParentSessionId + " and may not be abandoned directly."); 2052 } 2053 synchronized (mLock) { 2054 assertCallerIsOwnerOrRootLocked(); 2055 2056 if (isStagedAndInTerminalState()) { 2057 // We keep the session in the database if it's in a finalized state. It will be 2058 // removed by PackageInstallerService when the last update time is old enough. 2059 // Also, in such cases cleanStageDir() has already been executed so no need to 2060 // do it now. 2061 return; 2062 } 2063 if (mCommitted && params.isStaged) { 2064 synchronized (mLock) { 2065 mDestroyed = true; 2066 } 2067 mStagingManager.abortCommittedSession(this); 2068 2069 cleanStageDir(); 2070 } 2071 2072 if (mRelinquished) { 2073 Slog.d(TAG, "Ignoring abandon after commit relinquished control"); 2074 return; 2075 } 2076 destroyInternal(); 2077 } 2078 2079 dispatchSessionFinished(INSTALL_FAILED_ABORTED, "Session was abandoned", null); 2080 } 2081 2082 @Override isMultiPackage()2083 public boolean isMultiPackage() { 2084 return params.isMultiPackage; 2085 } 2086 2087 @Override isStaged()2088 public boolean isStaged() { 2089 return params.isStaged; 2090 } 2091 2092 @Override getChildSessionIds()2093 public int[] getChildSessionIds() { 2094 final int[] childSessionIds = mChildSessionIds.copyKeys(); 2095 if (childSessionIds != null) { 2096 return childSessionIds; 2097 } 2098 return EMPTY_CHILD_SESSION_ARRAY; 2099 } 2100 2101 @Override addChildSessionId(int childSessionId)2102 public void addChildSessionId(int childSessionId) { 2103 final PackageInstallerSession childSession = mSessionProvider.getSession(childSessionId); 2104 if (childSession == null 2105 || (childSession.hasParentSessionId() && childSession.mParentSessionId != sessionId) 2106 || childSession.mCommitted 2107 || childSession.mDestroyed) { 2108 throw new IllegalStateException("Unable to add child session " + childSessionId 2109 + " as it does not exist or is in an invalid state."); 2110 } 2111 synchronized (mLock) { 2112 assertCallerIsOwnerOrRootLocked(); 2113 assertPreparedAndNotSealedLocked("addChildSessionId"); 2114 2115 final int indexOfSession = mChildSessionIds.indexOfKey(childSessionId); 2116 if (indexOfSession >= 0) { 2117 return; 2118 } 2119 childSession.setParentSessionId(this.sessionId); 2120 addChildSessionIdInternal(childSessionId); 2121 } 2122 } 2123 2124 @Override removeChildSessionId(int sessionId)2125 public void removeChildSessionId(int sessionId) { 2126 final PackageInstallerSession session = mSessionProvider.getSession(sessionId); 2127 synchronized (mLock) { 2128 final int indexOfSession = mChildSessionIds.indexOfKey(sessionId); 2129 if (session != null) { 2130 session.setParentSessionId(SessionInfo.INVALID_ID); 2131 } 2132 if (indexOfSession < 0) { 2133 // not added in the first place; no-op 2134 return; 2135 } 2136 mChildSessionIds.removeAt(indexOfSession); 2137 } 2138 } 2139 2140 /** 2141 * Sets the parent session ID if not already set. 2142 * If {@link SessionInfo#INVALID_ID} is passed, it will be unset. 2143 */ setParentSessionId(int parentSessionId)2144 void setParentSessionId(int parentSessionId) { 2145 synchronized (mLock) { 2146 if (parentSessionId != SessionInfo.INVALID_ID 2147 && mParentSessionId != SessionInfo.INVALID_ID) { 2148 throw new IllegalStateException("The parent of " + sessionId + " is" + " already" 2149 + "set to " + mParentSessionId); 2150 } 2151 this.mParentSessionId = parentSessionId; 2152 } 2153 } 2154 hasParentSessionId()2155 boolean hasParentSessionId() { 2156 return mParentSessionId != SessionInfo.INVALID_ID; 2157 } 2158 2159 @Override getParentSessionId()2160 public int getParentSessionId() { 2161 return mParentSessionId; 2162 } 2163 dispatchSessionFinished(int returnCode, String msg, Bundle extras)2164 private void dispatchSessionFinished(int returnCode, String msg, Bundle extras) { 2165 final IntentSender statusReceiver; 2166 final String packageName; 2167 synchronized (mLock) { 2168 mFinalStatus = returnCode; 2169 mFinalMessage = msg; 2170 2171 statusReceiver = mRemoteStatusReceiver; 2172 packageName = mPackageName; 2173 } 2174 2175 if (statusReceiver != null) { 2176 // Execute observer.onPackageInstalled on different tread as we don't want callers 2177 // inside the system server have to worry about catching the callbacks while they are 2178 // calling into the session 2179 final SomeArgs args = SomeArgs.obtain(); 2180 args.arg1 = packageName; 2181 args.arg2 = msg; 2182 args.arg3 = extras; 2183 args.arg4 = statusReceiver; 2184 args.argi1 = returnCode; 2185 2186 mHandler.obtainMessage(MSG_ON_PACKAGE_INSTALLED, args).sendToTarget(); 2187 } 2188 2189 final boolean success = (returnCode == PackageManager.INSTALL_SUCCEEDED); 2190 2191 // Send broadcast to default launcher only if it's a new install 2192 // TODO(b/144270665): Secure the usage of this broadcast. 2193 final boolean isNewInstall = extras == null || !extras.getBoolean(Intent.EXTRA_REPLACING); 2194 if (success && isNewInstall && mPm.mInstallerService.okToSendBroadcasts() 2195 && (params.installFlags & PackageManager.INSTALL_DRY_RUN) == 0) { 2196 mPm.sendSessionCommitBroadcast(generateInfo(), userId); 2197 } 2198 2199 mCallback.onSessionFinished(this, success); 2200 } 2201 2202 /** {@hide} */ setStagedSessionReady()2203 void setStagedSessionReady() { 2204 synchronized (mLock) { 2205 mStagedSessionReady = true; 2206 mStagedSessionApplied = false; 2207 mStagedSessionFailed = false; 2208 mStagedSessionErrorCode = SessionInfo.STAGED_SESSION_NO_ERROR; 2209 mStagedSessionErrorMessage = ""; 2210 } 2211 mCallback.onStagedSessionChanged(this); 2212 } 2213 2214 /** {@hide} */ setStagedSessionFailed(@tagedSessionErrorCode int errorCode, String errorMessage)2215 void setStagedSessionFailed(@StagedSessionErrorCode int errorCode, 2216 String errorMessage) { 2217 synchronized (mLock) { 2218 mStagedSessionReady = false; 2219 mStagedSessionApplied = false; 2220 mStagedSessionFailed = true; 2221 mStagedSessionErrorCode = errorCode; 2222 mStagedSessionErrorMessage = errorMessage; 2223 Slog.d(TAG, "Marking session " + sessionId + " as failed: " + errorMessage); 2224 } 2225 cleanStageDir(); 2226 mCallback.onStagedSessionChanged(this); 2227 } 2228 2229 /** {@hide} */ setStagedSessionApplied()2230 void setStagedSessionApplied() { 2231 synchronized (mLock) { 2232 mStagedSessionReady = false; 2233 mStagedSessionApplied = true; 2234 mStagedSessionFailed = false; 2235 mStagedSessionErrorCode = SessionInfo.STAGED_SESSION_NO_ERROR; 2236 mStagedSessionErrorMessage = ""; 2237 Slog.d(TAG, "Marking session " + sessionId + " as applied"); 2238 } 2239 cleanStageDir(); 2240 mCallback.onStagedSessionChanged(this); 2241 } 2242 2243 /** {@hide} */ isStagedSessionReady()2244 boolean isStagedSessionReady() { 2245 return mStagedSessionReady; 2246 } 2247 2248 /** {@hide} */ isStagedSessionApplied()2249 boolean isStagedSessionApplied() { 2250 return mStagedSessionApplied; 2251 } 2252 2253 /** {@hide} */ isStagedSessionFailed()2254 boolean isStagedSessionFailed() { 2255 return mStagedSessionFailed; 2256 } 2257 2258 /** {@hide} */ getStagedSessionErrorCode()2259 @StagedSessionErrorCode int getStagedSessionErrorCode() { 2260 return mStagedSessionErrorCode; 2261 } 2262 2263 /** {@hide} */ getStagedSessionErrorMessage()2264 String getStagedSessionErrorMessage() { 2265 return mStagedSessionErrorMessage; 2266 } 2267 destroyInternal()2268 private void destroyInternal() { 2269 synchronized (mLock) { 2270 mSealed = true; 2271 if (!params.isStaged || isStagedAndInTerminalState()) { 2272 mDestroyed = true; 2273 } 2274 // Force shut down all bridges 2275 for (RevocableFileDescriptor fd : mFds) { 2276 fd.revoke(); 2277 } 2278 for (FileBridge bridge : mBridges) { 2279 bridge.forceClose(); 2280 } 2281 } 2282 // For staged sessions, we don't delete the directory where the packages have been copied, 2283 // since these packages are supposed to be read on reboot. 2284 // Those dirs are deleted when the staged session has reached a final state. 2285 if (stageDir != null && !params.isStaged) { 2286 try { 2287 mPm.mInstaller.rmPackageDir(stageDir.getAbsolutePath()); 2288 } catch (InstallerException ignored) { 2289 } 2290 } 2291 } 2292 cleanStageDir()2293 private void cleanStageDir() { 2294 if (isMultiPackage()) { 2295 for (int childSessionId : getChildSessionIds()) { 2296 mSessionProvider.getSession(childSessionId).cleanStageDir(); 2297 } 2298 } else { 2299 try { 2300 mPm.mInstaller.rmPackageDir(stageDir.getAbsolutePath()); 2301 } catch (InstallerException ignored) { 2302 } 2303 } 2304 } 2305 dump(IndentingPrintWriter pw)2306 void dump(IndentingPrintWriter pw) { 2307 synchronized (mLock) { 2308 dumpLocked(pw); 2309 } 2310 } 2311 2312 @GuardedBy("mLock") dumpLocked(IndentingPrintWriter pw)2313 private void dumpLocked(IndentingPrintWriter pw) { 2314 pw.println("Session " + sessionId + ":"); 2315 pw.increaseIndent(); 2316 2317 pw.printPair("userId", userId); 2318 pw.printPair("mOriginalInstallerUid", mOriginalInstallerUid); 2319 pw.printPair("mInstallerPackageName", mInstallerPackageName); 2320 pw.printPair("mInstallerUid", mInstallerUid); 2321 pw.printPair("createdMillis", createdMillis); 2322 pw.printPair("stageDir", stageDir); 2323 pw.printPair("stageCid", stageCid); 2324 pw.println(); 2325 2326 params.dump(pw); 2327 2328 pw.printPair("mClientProgress", mClientProgress); 2329 pw.printPair("mProgress", mProgress); 2330 pw.printPair("mCommitted", mCommitted); 2331 pw.printPair("mSealed", mSealed); 2332 pw.printPair("mPermissionsManuallyAccepted", mPermissionsManuallyAccepted); 2333 pw.printPair("mRelinquished", mRelinquished); 2334 pw.printPair("mDestroyed", mDestroyed); 2335 pw.printPair("mFds", mFds.size()); 2336 pw.printPair("mBridges", mBridges.size()); 2337 pw.printPair("mFinalStatus", mFinalStatus); 2338 pw.printPair("mFinalMessage", mFinalMessage); 2339 pw.printPair("params.isMultiPackage", params.isMultiPackage); 2340 pw.printPair("params.isStaged", params.isStaged); 2341 pw.println(); 2342 2343 pw.decreaseIndent(); 2344 } 2345 writeGrantedRuntimePermissionsLocked(XmlSerializer out, String[] grantedRuntimePermissions)2346 private static void writeGrantedRuntimePermissionsLocked(XmlSerializer out, 2347 String[] grantedRuntimePermissions) throws IOException { 2348 if (grantedRuntimePermissions != null) { 2349 for (String permission : grantedRuntimePermissions) { 2350 out.startTag(null, TAG_GRANTED_RUNTIME_PERMISSION); 2351 writeStringAttribute(out, ATTR_NAME, permission); 2352 out.endTag(null, TAG_GRANTED_RUNTIME_PERMISSION); 2353 } 2354 } 2355 } 2356 writeWhitelistedRestrictedPermissionsLocked(@onNull XmlSerializer out, @Nullable List<String> whitelistedRestrictedPermissions)2357 private static void writeWhitelistedRestrictedPermissionsLocked(@NonNull XmlSerializer out, 2358 @Nullable List<String> whitelistedRestrictedPermissions) throws IOException { 2359 if (whitelistedRestrictedPermissions != null) { 2360 final int permissionCount = whitelistedRestrictedPermissions.size(); 2361 for (int i = 0; i < permissionCount; i++) { 2362 out.startTag(null, TAG_WHITELISTED_RESTRICTED_PERMISSION); 2363 writeStringAttribute(out, ATTR_NAME, whitelistedRestrictedPermissions.get(i)); 2364 out.endTag(null, TAG_WHITELISTED_RESTRICTED_PERMISSION); 2365 } 2366 } 2367 } 2368 2369 buildAppIconFile(int sessionId, @NonNull File sessionsDir)2370 private static File buildAppIconFile(int sessionId, @NonNull File sessionsDir) { 2371 return new File(sessionsDir, "app_icon." + sessionId + ".png"); 2372 } 2373 2374 /** 2375 * Write this session to a {@link XmlSerializer}. 2376 * 2377 * @param out Where to write the session to 2378 * @param sessionsDir The directory containing the sessions 2379 */ write(@onNull XmlSerializer out, @NonNull File sessionsDir)2380 void write(@NonNull XmlSerializer out, @NonNull File sessionsDir) throws IOException { 2381 synchronized (mLock) { 2382 if (mDestroyed) { 2383 return; 2384 } 2385 2386 out.startTag(null, TAG_SESSION); 2387 2388 writeIntAttribute(out, ATTR_SESSION_ID, sessionId); 2389 writeIntAttribute(out, ATTR_USER_ID, userId); 2390 writeStringAttribute(out, ATTR_INSTALLER_PACKAGE_NAME, 2391 mInstallerPackageName); 2392 writeIntAttribute(out, ATTR_INSTALLER_UID, mInstallerUid); 2393 writeLongAttribute(out, ATTR_CREATED_MILLIS, createdMillis); 2394 writeLongAttribute(out, ATTR_UPDATED_MILLIS, updatedMillis); 2395 if (stageDir != null) { 2396 writeStringAttribute(out, ATTR_SESSION_STAGE_DIR, 2397 stageDir.getAbsolutePath()); 2398 } 2399 if (stageCid != null) { 2400 writeStringAttribute(out, ATTR_SESSION_STAGE_CID, stageCid); 2401 } 2402 writeBooleanAttribute(out, ATTR_PREPARED, isPrepared()); 2403 writeBooleanAttribute(out, ATTR_COMMITTED, isCommitted()); 2404 writeBooleanAttribute(out, ATTR_SEALED, isSealed()); 2405 2406 writeBooleanAttribute(out, ATTR_MULTI_PACKAGE, params.isMultiPackage); 2407 writeBooleanAttribute(out, ATTR_STAGED_SESSION, params.isStaged); 2408 writeBooleanAttribute(out, ATTR_IS_READY, mStagedSessionReady); 2409 writeBooleanAttribute(out, ATTR_IS_FAILED, mStagedSessionFailed); 2410 writeBooleanAttribute(out, ATTR_IS_APPLIED, mStagedSessionApplied); 2411 writeIntAttribute(out, ATTR_STAGED_SESSION_ERROR_CODE, mStagedSessionErrorCode); 2412 writeStringAttribute(out, ATTR_STAGED_SESSION_ERROR_MESSAGE, 2413 mStagedSessionErrorMessage); 2414 // TODO(patb,109941548): avoid writing to xml and instead infer / validate this after 2415 // we've read all sessions. 2416 writeIntAttribute(out, ATTR_PARENT_SESSION_ID, mParentSessionId); 2417 writeIntAttribute(out, ATTR_MODE, params.mode); 2418 writeIntAttribute(out, ATTR_INSTALL_FLAGS, params.installFlags); 2419 writeIntAttribute(out, ATTR_INSTALL_LOCATION, params.installLocation); 2420 writeLongAttribute(out, ATTR_SIZE_BYTES, params.sizeBytes); 2421 writeStringAttribute(out, ATTR_APP_PACKAGE_NAME, params.appPackageName); 2422 writeStringAttribute(out, ATTR_APP_LABEL, params.appLabel); 2423 writeUriAttribute(out, ATTR_ORIGINATING_URI, params.originatingUri); 2424 writeIntAttribute(out, ATTR_ORIGINATING_UID, params.originatingUid); 2425 writeUriAttribute(out, ATTR_REFERRER_URI, params.referrerUri); 2426 writeStringAttribute(out, ATTR_ABI_OVERRIDE, params.abiOverride); 2427 writeStringAttribute(out, ATTR_VOLUME_UUID, params.volumeUuid); 2428 writeIntAttribute(out, ATTR_INSTALL_REASON, params.installReason); 2429 2430 writeGrantedRuntimePermissionsLocked(out, params.grantedRuntimePermissions); 2431 writeWhitelistedRestrictedPermissionsLocked(out, 2432 params.whitelistedRestrictedPermissions); 2433 2434 // Persist app icon if changed since last written 2435 File appIconFile = buildAppIconFile(sessionId, sessionsDir); 2436 if (params.appIcon == null && appIconFile.exists()) { 2437 appIconFile.delete(); 2438 } else if (params.appIcon != null 2439 && appIconFile.lastModified() != params.appIconLastModified) { 2440 if (LOGD) Slog.w(TAG, "Writing changed icon " + appIconFile); 2441 FileOutputStream os = null; 2442 try { 2443 os = new FileOutputStream(appIconFile); 2444 params.appIcon.compress(Bitmap.CompressFormat.PNG, 90, os); 2445 } catch (IOException e) { 2446 Slog.w(TAG, "Failed to write icon " + appIconFile + ": " + e.getMessage()); 2447 } finally { 2448 IoUtils.closeQuietly(os); 2449 } 2450 2451 params.appIconLastModified = appIconFile.lastModified(); 2452 } 2453 final int[] childSessionIds = getChildSessionIds(); 2454 for (int childSessionId : childSessionIds) { 2455 out.startTag(null, TAG_CHILD_SESSION); 2456 writeIntAttribute(out, ATTR_SESSION_ID, childSessionId); 2457 out.endTag(null, TAG_CHILD_SESSION); 2458 } 2459 } 2460 2461 out.endTag(null, TAG_SESSION); 2462 } 2463 2464 // Sanity check to be performed when the session is restored from an external file. Only one 2465 // of the session states should be true, or none of them. isStagedSessionStateValid(boolean isReady, boolean isApplied, boolean isFailed)2466 private static boolean isStagedSessionStateValid(boolean isReady, boolean isApplied, 2467 boolean isFailed) { 2468 return (!isReady && !isApplied && !isFailed) 2469 || (isReady && !isApplied && !isFailed) 2470 || (!isReady && isApplied && !isFailed) 2471 || (!isReady && !isApplied && isFailed); 2472 } 2473 2474 /** 2475 * Read new session from a {@link XmlPullParser xml description} and create it. 2476 * 2477 * @param in The source of the description 2478 * @param callback Callback the session uses to notify about changes of it's state 2479 * @param context Context to be used by the session 2480 * @param pm PackageManager to use by the session 2481 * @param installerThread Thread to be used for callbacks of this session 2482 * @param sessionsDir The directory the sessions are stored in 2483 * 2484 * @param sessionProvider 2485 * @return The newly created session 2486 */ readFromXml(@onNull XmlPullParser in, @NonNull PackageInstallerService.InternalCallback callback, @NonNull Context context, @NonNull PackageManagerService pm, Looper installerThread, @NonNull StagingManager stagingManager, @NonNull File sessionsDir, @NonNull PackageSessionProvider sessionProvider)2487 public static PackageInstallerSession readFromXml(@NonNull XmlPullParser in, 2488 @NonNull PackageInstallerService.InternalCallback callback, @NonNull Context context, 2489 @NonNull PackageManagerService pm, Looper installerThread, 2490 @NonNull StagingManager stagingManager, @NonNull File sessionsDir, 2491 @NonNull PackageSessionProvider sessionProvider) 2492 throws IOException, XmlPullParserException { 2493 final int sessionId = readIntAttribute(in, ATTR_SESSION_ID); 2494 final int userId = readIntAttribute(in, ATTR_USER_ID); 2495 final String installerPackageName = readStringAttribute(in, ATTR_INSTALLER_PACKAGE_NAME); 2496 final int installerUid = readIntAttribute(in, ATTR_INSTALLER_UID, pm.getPackageUid( 2497 installerPackageName, PackageManager.MATCH_UNINSTALLED_PACKAGES, userId)); 2498 final long createdMillis = readLongAttribute(in, ATTR_CREATED_MILLIS); 2499 long updatedMillis = readLongAttribute(in, ATTR_UPDATED_MILLIS); 2500 final String stageDirRaw = readStringAttribute(in, ATTR_SESSION_STAGE_DIR); 2501 final File stageDir = (stageDirRaw != null) ? new File(stageDirRaw) : null; 2502 final String stageCid = readStringAttribute(in, ATTR_SESSION_STAGE_CID); 2503 final boolean prepared = readBooleanAttribute(in, ATTR_PREPARED, true); 2504 final boolean committed = readBooleanAttribute(in, ATTR_COMMITTED); 2505 final boolean sealed = readBooleanAttribute(in, ATTR_SEALED); 2506 final int parentSessionId = readIntAttribute(in, ATTR_PARENT_SESSION_ID, 2507 SessionInfo.INVALID_ID); 2508 2509 final SessionParams params = new SessionParams( 2510 SessionParams.MODE_INVALID); 2511 params.isMultiPackage = readBooleanAttribute(in, ATTR_MULTI_PACKAGE, false); 2512 params.isStaged = readBooleanAttribute(in, ATTR_STAGED_SESSION, false); 2513 params.mode = readIntAttribute(in, ATTR_MODE); 2514 params.installFlags = readIntAttribute(in, ATTR_INSTALL_FLAGS); 2515 params.installLocation = readIntAttribute(in, ATTR_INSTALL_LOCATION); 2516 params.sizeBytes = readLongAttribute(in, ATTR_SIZE_BYTES); 2517 params.appPackageName = readStringAttribute(in, ATTR_APP_PACKAGE_NAME); 2518 params.appIcon = readBitmapAttribute(in, ATTR_APP_ICON); 2519 params.appLabel = readStringAttribute(in, ATTR_APP_LABEL); 2520 params.originatingUri = readUriAttribute(in, ATTR_ORIGINATING_URI); 2521 params.originatingUid = 2522 readIntAttribute(in, ATTR_ORIGINATING_UID, SessionParams.UID_UNKNOWN); 2523 params.referrerUri = readUriAttribute(in, ATTR_REFERRER_URI); 2524 params.abiOverride = readStringAttribute(in, ATTR_ABI_OVERRIDE); 2525 params.volumeUuid = readStringAttribute(in, ATTR_VOLUME_UUID); 2526 params.installReason = readIntAttribute(in, ATTR_INSTALL_REASON); 2527 2528 final File appIconFile = buildAppIconFile(sessionId, sessionsDir); 2529 if (appIconFile.exists()) { 2530 params.appIcon = BitmapFactory.decodeFile(appIconFile.getAbsolutePath()); 2531 params.appIconLastModified = appIconFile.lastModified(); 2532 } 2533 final boolean isReady = readBooleanAttribute(in, ATTR_IS_READY); 2534 final boolean isFailed = readBooleanAttribute(in, ATTR_IS_FAILED); 2535 final boolean isApplied = readBooleanAttribute(in, ATTR_IS_APPLIED); 2536 final int stagedSessionErrorCode = readIntAttribute(in, ATTR_STAGED_SESSION_ERROR_CODE, 2537 SessionInfo.STAGED_SESSION_NO_ERROR); 2538 final String stagedSessionErrorMessage = readStringAttribute(in, 2539 ATTR_STAGED_SESSION_ERROR_MESSAGE); 2540 2541 if (!isStagedSessionStateValid(isReady, isApplied, isFailed)) { 2542 throw new IllegalArgumentException("Can't restore staged session with invalid state."); 2543 } 2544 2545 // Parse sub tags of this session, typically used for repeated values / arrays. 2546 // Sub tags can come in any order, therefore we need to keep track of what we find while 2547 // parsing and only set the right values at the end. 2548 2549 // Store the current depth. We should stop parsing when we reach an end tag at the same 2550 // depth. 2551 List<String> grantedRuntimePermissions = new ArrayList<>(); 2552 List<String> whitelistedRestrictedPermissions = new ArrayList<>(); 2553 List<Integer> childSessionIds = new ArrayList<>(); 2554 int outerDepth = in.getDepth(); 2555 int type; 2556 while ((type = in.next()) != XmlPullParser.END_DOCUMENT 2557 && (type != XmlPullParser.END_TAG || in.getDepth() > outerDepth)) { 2558 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { 2559 continue; 2560 } 2561 if (TAG_GRANTED_RUNTIME_PERMISSION.equals(in.getName())) { 2562 grantedRuntimePermissions.add(readStringAttribute(in, ATTR_NAME)); 2563 } 2564 if (TAG_WHITELISTED_RESTRICTED_PERMISSION.equals(in.getName())) { 2565 whitelistedRestrictedPermissions.add(readStringAttribute(in, ATTR_NAME)); 2566 2567 } 2568 if (TAG_CHILD_SESSION.equals(in.getName())) { 2569 childSessionIds.add(readIntAttribute(in, ATTR_SESSION_ID, SessionInfo.INVALID_ID)); 2570 } 2571 } 2572 2573 if (grantedRuntimePermissions.size() > 0) { 2574 params.grantedRuntimePermissions = grantedRuntimePermissions 2575 .stream().toArray(String[]::new); 2576 } 2577 2578 if (whitelistedRestrictedPermissions.size() > 0) { 2579 params.whitelistedRestrictedPermissions = whitelistedRestrictedPermissions; 2580 } 2581 2582 int[] childSessionIdsArray; 2583 if (childSessionIds.size() > 0) { 2584 childSessionIdsArray = childSessionIds.stream().mapToInt(i -> i).toArray(); 2585 } else { 2586 childSessionIdsArray = EMPTY_CHILD_SESSION_ARRAY; 2587 } 2588 2589 return new PackageInstallerSession(callback, context, pm, sessionProvider, 2590 installerThread, stagingManager, sessionId, userId, installerPackageName, 2591 installerUid, params, createdMillis, stageDir, stageCid, prepared, committed, 2592 sealed, childSessionIdsArray, parentSessionId, isReady, isFailed, isApplied, 2593 stagedSessionErrorCode, stagedSessionErrorMessage); 2594 } 2595 2596 /** 2597 * Reads the session ID from a child session tag stored in the provided {@link XmlPullParser} 2598 */ readChildSessionIdFromXml(@onNull XmlPullParser in)2599 static int readChildSessionIdFromXml(@NonNull XmlPullParser in) { 2600 return readIntAttribute(in, ATTR_SESSION_ID, SessionInfo.INVALID_ID); 2601 } 2602 } 2603