1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.pm; 18 19 import static android.app.AppOpsManager.MODE_DEFAULT; 20 import static android.content.pm.DataLoaderType.INCREMENTAL; 21 import static android.content.pm.DataLoaderType.STREAMING; 22 import static android.content.pm.PackageInstaller.LOCATION_DATA_APP; 23 import static android.content.pm.PackageManager.INSTALL_FAILED_ABORTED; 24 import static android.content.pm.PackageManager.INSTALL_FAILED_BAD_SIGNATURE; 25 import static android.content.pm.PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; 26 import static android.content.pm.PackageManager.INSTALL_FAILED_INTERNAL_ERROR; 27 import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK; 28 import static android.content.pm.PackageManager.INSTALL_FAILED_MEDIA_UNAVAILABLE; 29 import static android.content.pm.PackageManager.INSTALL_FAILED_MISSING_SPLIT; 30 import static android.content.pm.PackageParser.APEX_FILE_EXTENSION; 31 import static android.content.pm.PackageParser.APK_FILE_EXTENSION; 32 import static android.system.OsConstants.O_CREAT; 33 import static android.system.OsConstants.O_RDONLY; 34 import static android.system.OsConstants.O_WRONLY; 35 36 import static com.android.internal.util.XmlUtils.readBitmapAttribute; 37 import static com.android.internal.util.XmlUtils.readBooleanAttribute; 38 import static com.android.internal.util.XmlUtils.readByteArrayAttribute; 39 import static com.android.internal.util.XmlUtils.readIntAttribute; 40 import static com.android.internal.util.XmlUtils.readLongAttribute; 41 import static com.android.internal.util.XmlUtils.readStringAttribute; 42 import static com.android.internal.util.XmlUtils.readUriAttribute; 43 import static com.android.internal.util.XmlUtils.writeBooleanAttribute; 44 import static com.android.internal.util.XmlUtils.writeByteArrayAttribute; 45 import static com.android.internal.util.XmlUtils.writeIntAttribute; 46 import static com.android.internal.util.XmlUtils.writeLongAttribute; 47 import static com.android.internal.util.XmlUtils.writeStringAttribute; 48 import static com.android.internal.util.XmlUtils.writeUriAttribute; 49 import static com.android.server.pm.PackageInstallerService.prepareStageDir; 50 51 import android.Manifest; 52 import android.annotation.NonNull; 53 import android.annotation.Nullable; 54 import android.app.Notification; 55 import android.app.NotificationManager; 56 import android.app.admin.DevicePolicyEventLogger; 57 import android.app.admin.DevicePolicyManagerInternal; 58 import android.content.ComponentName; 59 import android.content.Context; 60 import android.content.IIntentReceiver; 61 import android.content.IIntentSender; 62 import android.content.Intent; 63 import android.content.IntentSender; 64 import android.content.pm.ApplicationInfo; 65 import android.content.pm.DataLoaderManager; 66 import android.content.pm.DataLoaderParams; 67 import android.content.pm.DataLoaderParamsParcel; 68 import android.content.pm.FileSystemControlParcel; 69 import android.content.pm.IDataLoader; 70 import android.content.pm.IDataLoaderStatusListener; 71 import android.content.pm.IPackageInstallObserver2; 72 import android.content.pm.IPackageInstallerSession; 73 import android.content.pm.IPackageInstallerSessionFileSystemConnector; 74 import android.content.pm.InstallationFile; 75 import android.content.pm.InstallationFileParcel; 76 import android.content.pm.PackageInfo; 77 import android.content.pm.PackageInstaller; 78 import android.content.pm.PackageInstaller.SessionInfo; 79 import android.content.pm.PackageInstaller.SessionInfo.StagedSessionErrorCode; 80 import android.content.pm.PackageInstaller.SessionParams; 81 import android.content.pm.PackageManager; 82 import android.content.pm.PackageManagerInternal; 83 import android.content.pm.PackageParser; 84 import android.content.pm.PackageParser.ApkLite; 85 import android.content.pm.PackageParser.PackageLite; 86 import android.content.pm.PackageParser.PackageParserException; 87 import android.content.pm.dex.DexMetadataHelper; 88 import android.content.pm.parsing.ApkLiteParseUtils; 89 import android.content.pm.parsing.result.ParseResult; 90 import android.content.pm.parsing.result.ParseTypeImpl; 91 import android.graphics.Bitmap; 92 import android.graphics.BitmapFactory; 93 import android.os.Binder; 94 import android.os.Bundle; 95 import android.os.FileBridge; 96 import android.os.FileUtils; 97 import android.os.Handler; 98 import android.os.IBinder; 99 import android.os.Looper; 100 import android.os.Message; 101 import android.os.ParcelFileDescriptor; 102 import android.os.ParcelableException; 103 import android.os.Process; 104 import android.os.RemoteException; 105 import android.os.RevocableFileDescriptor; 106 import android.os.SystemProperties; 107 import android.os.UserHandle; 108 import android.os.incremental.IStorageHealthListener; 109 import android.os.incremental.IncrementalFileStorages; 110 import android.os.incremental.IncrementalManager; 111 import android.os.incremental.StorageHealthCheckParams; 112 import android.os.storage.StorageManager; 113 import android.provider.Settings.Secure; 114 import android.stats.devicepolicy.DevicePolicyEnums; 115 import android.system.ErrnoException; 116 import android.system.Int64Ref; 117 import android.system.Os; 118 import android.system.OsConstants; 119 import android.system.StructStat; 120 import android.text.TextUtils; 121 import android.util.ArraySet; 122 import android.util.ExceptionUtils; 123 import android.util.MathUtils; 124 import android.util.Slog; 125 import android.util.SparseIntArray; 126 import android.util.apk.ApkSignatureVerifier; 127 128 import com.android.internal.R; 129 import com.android.internal.annotations.GuardedBy; 130 import com.android.internal.content.NativeLibraryHelper; 131 import com.android.internal.content.PackageHelper; 132 import com.android.internal.messages.nano.SystemMessageProto; 133 import com.android.internal.os.SomeArgs; 134 import com.android.internal.util.ArrayUtils; 135 import com.android.internal.util.FrameworkStatsLog; 136 import com.android.internal.util.IndentingPrintWriter; 137 import com.android.server.LocalServices; 138 import com.android.server.pm.Installer.InstallerException; 139 import com.android.server.pm.dex.DexManager; 140 import com.android.server.pm.parsing.pkg.AndroidPackage; 141 import com.android.server.security.VerityUtils; 142 143 import libcore.io.IoUtils; 144 import libcore.util.EmptyArray; 145 146 import org.xmlpull.v1.XmlPullParser; 147 import org.xmlpull.v1.XmlPullParserException; 148 import org.xmlpull.v1.XmlSerializer; 149 150 import java.io.File; 151 import java.io.FileDescriptor; 152 import java.io.FileFilter; 153 import java.io.FileOutputStream; 154 import java.io.IOException; 155 import java.util.ArrayList; 156 import java.util.Arrays; 157 import java.util.List; 158 import java.util.Objects; 159 import java.util.Set; 160 import java.util.concurrent.atomic.AtomicInteger; 161 162 public class PackageInstallerSession extends IPackageInstallerSession.Stub { 163 private static final String TAG = "PackageInstallerSession"; 164 private static final boolean LOGD = true; 165 private static final String REMOVE_MARKER_EXTENSION = ".removed"; 166 167 private static final int MSG_STREAM_VALIDATE_AND_COMMIT = 1; 168 private static final int MSG_INSTALL = 2; 169 private static final int MSG_ON_PACKAGE_INSTALLED = 3; 170 private static final int MSG_SESSION_VERIFICATION_FAILURE = 4; 171 172 /** XML constants used for persisting a session */ 173 static final String TAG_SESSION = "session"; 174 static final String TAG_CHILD_SESSION = "childSession"; 175 static final String TAG_SESSION_FILE = "sessionFile"; 176 private static final String TAG_GRANTED_RUNTIME_PERMISSION = "granted-runtime-permission"; 177 private static final String TAG_WHITELISTED_RESTRICTED_PERMISSION = 178 "whitelisted-restricted-permission"; 179 private static final String TAG_AUTO_REVOKE_PERMISSIONS_MODE = 180 "auto-revoke-permissions-mode"; 181 private static final String ATTR_SESSION_ID = "sessionId"; 182 private static final String ATTR_USER_ID = "userId"; 183 private static final String ATTR_INSTALLER_PACKAGE_NAME = "installerPackageName"; 184 private static final String ATTR_INSTALLER_UID = "installerUid"; 185 private static final String ATTR_INITIATING_PACKAGE_NAME = 186 "installInitiatingPackageName"; 187 private static final String ATTR_ORIGINATING_PACKAGE_NAME = 188 "installOriginatingPackageName"; 189 private static final String ATTR_CREATED_MILLIS = "createdMillis"; 190 private static final String ATTR_UPDATED_MILLIS = "updatedMillis"; 191 private static final String ATTR_SESSION_STAGE_DIR = "sessionStageDir"; 192 private static final String ATTR_SESSION_STAGE_CID = "sessionStageCid"; 193 private static final String ATTR_PREPARED = "prepared"; 194 private static final String ATTR_COMMITTED = "committed"; 195 private static final String ATTR_DESTROYED = "destroyed"; 196 private static final String ATTR_SEALED = "sealed"; 197 private static final String ATTR_MULTI_PACKAGE = "multiPackage"; 198 private static final String ATTR_PARENT_SESSION_ID = "parentSessionId"; 199 private static final String ATTR_STAGED_SESSION = "stagedSession"; 200 private static final String ATTR_IS_READY = "isReady"; 201 private static final String ATTR_IS_FAILED = "isFailed"; 202 private static final String ATTR_IS_APPLIED = "isApplied"; 203 private static final String ATTR_STAGED_SESSION_ERROR_CODE = "errorCode"; 204 private static final String ATTR_STAGED_SESSION_ERROR_MESSAGE = "errorMessage"; 205 private static final String ATTR_MODE = "mode"; 206 private static final String ATTR_INSTALL_FLAGS = "installFlags"; 207 private static final String ATTR_INSTALL_LOCATION = "installLocation"; 208 private static final String ATTR_SIZE_BYTES = "sizeBytes"; 209 private static final String ATTR_APP_PACKAGE_NAME = "appPackageName"; 210 @Deprecated 211 private static final String ATTR_APP_ICON = "appIcon"; 212 private static final String ATTR_APP_LABEL = "appLabel"; 213 private static final String ATTR_ORIGINATING_URI = "originatingUri"; 214 private static final String ATTR_ORIGINATING_UID = "originatingUid"; 215 private static final String ATTR_REFERRER_URI = "referrerUri"; 216 private static final String ATTR_ABI_OVERRIDE = "abiOverride"; 217 private static final String ATTR_VOLUME_UUID = "volumeUuid"; 218 private static final String ATTR_NAME = "name"; 219 private static final String ATTR_INSTALL_REASON = "installRason"; 220 private static final String ATTR_IS_DATALOADER = "isDataLoader"; 221 private static final String ATTR_DATALOADER_TYPE = "dataLoaderType"; 222 private static final String ATTR_DATALOADER_PACKAGE_NAME = "dataLoaderPackageName"; 223 private static final String ATTR_DATALOADER_CLASS_NAME = "dataLoaderClassName"; 224 private static final String ATTR_DATALOADER_ARGUMENTS = "dataLoaderArguments"; 225 private static final String ATTR_LOCATION = "location"; 226 private static final String ATTR_LENGTH_BYTES = "lengthBytes"; 227 private static final String ATTR_METADATA = "metadata"; 228 private static final String ATTR_SIGNATURE = "signature"; 229 230 private static final String PROPERTY_NAME_INHERIT_NATIVE = "pi.inherit_native_on_dont_kill"; 231 private static final int[] EMPTY_CHILD_SESSION_ARRAY = EmptyArray.INT; 232 private static final InstallationFile[] EMPTY_INSTALLATION_FILE_ARRAY = {}; 233 234 private static final String SYSTEM_DATA_LOADER_PACKAGE = "android"; 235 236 private static final int INCREMENTAL_STORAGE_BLOCKED_TIMEOUT_MS = 2000; 237 private static final int INCREMENTAL_STORAGE_UNHEALTHY_TIMEOUT_MS = 7000; 238 private static final int INCREMENTAL_STORAGE_UNHEALTHY_MONITORING_MS = 60000; 239 240 // TODO: enforce INSTALL_ALLOW_TEST 241 // TODO: enforce INSTALL_ALLOW_DOWNGRADE 242 243 private final PackageInstallerService.InternalCallback mCallback; 244 private final Context mContext; 245 private final PackageManagerService mPm; 246 private final Handler mHandler; 247 private final PackageSessionProvider mSessionProvider; 248 private final StagingManager mStagingManager; 249 250 final int sessionId; 251 final int userId; 252 final SessionParams params; 253 final long createdMillis; 254 255 /** Staging location where client data is written. */ 256 final File stageDir; 257 final String stageCid; 258 259 private final AtomicInteger mActiveCount = new AtomicInteger(); 260 261 private final Object mLock = new Object(); 262 263 /** Timestamp of the last time this session changed state */ 264 @GuardedBy("mLock") 265 private long updatedMillis; 266 267 /** Uid of the creator of this session. */ 268 private final int mOriginalInstallerUid; 269 270 /** Package name of the app that created the installation session. */ 271 private final String mOriginalInstallerPackageName; 272 273 /** Uid of the owner of the installer session */ 274 @GuardedBy("mLock") 275 private int mInstallerUid; 276 277 /** Where this install request came from */ 278 @GuardedBy("mLock") 279 private InstallSource mInstallSource; 280 281 @GuardedBy("mLock") 282 private float mClientProgress = 0; 283 @GuardedBy("mLock") 284 private float mInternalProgress = 0; 285 286 @GuardedBy("mLock") 287 private float mProgress = 0; 288 @GuardedBy("mLock") 289 private float mReportedProgress = -1; 290 291 /** State of the session. */ 292 @GuardedBy("mLock") 293 private boolean mPrepared = false; 294 @GuardedBy("mLock") 295 private boolean mSealed = false; 296 @GuardedBy("mLock") 297 private boolean mShouldBeSealed = false; 298 @GuardedBy("mLock") 299 private boolean mCommitted = false; 300 @GuardedBy("mLock") 301 private boolean mRelinquished = false; 302 @GuardedBy("mLock") 303 private boolean mDestroyed = false; 304 305 /** Permissions have been accepted by the user (see {@link #setPermissionsResult}) */ 306 @GuardedBy("mLock") 307 private boolean mPermissionsManuallyAccepted = false; 308 309 @GuardedBy("mLock") 310 private int mFinalStatus; 311 @GuardedBy("mLock") 312 private String mFinalMessage; 313 314 @GuardedBy("mLock") 315 private final ArrayList<RevocableFileDescriptor> mFds = new ArrayList<>(); 316 @GuardedBy("mLock") 317 private final ArrayList<FileBridge> mBridges = new ArrayList<>(); 318 319 @GuardedBy("mLock") 320 private IntentSender mRemoteStatusReceiver; 321 322 /** Fields derived from commit parsing */ 323 @GuardedBy("mLock") 324 private String mPackageName; 325 @GuardedBy("mLock") 326 private long mVersionCode; 327 @GuardedBy("mLock") 328 private PackageParser.SigningDetails mSigningDetails; 329 @GuardedBy("mLock") 330 private SparseIntArray mChildSessionIds = new SparseIntArray(); 331 @GuardedBy("mLock") 332 private int mParentSessionId; 333 334 static class FileEntry { 335 private final int mIndex; 336 private final InstallationFile mFile; 337 FileEntry(int index, InstallationFile file)338 FileEntry(int index, InstallationFile file) { 339 this.mIndex = index; 340 this.mFile = file; 341 } 342 getIndex()343 int getIndex() { 344 return this.mIndex; 345 } 346 getFile()347 InstallationFile getFile() { 348 return this.mFile; 349 } 350 351 @Override equals(Object obj)352 public boolean equals(Object obj) { 353 if (!(obj instanceof FileEntry)) { 354 return false; 355 } 356 final FileEntry rhs = (FileEntry) obj; 357 return (mFile.getLocation() == rhs.mFile.getLocation()) && TextUtils.equals( 358 mFile.getName(), rhs.mFile.getName()); 359 } 360 361 @Override hashCode()362 public int hashCode() { 363 return Objects.hash(mFile.getLocation(), mFile.getName()); 364 } 365 } 366 367 @GuardedBy("mLock") 368 private ArraySet<FileEntry> mFiles = new ArraySet<>(); 369 370 @GuardedBy("mLock") 371 private boolean mStagedSessionApplied; 372 @GuardedBy("mLock") 373 private boolean mStagedSessionReady; 374 @GuardedBy("mLock") 375 private boolean mStagedSessionFailed; 376 @GuardedBy("mLock") 377 private int mStagedSessionErrorCode = SessionInfo.STAGED_SESSION_NO_ERROR; 378 @GuardedBy("mLock") 379 private String mStagedSessionErrorMessage; 380 381 /** 382 * Path to the validated base APK for this session, which may point at an 383 * APK inside the session (when the session defines the base), or it may 384 * point at the existing base APK (when adding splits to an existing app). 385 * <p> 386 * This is used when confirming permissions, since we can't fully stage the 387 * session inside an ASEC before confirming with user. 388 */ 389 @GuardedBy("mLock") 390 private File mResolvedBaseFile; 391 392 @GuardedBy("mLock") 393 private final List<File> mResolvedStagedFiles = new ArrayList<>(); 394 @GuardedBy("mLock") 395 private final List<File> mResolvedInheritedFiles = new ArrayList<>(); 396 @GuardedBy("mLock") 397 private final List<String> mResolvedInstructionSets = new ArrayList<>(); 398 @GuardedBy("mLock") 399 private final List<String> mResolvedNativeLibPaths = new ArrayList<>(); 400 @GuardedBy("mLock") 401 private File mInheritedFilesBase; 402 @GuardedBy("mLock") 403 private boolean mVerityFound; 404 405 private boolean mDataLoaderFinished = false; 406 407 // TODO(b/159663586): should be protected by mLock 408 private IncrementalFileStorages mIncrementalFileStorages; 409 410 private static final FileFilter sAddedApkFilter = new FileFilter() { 411 @Override 412 public boolean accept(File file) { 413 // Installers can't stage directories, so it's fine to ignore 414 // entries like "lost+found". 415 if (file.isDirectory()) return false; 416 if (file.getName().endsWith(REMOVE_MARKER_EXTENSION)) return false; 417 if (DexMetadataHelper.isDexMetadataFile(file)) return false; 418 if (VerityUtils.isFsveritySignatureFile(file)) return false; 419 return true; 420 } 421 }; 422 private static final FileFilter sAddedFilter = new FileFilter() { 423 @Override 424 public boolean accept(File file) { 425 // Installers can't stage directories, so it's fine to ignore 426 // entries like "lost+found". 427 if (file.isDirectory()) return false; 428 if (file.getName().endsWith(REMOVE_MARKER_EXTENSION)) return false; 429 return true; 430 } 431 }; 432 private static final FileFilter sRemovedFilter = new FileFilter() { 433 @Override 434 public boolean accept(File file) { 435 if (file.isDirectory()) return false; 436 if (!file.getName().endsWith(REMOVE_MARKER_EXTENSION)) return false; 437 return true; 438 } 439 }; 440 441 private final Handler.Callback mHandlerCallback = new Handler.Callback() { 442 @Override 443 public boolean handleMessage(Message msg) { 444 switch (msg.what) { 445 case MSG_STREAM_VALIDATE_AND_COMMIT: 446 handleStreamValidateAndCommit(); 447 break; 448 case MSG_INSTALL: 449 handleInstall(); 450 break; 451 case MSG_ON_PACKAGE_INSTALLED: 452 final SomeArgs args = (SomeArgs) msg.obj; 453 final String packageName = (String) args.arg1; 454 final String message = (String) args.arg2; 455 final Bundle extras = (Bundle) args.arg3; 456 final IntentSender statusReceiver = (IntentSender) args.arg4; 457 final int returnCode = args.argi1; 458 args.recycle(); 459 460 sendOnPackageInstalled(mContext, statusReceiver, sessionId, 461 isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked(), userId, 462 packageName, returnCode, message, extras); 463 464 break; 465 case MSG_SESSION_VERIFICATION_FAILURE: 466 final int error = msg.arg1; 467 final String detailMessage = (String) msg.obj; 468 onSessionVerificationFailure(error, detailMessage); 469 break; 470 } 471 472 return true; 473 } 474 }; 475 isDataLoaderInstallation()476 private boolean isDataLoaderInstallation() { 477 return params.dataLoaderParams != null; 478 } 479 isStreamingInstallation()480 private boolean isStreamingInstallation() { 481 return isDataLoaderInstallation() && params.dataLoaderParams.getType() == STREAMING; 482 } 483 isIncrementalInstallation()484 private boolean isIncrementalInstallation() { 485 return isDataLoaderInstallation() && params.dataLoaderParams.getType() == INCREMENTAL; 486 } 487 488 /** 489 * @return {@code true} iff the installing is app an device owner or affiliated profile owner. 490 */ 491 @GuardedBy("mLock") isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked()492 private boolean isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked() { 493 if (userId != UserHandle.getUserId(mInstallerUid)) { 494 return false; 495 } 496 DevicePolicyManagerInternal dpmi = 497 LocalServices.getService(DevicePolicyManagerInternal.class); 498 return dpmi != null && dpmi.canSilentlyInstallPackage( 499 mInstallSource.installerPackageName, mInstallerUid); 500 } 501 502 /** 503 * Checks if the permissions still need to be confirmed. 504 * 505 * <p>This is dependant on the identity of the installer, hence this cannot be cached if the 506 * installer might still {@link #transfer(String) change}. 507 * 508 * @return {@code true} iff we need to ask to confirm the permissions? 509 */ 510 @GuardedBy("mLock") needToAskForPermissionsLocked()511 private boolean needToAskForPermissionsLocked() { 512 if (mPermissionsManuallyAccepted) { 513 return false; 514 } 515 516 final boolean isInstallPermissionGranted = 517 (mPm.checkUidPermission(android.Manifest.permission.INSTALL_PACKAGES, 518 mInstallerUid) == PackageManager.PERMISSION_GRANTED); 519 final boolean isSelfUpdatePermissionGranted = 520 (mPm.checkUidPermission(android.Manifest.permission.INSTALL_SELF_UPDATES, 521 mInstallerUid) == PackageManager.PERMISSION_GRANTED); 522 final boolean isUpdatePermissionGranted = 523 (mPm.checkUidPermission(android.Manifest.permission.INSTALL_PACKAGE_UPDATES, 524 mInstallerUid) == PackageManager.PERMISSION_GRANTED); 525 final int targetPackageUid = mPm.getPackageUid(mPackageName, 0, userId); 526 final boolean isPermissionGranted = isInstallPermissionGranted 527 || (isUpdatePermissionGranted && targetPackageUid != -1) 528 || (isSelfUpdatePermissionGranted && targetPackageUid == mInstallerUid); 529 final boolean isInstallerRoot = (mInstallerUid == Process.ROOT_UID); 530 final boolean isInstallerSystem = (mInstallerUid == Process.SYSTEM_UID); 531 final boolean forcePermissionPrompt = 532 (params.installFlags & PackageManager.INSTALL_FORCE_PERMISSION_PROMPT) != 0; 533 534 // Device owners and affiliated profile owners are allowed to silently install packages, so 535 // the permission check is waived if the installer is the device owner. 536 return forcePermissionPrompt || !(isPermissionGranted || isInstallerRoot 537 || isInstallerSystem || isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked()); 538 } 539 PackageInstallerSession(PackageInstallerService.InternalCallback callback, Context context, PackageManagerService pm, PackageSessionProvider sessionProvider, Looper looper, StagingManager stagingManager, int sessionId, int userId, int installerUid, @NonNull InstallSource installSource, SessionParams params, long createdMillis, File stageDir, String stageCid, InstallationFile[] files, boolean prepared, boolean committed, boolean destroyed, boolean sealed, @Nullable int[] childSessionIds, int parentSessionId, boolean isReady, boolean isFailed, boolean isApplied, int stagedSessionErrorCode, String stagedSessionErrorMessage)540 public PackageInstallerSession(PackageInstallerService.InternalCallback callback, 541 Context context, PackageManagerService pm, 542 PackageSessionProvider sessionProvider, Looper looper, StagingManager stagingManager, 543 int sessionId, int userId, int installerUid, @NonNull InstallSource installSource, 544 SessionParams params, long createdMillis, 545 File stageDir, String stageCid, InstallationFile[] files, boolean prepared, 546 boolean committed, boolean destroyed, boolean sealed, 547 @Nullable int[] childSessionIds, int parentSessionId, boolean isReady, 548 boolean isFailed, boolean isApplied, int stagedSessionErrorCode, 549 String stagedSessionErrorMessage) { 550 mCallback = callback; 551 mContext = context; 552 mPm = pm; 553 mSessionProvider = sessionProvider; 554 mHandler = new Handler(looper, mHandlerCallback); 555 mStagingManager = stagingManager; 556 557 this.sessionId = sessionId; 558 this.userId = userId; 559 mOriginalInstallerUid = installerUid; 560 mInstallerUid = installerUid; 561 mInstallSource = Objects.requireNonNull(installSource); 562 mOriginalInstallerPackageName = mInstallSource.installerPackageName; 563 this.params = params; 564 this.createdMillis = createdMillis; 565 this.updatedMillis = createdMillis; 566 this.stageDir = stageDir; 567 this.stageCid = stageCid; 568 this.mShouldBeSealed = sealed; 569 if (childSessionIds != null) { 570 for (int childSessionId : childSessionIds) { 571 mChildSessionIds.put(childSessionId, 0); 572 } 573 } 574 this.mParentSessionId = parentSessionId; 575 576 if (files != null) { 577 for (int i = 0, size = files.length; i < size; ++i) { 578 InstallationFile file = files[i]; 579 if (!mFiles.add(new FileEntry(i, file))) { 580 throw new IllegalArgumentException( 581 "Trying to add a duplicate installation file"); 582 } 583 } 584 } 585 586 if (!params.isMultiPackage && (stageDir == null) == (stageCid == null)) { 587 throw new IllegalArgumentException( 588 "Exactly one of stageDir or stageCid stage must be set"); 589 } 590 591 mPrepared = prepared; 592 mCommitted = committed; 593 mDestroyed = destroyed; 594 mStagedSessionReady = isReady; 595 mStagedSessionFailed = isFailed; 596 mStagedSessionApplied = isApplied; 597 mStagedSessionErrorCode = stagedSessionErrorCode; 598 mStagedSessionErrorMessage = 599 stagedSessionErrorMessage != null ? stagedSessionErrorMessage : ""; 600 601 if (isDataLoaderInstallation()) { 602 if (isApexInstallation()) { 603 throw new IllegalArgumentException( 604 "DataLoader installation of APEX modules is not allowed."); 605 } 606 if (this.params.dataLoaderParams.getComponentName().getPackageName() 607 == SYSTEM_DATA_LOADER_PACKAGE) { 608 assertShellOrSystemCalling("System data loaders"); 609 } 610 } 611 612 if (isIncrementalInstallation()) { 613 if (!IncrementalManager.isAllowed()) { 614 throw new IllegalArgumentException("Incremental installation not allowed."); 615 } 616 if (!isIncrementalInstallationAllowed(mPackageName)) { 617 throw new IllegalArgumentException( 618 "Incremental installation of this package is not allowed."); 619 } 620 } 621 } 622 623 /** 624 * Returns {@code true} if the {@link SessionInfo} object should be produced with potentially 625 * sensitive data scrubbed from its fields. 626 * 627 * @param callingUid the uid of the caller; the recipient of the {@link SessionInfo} that may 628 * need to be scrubbed 629 */ shouldScrubData(int callingUid)630 private boolean shouldScrubData(int callingUid) { 631 return !(callingUid < Process.FIRST_APPLICATION_UID || getInstallerUid() == callingUid); 632 } 633 634 /** 635 * Generates a {@link SessionInfo} object for the provided uid. This may result in some fields 636 * that may contain sensitive info being filtered. 637 * 638 * @param includeIcon true if the icon should be included in the object 639 * @param callingUid the uid of the caller; the recipient of the {@link SessionInfo} that may 640 * need to be scrubbed 641 * @see #shouldScrubData(int) 642 */ generateInfoForCaller(boolean includeIcon, int callingUid)643 public SessionInfo generateInfoForCaller(boolean includeIcon, int callingUid) { 644 return generateInfoInternal(includeIcon, shouldScrubData(callingUid)); 645 } 646 647 /** 648 * Generates a {@link SessionInfo} object to ensure proper hiding of sensitive fields. 649 * 650 * @param includeIcon true if the icon should be included in the object 651 * @see #generateInfoForCaller(boolean, int) 652 */ generateInfoScrubbed(boolean includeIcon)653 public SessionInfo generateInfoScrubbed(boolean includeIcon) { 654 return generateInfoInternal(includeIcon, true /*scrubData*/); 655 } 656 generateInfoInternal(boolean includeIcon, boolean scrubData)657 private SessionInfo generateInfoInternal(boolean includeIcon, boolean scrubData) { 658 final SessionInfo info = new SessionInfo(); 659 synchronized (mLock) { 660 info.sessionId = sessionId; 661 info.userId = userId; 662 info.installerPackageName = mInstallSource.installerPackageName; 663 info.resolvedBaseCodePath = (mResolvedBaseFile != null) ? 664 mResolvedBaseFile.getAbsolutePath() : null; 665 info.progress = mProgress; 666 info.sealed = mSealed; 667 info.isCommitted = mCommitted; 668 info.active = mActiveCount.get() > 0; 669 670 info.mode = params.mode; 671 info.installReason = params.installReason; 672 info.sizeBytes = params.sizeBytes; 673 info.appPackageName = params.appPackageName; 674 if (includeIcon) { 675 info.appIcon = params.appIcon; 676 } 677 info.appLabel = params.appLabel; 678 679 info.installLocation = params.installLocation; 680 if (!scrubData) { 681 info.originatingUri = params.originatingUri; 682 } 683 info.originatingUid = params.originatingUid; 684 if (!scrubData) { 685 info.referrerUri = params.referrerUri; 686 } 687 info.grantedRuntimePermissions = params.grantedRuntimePermissions; 688 info.whitelistedRestrictedPermissions = params.whitelistedRestrictedPermissions; 689 info.autoRevokePermissionsMode = params.autoRevokePermissionsMode; 690 info.installFlags = params.installFlags; 691 info.isMultiPackage = params.isMultiPackage; 692 info.isStaged = params.isStaged; 693 info.rollbackDataPolicy = params.rollbackDataPolicy; 694 info.parentSessionId = mParentSessionId; 695 info.childSessionIds = mChildSessionIds.copyKeys(); 696 if (info.childSessionIds == null) { 697 info.childSessionIds = EMPTY_CHILD_SESSION_ARRAY; 698 } 699 info.isStagedSessionApplied = mStagedSessionApplied; 700 info.isStagedSessionReady = mStagedSessionReady; 701 info.isStagedSessionFailed = mStagedSessionFailed; 702 info.setStagedSessionErrorCode(mStagedSessionErrorCode, mStagedSessionErrorMessage); 703 info.createdMillis = createdMillis; 704 info.updatedMillis = updatedMillis; 705 } 706 return info; 707 } 708 isPrepared()709 public boolean isPrepared() { 710 synchronized (mLock) { 711 return mPrepared; 712 } 713 } 714 isSealed()715 public boolean isSealed() { 716 synchronized (mLock) { 717 return mSealed; 718 } 719 } 720 721 /** {@hide} */ isCommitted()722 boolean isCommitted() { 723 synchronized (mLock) { 724 return mCommitted; 725 } 726 } 727 728 /** {@hide} */ isDestroyed()729 boolean isDestroyed() { 730 synchronized (mLock) { 731 return mDestroyed; 732 } 733 } 734 735 /** Returns true if a staged session has reached a final state and can be forgotten about */ isStagedAndInTerminalState()736 public boolean isStagedAndInTerminalState() { 737 synchronized (mLock) { 738 return params.isStaged && (mStagedSessionApplied || mStagedSessionFailed); 739 } 740 } 741 742 @GuardedBy("mLock") assertPreparedAndNotSealedLocked(String cookie)743 private void assertPreparedAndNotSealedLocked(String cookie) { 744 assertPreparedAndNotCommittedOrDestroyedLocked(cookie); 745 if (mSealed) { 746 throw new SecurityException(cookie + " not allowed after sealing"); 747 } 748 } 749 750 @GuardedBy("mLock") assertPreparedAndNotCommittedOrDestroyedLocked(String cookie)751 private void assertPreparedAndNotCommittedOrDestroyedLocked(String cookie) { 752 assertPreparedAndNotDestroyedLocked(cookie); 753 if (mCommitted) { 754 throw new SecurityException(cookie + " not allowed after commit"); 755 } 756 } 757 758 @GuardedBy("mLock") assertPreparedAndNotDestroyedLocked(String cookie)759 private void assertPreparedAndNotDestroyedLocked(String cookie) { 760 if (!mPrepared) { 761 throw new IllegalStateException(cookie + " before prepared"); 762 } 763 if (mDestroyed) { 764 throw new SecurityException(cookie + " not allowed after destruction"); 765 } 766 } 767 768 @GuardedBy("mLock") setClientProgressLocked(float progress)769 private void setClientProgressLocked(float progress) { 770 // Always publish first staging movement 771 final boolean forcePublish = (mClientProgress == 0); 772 mClientProgress = progress; 773 computeProgressLocked(forcePublish); 774 } 775 776 @Override setClientProgress(float progress)777 public void setClientProgress(float progress) { 778 synchronized (mLock) { 779 assertCallerIsOwnerOrRootLocked(); 780 setClientProgressLocked(progress); 781 } 782 } 783 784 @Override addClientProgress(float progress)785 public void addClientProgress(float progress) { 786 synchronized (mLock) { 787 assertCallerIsOwnerOrRootLocked(); 788 setClientProgressLocked(mClientProgress + progress); 789 } 790 } 791 792 @GuardedBy("mLock") computeProgressLocked(boolean forcePublish)793 private void computeProgressLocked(boolean forcePublish) { 794 mProgress = MathUtils.constrain(mClientProgress * 0.8f, 0f, 0.8f) 795 + MathUtils.constrain(mInternalProgress * 0.2f, 0f, 0.2f); 796 797 // Only publish when meaningful change 798 if (forcePublish || Math.abs(mProgress - mReportedProgress) >= 0.01) { 799 mReportedProgress = mProgress; 800 mCallback.onSessionProgressChanged(this, mProgress); 801 } 802 } 803 804 @Override getNames()805 public String[] getNames() { 806 synchronized (mLock) { 807 assertCallerIsOwnerOrRootLocked(); 808 assertPreparedAndNotCommittedOrDestroyedLocked("getNames"); 809 810 return getNamesLocked(); 811 } 812 } 813 814 @GuardedBy("mLock") getNamesLocked()815 private String[] getNamesLocked() { 816 if (!isDataLoaderInstallation()) { 817 String[] result = stageDir.list(); 818 if (result == null) { 819 result = EmptyArray.STRING; 820 } 821 return result; 822 } 823 824 InstallationFile[] files = getInstallationFilesLocked(); 825 String[] result = new String[files.length]; 826 for (int i = 0, size = files.length; i < size; ++i) { 827 result[i] = files[i].getName(); 828 } 829 return result; 830 } 831 832 @GuardedBy("mLock") getInstallationFilesLocked()833 private InstallationFile[] getInstallationFilesLocked() { 834 final InstallationFile[] result = new InstallationFile[mFiles.size()]; 835 for (FileEntry fileEntry : mFiles) { 836 result[fileEntry.getIndex()] = fileEntry.getFile(); 837 } 838 return result; 839 } 840 filterFiles(File parent, String[] names, FileFilter filter)841 private static ArrayList<File> filterFiles(File parent, String[] names, FileFilter filter) { 842 ArrayList<File> result = new ArrayList<>(names.length); 843 for (String name : names) { 844 File file = new File(parent, name); 845 if (filter.accept(file)) { 846 result.add(file); 847 } 848 } 849 return result; 850 } 851 852 @GuardedBy("mLock") getAddedApksLocked()853 private List<File> getAddedApksLocked() { 854 String[] names = getNamesLocked(); 855 return filterFiles(stageDir, names, sAddedApkFilter); 856 } 857 858 @GuardedBy("mLock") getRemovedFilesLocked()859 private List<File> getRemovedFilesLocked() { 860 String[] names = getNamesLocked(); 861 return filterFiles(stageDir, names, sRemovedFilter); 862 } 863 864 @Override removeSplit(String splitName)865 public void removeSplit(String splitName) { 866 if (isDataLoaderInstallation()) { 867 throw new IllegalStateException( 868 "Cannot remove splits in a data loader installation session."); 869 } 870 if (TextUtils.isEmpty(params.appPackageName)) { 871 throw new IllegalStateException("Must specify package name to remove a split"); 872 } 873 874 synchronized (mLock) { 875 assertCallerIsOwnerOrRootLocked(); 876 assertPreparedAndNotCommittedOrDestroyedLocked("removeSplit"); 877 878 try { 879 createRemoveSplitMarkerLocked(splitName); 880 } catch (IOException e) { 881 throw ExceptionUtils.wrap(e); 882 } 883 } 884 } 885 getRemoveMarkerName(String name)886 private static String getRemoveMarkerName(String name) { 887 final String markerName = name + REMOVE_MARKER_EXTENSION; 888 if (!FileUtils.isValidExtFilename(markerName)) { 889 throw new IllegalArgumentException("Invalid marker: " + markerName); 890 } 891 return markerName; 892 } 893 createRemoveSplitMarkerLocked(String splitName)894 private void createRemoveSplitMarkerLocked(String splitName) throws IOException { 895 try { 896 final File target = new File(stageDir, getRemoveMarkerName(splitName)); 897 target.createNewFile(); 898 Os.chmod(target.getAbsolutePath(), 0 /*mode*/); 899 } catch (ErrnoException e) { 900 throw e.rethrowAsIOException(); 901 } 902 } 903 assertShellOrSystemCalling(String operation)904 private void assertShellOrSystemCalling(String operation) { 905 switch (Binder.getCallingUid()) { 906 case android.os.Process.SHELL_UID: 907 case android.os.Process.ROOT_UID: 908 case android.os.Process.SYSTEM_UID: 909 break; 910 default: 911 throw new SecurityException(operation + " only supported from shell or system"); 912 } 913 } 914 assertCanWrite(boolean reverseMode)915 private void assertCanWrite(boolean reverseMode) { 916 if (isDataLoaderInstallation()) { 917 throw new IllegalStateException( 918 "Cannot write regular files in a data loader installation session."); 919 } 920 synchronized (mLock) { 921 assertCallerIsOwnerOrRootLocked(); 922 assertPreparedAndNotSealedLocked("assertCanWrite"); 923 } 924 if (reverseMode) { 925 assertShellOrSystemCalling("Reverse mode"); 926 } 927 } 928 929 @Override openWrite(String name, long offsetBytes, long lengthBytes)930 public ParcelFileDescriptor openWrite(String name, long offsetBytes, long lengthBytes) { 931 assertCanWrite(false); 932 try { 933 return doWriteInternal(name, offsetBytes, lengthBytes, null); 934 } catch (IOException e) { 935 throw ExceptionUtils.wrap(e); 936 } 937 } 938 939 @Override write(String name, long offsetBytes, long lengthBytes, ParcelFileDescriptor fd)940 public void write(String name, long offsetBytes, long lengthBytes, 941 ParcelFileDescriptor fd) { 942 assertCanWrite(fd != null); 943 try { 944 doWriteInternal(name, offsetBytes, lengthBytes, fd); 945 } catch (IOException e) { 946 throw ExceptionUtils.wrap(e); 947 } 948 } 949 openTargetInternal(String path, int flags, int mode)950 private ParcelFileDescriptor openTargetInternal(String path, int flags, int mode) 951 throws IOException, ErrnoException { 952 // TODO: this should delegate to DCS so the system process avoids 953 // holding open FDs into containers. 954 final FileDescriptor fd = Os.open(path, flags, mode); 955 return new ParcelFileDescriptor(fd); 956 } 957 createRevocableFdInternal(RevocableFileDescriptor fd, ParcelFileDescriptor pfd)958 private ParcelFileDescriptor createRevocableFdInternal(RevocableFileDescriptor fd, 959 ParcelFileDescriptor pfd) throws IOException { 960 int releasedFdInt = pfd.detachFd(); 961 FileDescriptor releasedFd = new FileDescriptor(); 962 releasedFd.setInt$(releasedFdInt); 963 fd.init(mContext, releasedFd); 964 return fd.getRevocableFileDescriptor(); 965 } 966 doWriteInternal(String name, long offsetBytes, long lengthBytes, ParcelFileDescriptor incomingFd)967 private ParcelFileDescriptor doWriteInternal(String name, long offsetBytes, long lengthBytes, 968 ParcelFileDescriptor incomingFd) throws IOException { 969 // Quick sanity check of state, and allocate a pipe for ourselves. We 970 // then do heavy disk allocation outside the lock, but this open pipe 971 // will block any attempted install transitions. 972 final RevocableFileDescriptor fd; 973 final FileBridge bridge; 974 synchronized (mLock) { 975 if (PackageInstaller.ENABLE_REVOCABLE_FD) { 976 fd = new RevocableFileDescriptor(); 977 bridge = null; 978 mFds.add(fd); 979 } else { 980 fd = null; 981 bridge = new FileBridge(); 982 mBridges.add(bridge); 983 } 984 } 985 986 try { 987 // Use installer provided name for now; we always rename later 988 if (!FileUtils.isValidExtFilename(name)) { 989 throw new IllegalArgumentException("Invalid name: " + name); 990 } 991 final File target; 992 final long identity = Binder.clearCallingIdentity(); 993 try { 994 target = new File(stageDir, name); 995 } finally { 996 Binder.restoreCallingIdentity(identity); 997 } 998 999 ParcelFileDescriptor targetPfd = openTargetInternal(target.getAbsolutePath(), 1000 O_CREAT | O_WRONLY, 0644); 1001 Os.chmod(target.getAbsolutePath(), 0644); 1002 1003 // If caller specified a total length, allocate it for them. Free up 1004 // cache space to grow, if needed. 1005 if (stageDir != null && lengthBytes > 0) { 1006 mContext.getSystemService(StorageManager.class).allocateBytes( 1007 targetPfd.getFileDescriptor(), lengthBytes, 1008 PackageHelper.translateAllocateFlags(params.installFlags)); 1009 } 1010 1011 if (offsetBytes > 0) { 1012 Os.lseek(targetPfd.getFileDescriptor(), offsetBytes, OsConstants.SEEK_SET); 1013 } 1014 1015 if (incomingFd != null) { 1016 // In "reverse" mode, we're streaming data ourselves from the 1017 // incoming FD, which means we never have to hand out our 1018 // sensitive internal FD. We still rely on a "bridge" being 1019 // inserted above to hold the session active. 1020 try { 1021 final Int64Ref last = new Int64Ref(0); 1022 FileUtils.copy(incomingFd.getFileDescriptor(), targetPfd.getFileDescriptor(), 1023 lengthBytes, null, Runnable::run, 1024 (long progress) -> { 1025 if (params.sizeBytes > 0) { 1026 final long delta = progress - last.value; 1027 last.value = progress; 1028 synchronized (mLock) { 1029 setClientProgressLocked(mClientProgress 1030 + (float) delta / (float) params.sizeBytes); 1031 } 1032 } 1033 }); 1034 } finally { 1035 IoUtils.closeQuietly(targetPfd); 1036 IoUtils.closeQuietly(incomingFd); 1037 1038 // We're done here, so remove the "bridge" that was holding 1039 // the session active. 1040 synchronized (mLock) { 1041 if (PackageInstaller.ENABLE_REVOCABLE_FD) { 1042 mFds.remove(fd); 1043 } else { 1044 bridge.forceClose(); 1045 mBridges.remove(bridge); 1046 } 1047 } 1048 } 1049 return null; 1050 } else if (PackageInstaller.ENABLE_REVOCABLE_FD) { 1051 return createRevocableFdInternal(fd, targetPfd); 1052 } else { 1053 bridge.setTargetFile(targetPfd); 1054 bridge.start(); 1055 return bridge.getClientSocket(); 1056 } 1057 1058 } catch (ErrnoException e) { 1059 throw e.rethrowAsIOException(); 1060 } 1061 } 1062 1063 @Override openRead(String name)1064 public ParcelFileDescriptor openRead(String name) { 1065 if (isDataLoaderInstallation()) { 1066 throw new IllegalStateException( 1067 "Cannot read regular files in a data loader installation session."); 1068 } 1069 synchronized (mLock) { 1070 assertCallerIsOwnerOrRootLocked(); 1071 assertPreparedAndNotCommittedOrDestroyedLocked("openRead"); 1072 try { 1073 return openReadInternalLocked(name); 1074 } catch (IOException e) { 1075 throw ExceptionUtils.wrap(e); 1076 } 1077 } 1078 } 1079 openReadInternalLocked(String name)1080 private ParcelFileDescriptor openReadInternalLocked(String name) throws IOException { 1081 try { 1082 if (!FileUtils.isValidExtFilename(name)) { 1083 throw new IllegalArgumentException("Invalid name: " + name); 1084 } 1085 final File target = new File(stageDir, name); 1086 final FileDescriptor targetFd = Os.open(target.getAbsolutePath(), O_RDONLY, 0); 1087 return new ParcelFileDescriptor(targetFd); 1088 } catch (ErrnoException e) { 1089 throw e.rethrowAsIOException(); 1090 } 1091 } 1092 1093 /** 1094 * Check if the caller is the owner of this session. Otherwise throw a 1095 * {@link SecurityException}. 1096 */ 1097 @GuardedBy("mLock") assertCallerIsOwnerOrRootLocked()1098 private void assertCallerIsOwnerOrRootLocked() { 1099 final int callingUid = Binder.getCallingUid(); 1100 if (callingUid != Process.ROOT_UID && callingUid != mInstallerUid) { 1101 throw new SecurityException("Session does not belong to uid " + callingUid); 1102 } 1103 } 1104 1105 /** 1106 * Check if the caller is the owner of this session. Otherwise throw a 1107 * {@link SecurityException}. 1108 */ 1109 @GuardedBy("mLock") assertCallerIsOwnerOrRootOrSystemLocked()1110 private void assertCallerIsOwnerOrRootOrSystemLocked() { 1111 final int callingUid = Binder.getCallingUid(); 1112 if (callingUid != Process.ROOT_UID && callingUid != mInstallerUid 1113 && callingUid != Process.SYSTEM_UID) { 1114 throw new SecurityException("Session does not belong to uid " + callingUid); 1115 } 1116 } 1117 1118 /** 1119 * If anybody is reading or writing data of the session, throw an {@link SecurityException}. 1120 */ 1121 @GuardedBy("mLock") assertNoWriteFileTransfersOpenLocked()1122 private void assertNoWriteFileTransfersOpenLocked() { 1123 // Verify that all writers are hands-off 1124 for (RevocableFileDescriptor fd : mFds) { 1125 if (!fd.isRevoked()) { 1126 throw new SecurityException("Files still open"); 1127 } 1128 } 1129 for (FileBridge bridge : mBridges) { 1130 if (!bridge.isClosed()) { 1131 throw new SecurityException("Files still open"); 1132 } 1133 } 1134 } 1135 1136 @Override commit(@onNull IntentSender statusReceiver, boolean forTransfer)1137 public void commit(@NonNull IntentSender statusReceiver, boolean forTransfer) { 1138 if (hasParentSessionId()) { 1139 throw new IllegalStateException( 1140 "Session " + sessionId + " is a child of multi-package session " 1141 + mParentSessionId + " and may not be committed directly."); 1142 } 1143 1144 if (!markAsSealed(statusReceiver, forTransfer)) { 1145 return; 1146 } 1147 if (isMultiPackage()) { 1148 final SparseIntArray remainingSessions = mChildSessionIds.clone(); 1149 final IntentSender childIntentSender = 1150 new ChildStatusIntentReceiver(remainingSessions, statusReceiver) 1151 .getIntentSender(); 1152 boolean sealFailed = false; 1153 for (int i = mChildSessionIds.size() - 1; i >= 0; --i) { 1154 final int childSessionId = mChildSessionIds.keyAt(i); 1155 // seal all children, regardless if any of them fail; we'll throw/return 1156 // as appropriate once all children have been processed 1157 if (!mSessionProvider.getSession(childSessionId) 1158 .markAsSealed(childIntentSender, forTransfer)) { 1159 sealFailed = true; 1160 } 1161 } 1162 if (sealFailed) { 1163 return; 1164 } 1165 } 1166 1167 dispatchStreamValidateAndCommit(); 1168 } 1169 dispatchStreamValidateAndCommit()1170 private void dispatchStreamValidateAndCommit() { 1171 mHandler.obtainMessage(MSG_STREAM_VALIDATE_AND_COMMIT).sendToTarget(); 1172 } 1173 handleStreamValidateAndCommit()1174 private void handleStreamValidateAndCommit() { 1175 PackageManagerException unrecoverableFailure = null; 1176 // This will track whether the session and any children were validated and are ready to 1177 // progress to the next phase of install 1178 boolean allSessionsReady = false; 1179 try { 1180 allSessionsReady = streamValidateAndCommit(); 1181 } catch (PackageManagerException e) { 1182 unrecoverableFailure = e; 1183 } 1184 1185 if (isMultiPackage()) { 1186 int childCount = mChildSessionIds.size(); 1187 1188 // This will contain all child sessions that do not encounter an unrecoverable failure 1189 ArrayList<PackageInstallerSession> nonFailingSessions = new ArrayList<>(childCount); 1190 1191 for (int i = childCount - 1; i >= 0; --i) { 1192 final int childSessionId = mChildSessionIds.keyAt(i); 1193 // commit all children, regardless if any of them fail; we'll throw/return 1194 // as appropriate once all children have been processed 1195 try { 1196 PackageInstallerSession session = mSessionProvider.getSession(childSessionId); 1197 allSessionsReady &= session.streamValidateAndCommit(); 1198 nonFailingSessions.add(session); 1199 } catch (PackageManagerException e) { 1200 allSessionsReady = false; 1201 if (unrecoverableFailure == null) { 1202 unrecoverableFailure = e; 1203 } 1204 } 1205 } 1206 // If we encountered any unrecoverable failures, destroy all other sessions including 1207 // the parent 1208 if (unrecoverableFailure != null) { 1209 // {@link #streamValidateAndCommit()} calls 1210 // {@link #onSessionVerificationFailure(PackageManagerException)}, but we don't 1211 // expect it to ever do so for parent sessions. Call that on this parent to clean 1212 // it up and notify listeners of the error. 1213 onSessionVerificationFailure(unrecoverableFailure); 1214 // fail other child sessions that did not already fail 1215 for (int i = nonFailingSessions.size() - 1; i >= 0; --i) { 1216 PackageInstallerSession session = nonFailingSessions.get(i); 1217 session.onSessionVerificationFailure(unrecoverableFailure); 1218 } 1219 } 1220 } 1221 1222 if (!allSessionsReady) { 1223 return; 1224 } 1225 1226 mHandler.obtainMessage(MSG_INSTALL).sendToTarget(); 1227 } 1228 1229 private final class FileSystemConnector extends 1230 IPackageInstallerSessionFileSystemConnector.Stub { 1231 final Set<String> mAddedFiles = new ArraySet<>(); 1232 FileSystemConnector(List<InstallationFileParcel> addedFiles)1233 FileSystemConnector(List<InstallationFileParcel> addedFiles) { 1234 for (InstallationFileParcel file : addedFiles) { 1235 mAddedFiles.add(file.name); 1236 } 1237 } 1238 1239 @Override writeData(String name, long offsetBytes, long lengthBytes, ParcelFileDescriptor incomingFd)1240 public void writeData(String name, long offsetBytes, long lengthBytes, 1241 ParcelFileDescriptor incomingFd) { 1242 if (incomingFd == null) { 1243 throw new IllegalArgumentException("incomingFd can't be null"); 1244 } 1245 if (!mAddedFiles.contains(name)) { 1246 throw new SecurityException("File name is not in the list of added files."); 1247 } 1248 try { 1249 doWriteInternal(name, offsetBytes, lengthBytes, incomingFd); 1250 } catch (IOException e) { 1251 throw ExceptionUtils.wrap(e); 1252 } 1253 } 1254 } 1255 1256 private class ChildStatusIntentReceiver { 1257 private final SparseIntArray mChildSessionsRemaining; 1258 private final IntentSender mStatusReceiver; 1259 private final IIntentSender.Stub mLocalSender = new IIntentSender.Stub() { 1260 @Override 1261 public void send(int code, Intent intent, String resolvedType, IBinder whitelistToken, 1262 IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) { 1263 statusUpdate(intent); 1264 } 1265 }; 1266 ChildStatusIntentReceiver(SparseIntArray remainingSessions, IntentSender statusReceiver)1267 private ChildStatusIntentReceiver(SparseIntArray remainingSessions, 1268 IntentSender statusReceiver) { 1269 this.mChildSessionsRemaining = remainingSessions; 1270 this.mStatusReceiver = statusReceiver; 1271 } 1272 getIntentSender()1273 public IntentSender getIntentSender() { 1274 return new IntentSender((IIntentSender) mLocalSender); 1275 } 1276 statusUpdate(Intent intent)1277 public void statusUpdate(Intent intent) { 1278 mHandler.post(() -> { 1279 if (mChildSessionsRemaining.size() == 0) { 1280 // no children to deal with, ignore. 1281 return; 1282 } 1283 final boolean destroyed; 1284 synchronized (mLock) { 1285 destroyed = mDestroyed; 1286 } 1287 if (destroyed) { 1288 // the parent has already been terminated, ignore. 1289 return; 1290 } 1291 final int sessionId = intent.getIntExtra( 1292 PackageInstaller.EXTRA_SESSION_ID, 0); 1293 final int status = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, 1294 PackageInstaller.STATUS_FAILURE); 1295 final int sessionIndex = mChildSessionsRemaining.indexOfKey(sessionId); 1296 if (PackageInstaller.STATUS_SUCCESS == status) { 1297 mChildSessionsRemaining.removeAt(sessionIndex); 1298 if (mChildSessionsRemaining.size() == 0) { 1299 try { 1300 intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, 1301 PackageInstallerSession.this.sessionId); 1302 mStatusReceiver.sendIntent(mContext, 0, intent, null, null); 1303 } catch (IntentSender.SendIntentException ignore) { 1304 } 1305 } 1306 } else if (PackageInstaller.STATUS_PENDING_USER_ACTION == status) { 1307 try { 1308 mStatusReceiver.sendIntent(mContext, 0, intent, null, null); 1309 } catch (IntentSender.SendIntentException ignore) { 1310 } 1311 } else { // failure, let's forward and clean up this session. 1312 intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, 1313 PackageInstallerSession.this.sessionId); 1314 mChildSessionsRemaining.clear(); // we're done. Don't send any more. 1315 try { 1316 mStatusReceiver.sendIntent(mContext, 0, intent, null, null); 1317 } catch (IntentSender.SendIntentException ignore) { 1318 } 1319 } 1320 }); 1321 } 1322 } 1323 1324 /** {@hide} */ 1325 private class StreamingException extends Exception { StreamingException(Throwable cause)1326 StreamingException(Throwable cause) { 1327 super(cause); 1328 } 1329 } 1330 1331 /** 1332 * Returns whether or not a package can be installed while Secure FRP is enabled. 1333 * <p> 1334 * Only callers with the INSTALL_PACKAGES permission are allowed to install. However, 1335 * prevent the package installer from installing anything because, while it has the 1336 * permission, it will allows packages to be installed from anywhere. 1337 */ isSecureFrpInstallAllowed(Context context, int callingUid)1338 private static boolean isSecureFrpInstallAllowed(Context context, int callingUid) { 1339 final PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class); 1340 final String[] systemInstaller = pmi.getKnownPackageNames( 1341 PackageManagerInternal.PACKAGE_INSTALLER, UserHandle.USER_SYSTEM); 1342 final AndroidPackage callingInstaller = pmi.getPackage(callingUid); 1343 if (callingInstaller != null 1344 && ArrayUtils.contains(systemInstaller, callingInstaller.getPackageName())) { 1345 // don't allow the system package installer to install while under secure FRP 1346 return false; 1347 } 1348 1349 // require caller to hold the INSTALL_PACKAGES permission 1350 return context.checkCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES) 1351 == PackageManager.PERMISSION_GRANTED; 1352 } 1353 1354 /** 1355 * Checks if the package can be installed on IncFs. 1356 */ isIncrementalInstallationAllowed(String packageName)1357 private static boolean isIncrementalInstallationAllowed(String packageName) { 1358 final PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class); 1359 final PackageSetting existingPkgSetting = pmi.getPackageSetting(packageName); 1360 if (existingPkgSetting == null || existingPkgSetting.pkg == null) { 1361 return true; 1362 } 1363 1364 return !existingPkgSetting.pkg.isSystem() 1365 && !existingPkgSetting.getPkgState().isUpdatedSystemApp(); 1366 } 1367 1368 /** 1369 * If this was not already called, the session will be sealed. 1370 * 1371 * This method may be called multiple times to update the status receiver validate caller 1372 * permissions. 1373 */ markAsSealed(@onNull IntentSender statusReceiver, boolean forTransfer)1374 private boolean markAsSealed(@NonNull IntentSender statusReceiver, boolean forTransfer) { 1375 Objects.requireNonNull(statusReceiver); 1376 1377 List<PackageInstallerSession> childSessions = getChildSessionsNotLocked(); 1378 1379 synchronized (mLock) { 1380 assertCallerIsOwnerOrRootLocked(); 1381 assertPreparedAndNotDestroyedLocked("commit"); 1382 assertNoWriteFileTransfersOpenLocked(); 1383 1384 final boolean isSecureFrpEnabled = 1385 (Secure.getInt(mContext.getContentResolver(), Secure.SECURE_FRP_MODE, 0) == 1); 1386 if (isSecureFrpEnabled 1387 && !isSecureFrpInstallAllowed(mContext, Binder.getCallingUid())) { 1388 throw new SecurityException("Can't install packages while in secure FRP"); 1389 } 1390 1391 if (forTransfer) { 1392 mContext.enforceCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES, null); 1393 if (mInstallerUid == mOriginalInstallerUid) { 1394 throw new IllegalArgumentException("Session has not been transferred"); 1395 } 1396 } else { 1397 if (mInstallerUid != mOriginalInstallerUid) { 1398 throw new IllegalArgumentException("Session has been transferred"); 1399 } 1400 } 1401 1402 mRemoteStatusReceiver = statusReceiver; 1403 1404 // After updating the observer, we can skip re-sealing. 1405 if (mSealed) { 1406 return true; 1407 } 1408 1409 try { 1410 sealLocked(childSessions); 1411 } catch (PackageManagerException e) { 1412 return false; 1413 } 1414 } 1415 1416 // Persist the fact that we've sealed ourselves to prevent 1417 // mutations of any hard links we create. We do this without holding 1418 // the session lock, since otherwise it's a lock inversion. 1419 mCallback.onSessionSealedBlocking(this); 1420 1421 return true; 1422 } 1423 1424 /** 1425 * Returns true if the session is successfully validated and committed. Returns false if the 1426 * dataloader could not be prepared. This can be called multiple times so long as no 1427 * exception is thrown. 1428 * @throws PackageManagerException on an unrecoverable error. 1429 */ streamValidateAndCommit()1430 private boolean streamValidateAndCommit() throws PackageManagerException { 1431 // TODO(patb): since the work done here for a parent session in a multi-package install is 1432 // mostly superficial, consider splitting this method for the parent and 1433 // single / child sessions. 1434 synchronized (mLock) { 1435 if (mCommitted) { 1436 return true; 1437 } 1438 1439 if (!streamAndValidateLocked()) { 1440 return false; 1441 } 1442 1443 // Client staging is fully done at this point 1444 mClientProgress = 1f; 1445 computeProgressLocked(true); 1446 1447 // This ongoing commit should keep session active, even though client 1448 // will probably close their end. 1449 mActiveCount.incrementAndGet(); 1450 1451 mCommitted = true; 1452 } 1453 return true; 1454 } 1455 1456 /** Return a list of child sessions or null if the session is not multipackage 1457 * 1458 * <p> This method is handy to prevent potential deadlocks (b/123391593) 1459 */ getChildSessionsNotLocked()1460 private @Nullable List<PackageInstallerSession> getChildSessionsNotLocked() { 1461 if (Thread.holdsLock(mLock)) { 1462 Slog.wtf(TAG, "Calling thread " + Thread.currentThread().getName() 1463 + " is holding mLock", new Throwable()); 1464 } 1465 List<PackageInstallerSession> childSessions = null; 1466 if (isMultiPackage()) { 1467 final int[] childSessionIds = getChildSessionIds(); 1468 childSessions = new ArrayList<>(childSessionIds.length); 1469 for (int childSessionId : childSessionIds) { 1470 childSessions.add(mSessionProvider.getSession(childSessionId)); 1471 } 1472 } 1473 return childSessions; 1474 } 1475 1476 /** 1477 * Assert multipackage install has consistent sessions. 1478 * 1479 * @throws PackageManagerException if child sessions don't match parent session 1480 * in respect to staged and enable rollback parameters. 1481 */ 1482 @GuardedBy("mLock") assertMultiPackageConsistencyLocked( @onNull List<PackageInstallerSession> childSessions)1483 private void assertMultiPackageConsistencyLocked( 1484 @NonNull List<PackageInstallerSession> childSessions) throws PackageManagerException { 1485 for (PackageInstallerSession childSession : childSessions) { 1486 // It might be that the parent session is loaded before all of it's child sessions are, 1487 // e.g. when reading sessions from XML. Those sessions will be null here, and their 1488 // conformance with the multipackage params will be checked when they're loaded. 1489 if (childSession == null) { 1490 continue; 1491 } 1492 assertConsistencyWithLocked(childSession); 1493 } 1494 } 1495 1496 /** 1497 * Assert consistency with the given session. 1498 * 1499 * @throws PackageManagerException if other sessions doesn't match this session 1500 * in respect to staged and enable rollback parameters. 1501 */ 1502 @GuardedBy("mLock") assertConsistencyWithLocked(PackageInstallerSession other)1503 private void assertConsistencyWithLocked(PackageInstallerSession other) 1504 throws PackageManagerException { 1505 // Session groups must be consistent wrt to isStaged parameter. Non-staging session 1506 // cannot be grouped with staging sessions. 1507 if (this.params.isStaged != other.params.isStaged) { 1508 throw new PackageManagerException( 1509 PackageManager.INSTALL_FAILED_MULTIPACKAGE_INCONSISTENCY, 1510 "Multipackage Inconsistency: session " + other.sessionId 1511 + " and session " + sessionId 1512 + " have inconsistent staged settings"); 1513 } 1514 if (this.params.getEnableRollback() != other.params.getEnableRollback()) { 1515 throw new PackageManagerException( 1516 PackageManager.INSTALL_FAILED_MULTIPACKAGE_INCONSISTENCY, 1517 "Multipackage Inconsistency: session " + other.sessionId 1518 + " and session " + sessionId 1519 + " have inconsistent rollback settings"); 1520 } 1521 } 1522 1523 /** 1524 * Seal the session to prevent further modification. 1525 * 1526 * <p>The session will be sealed after calling this method even if it failed. 1527 * 1528 * @throws PackageManagerException if the session was sealed but something went wrong. If the 1529 * session was sealed this is the only possible exception. 1530 */ 1531 @GuardedBy("mLock") sealLocked(List<PackageInstallerSession> childSessions)1532 private void sealLocked(List<PackageInstallerSession> childSessions) 1533 throws PackageManagerException { 1534 try { 1535 assertNoWriteFileTransfersOpenLocked(); 1536 assertPreparedAndNotDestroyedLocked("sealing of session"); 1537 1538 mSealed = true; 1539 1540 if (childSessions != null) { 1541 assertMultiPackageConsistencyLocked(childSessions); 1542 } 1543 } catch (PackageManagerException e) { 1544 throw onSessionVerificationFailure(e); 1545 } catch (Throwable e) { 1546 // Convert all exceptions into package manager exceptions as only those are handled 1547 // in the code above. 1548 throw onSessionVerificationFailure(new PackageManagerException(e)); 1549 } 1550 } 1551 1552 /** 1553 * Prepare DataLoader and stream content for DataLoader sessions. 1554 * Validate the contents of all session. 1555 * 1556 * @return false if the data loader could not be prepared. 1557 * @throws PackageManagerException when an unrecoverable exception is encountered 1558 */ 1559 @GuardedBy("mLock") streamAndValidateLocked()1560 private boolean streamAndValidateLocked() throws PackageManagerException { 1561 try { 1562 // Read transfers from the original owner stay open, but as the session's data cannot 1563 // be modified anymore, there is no leak of information. For staged sessions, further 1564 // validation is performed by the staging manager. 1565 if (!params.isMultiPackage) { 1566 if (!prepareDataLoaderLocked()) { 1567 return false; 1568 } 1569 1570 if (isApexInstallation()) { 1571 validateApexInstallLocked(); 1572 } else { 1573 validateApkInstallLocked(); 1574 } 1575 } 1576 1577 if (params.isStaged) { 1578 mStagingManager.checkNonOverlappingWithStagedSessions(this); 1579 } 1580 return true; 1581 } catch (PackageManagerException e) { 1582 throw onSessionVerificationFailure(e); 1583 } catch (Throwable e) { 1584 // Convert all exceptions into package manager exceptions as only those are handled 1585 // in the code above. 1586 throw onSessionVerificationFailure(new PackageManagerException(e)); 1587 } 1588 } 1589 onSessionVerificationFailure(PackageManagerException e)1590 private PackageManagerException onSessionVerificationFailure(PackageManagerException e) { 1591 onSessionVerificationFailure(e.error, ExceptionUtils.getCompleteMessage(e)); 1592 return e; 1593 } 1594 onSessionVerificationFailure(int error, String detailMessage)1595 private void onSessionVerificationFailure(int error, String detailMessage) { 1596 // Session is sealed but could not be verified, we need to destroy it. 1597 destroyInternal(); 1598 // Dispatch message to remove session from PackageInstallerService. 1599 dispatchSessionFinished(error, detailMessage, null); 1600 // TODO(b/173194203): clean up staged session in destroyInternal() call instead 1601 if (isStaged() && stageDir != null) { 1602 cleanStageDir(); 1603 } 1604 } 1605 onStorageUnhealthy()1606 private void onStorageUnhealthy() { 1607 if (TextUtils.isEmpty(mPackageName)) { 1608 // The package has not been installed. 1609 return; 1610 } 1611 final PackageManagerService packageManagerService = mPm; 1612 final String packageName = mPackageName; 1613 mHandler.post(() -> { 1614 if (packageManagerService.deletePackageX(packageName, 1615 PackageManager.VERSION_CODE_HIGHEST, UserHandle.USER_SYSTEM, 1616 PackageManager.DELETE_ALL_USERS) != PackageManager.DELETE_SUCCEEDED) { 1617 Slog.e(TAG, "Failed to uninstall package with failed dataloader: " + packageName); 1618 } 1619 }); 1620 } 1621 1622 /** 1623 * If session should be sealed, then it's sealed to prevent further modification. 1624 * If the session can't be sealed then it's destroyed. 1625 * 1626 * Additionally for staged APEX sessions read+validate the package and populate req'd fields. 1627 * 1628 * <p> This is meant to be called after all of the sessions are loaded and added to 1629 * PackageInstallerService 1630 */ onAfterSessionRead()1631 void onAfterSessionRead() { 1632 synchronized (mLock) { 1633 if (!mShouldBeSealed || isStagedAndInTerminalState()) { 1634 return; 1635 } 1636 } 1637 List<PackageInstallerSession> childSessions = getChildSessionsNotLocked(); 1638 synchronized (mLock) { 1639 try { 1640 sealLocked(childSessions); 1641 1642 if (isApexInstallation()) { 1643 // APEX installations rely on certain fields to be populated after reboot. 1644 // E.g. mPackageName. 1645 validateApexInstallLocked(); 1646 } 1647 } catch (PackageManagerException e) { 1648 Slog.e(TAG, "Package not valid", e); 1649 } 1650 } 1651 } 1652 1653 /** Update the timestamp of when the staged session last changed state */ markUpdated()1654 public void markUpdated() { 1655 synchronized (mLock) { 1656 this.updatedMillis = System.currentTimeMillis(); 1657 } 1658 } 1659 1660 @Override transfer(String packageName)1661 public void transfer(String packageName) { 1662 Objects.requireNonNull(packageName); 1663 1664 ApplicationInfo newOwnerAppInfo = mPm.getApplicationInfo(packageName, 0, userId); 1665 if (newOwnerAppInfo == null) { 1666 throw new ParcelableException(new PackageManager.NameNotFoundException(packageName)); 1667 } 1668 1669 if (PackageManager.PERMISSION_GRANTED != mPm.checkUidPermission( 1670 Manifest.permission.INSTALL_PACKAGES, newOwnerAppInfo.uid)) { 1671 throw new SecurityException("Destination package " + packageName + " does not have " 1672 + "the " + Manifest.permission.INSTALL_PACKAGES + " permission"); 1673 } 1674 1675 // Only install flags that can be verified by the app the session is transferred to are 1676 // allowed. The parameters can be read via PackageInstaller.SessionInfo. 1677 if (!params.areHiddenOptionsSet()) { 1678 throw new SecurityException("Can only transfer sessions that use public options"); 1679 } 1680 1681 List<PackageInstallerSession> childSessions = getChildSessionsNotLocked(); 1682 1683 synchronized (mLock) { 1684 assertCallerIsOwnerOrRootLocked(); 1685 assertPreparedAndNotSealedLocked("transfer"); 1686 1687 try { 1688 sealLocked(childSessions); 1689 } catch (PackageManagerException e) { 1690 throw new IllegalArgumentException("Package is not valid", e); 1691 } 1692 1693 mInstallerUid = newOwnerAppInfo.uid; 1694 mInstallSource = InstallSource.create(packageName, null, packageName); 1695 } 1696 1697 // Persist the fact that we've sealed ourselves to prevent 1698 // mutations of any hard links we create. We do this without holding 1699 // the session lock, since otherwise it's a lock inversion. 1700 mCallback.onSessionSealedBlocking(this); 1701 } 1702 handleInstall()1703 private void handleInstall() { 1704 if (isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked()) { 1705 DevicePolicyEventLogger 1706 .createEvent(DevicePolicyEnums.INSTALL_PACKAGE) 1707 .setAdmin(mInstallSource.installerPackageName) 1708 .write(); 1709 } 1710 if (params.isStaged) { 1711 mStagingManager.commitSession(this); 1712 destroyInternal(); 1713 dispatchSessionFinished(PackageManager.INSTALL_SUCCEEDED, "Session staged", null); 1714 return; 1715 } 1716 1717 if (isApexInstallation()) { 1718 destroyInternal(); 1719 dispatchSessionFinished(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, 1720 "APEX packages can only be installed using staged sessions.", null); 1721 return; 1722 } 1723 1724 // For a multiPackage session, read the child sessions 1725 // outside of the lock, because reading the child 1726 // sessions with the lock held could lead to deadlock 1727 // (b/123391593). 1728 List<PackageInstallerSession> childSessions = getChildSessionsNotLocked(); 1729 1730 try { 1731 synchronized (mLock) { 1732 installNonStagedLocked(childSessions); 1733 } 1734 } catch (PackageManagerException e) { 1735 final String completeMsg = ExceptionUtils.getCompleteMessage(e); 1736 Slog.e(TAG, "Commit of session " + sessionId + " failed: " + completeMsg); 1737 destroyInternal(); 1738 dispatchSessionFinished(e.error, completeMsg, null); 1739 } 1740 } 1741 1742 @GuardedBy("mLock") installNonStagedLocked(List<PackageInstallerSession> childSessions)1743 private void installNonStagedLocked(List<PackageInstallerSession> childSessions) 1744 throws PackageManagerException { 1745 final PackageManagerService.ActiveInstallSession installingSession = 1746 makeSessionActiveLocked(); 1747 if (installingSession == null) { 1748 return; 1749 } 1750 if (isMultiPackage()) { 1751 List<PackageManagerService.ActiveInstallSession> installingChildSessions = 1752 new ArrayList<>(childSessions.size()); 1753 boolean success = true; 1754 PackageManagerException failure = null; 1755 for (int i = 0; i < childSessions.size(); ++i) { 1756 final PackageInstallerSession session = childSessions.get(i); 1757 try { 1758 final PackageManagerService.ActiveInstallSession installingChildSession = 1759 session.makeSessionActiveLocked(); 1760 if (installingChildSession != null) { 1761 installingChildSessions.add(installingChildSession); 1762 } 1763 } catch (PackageManagerException e) { 1764 failure = e; 1765 success = false; 1766 } 1767 } 1768 if (!success) { 1769 sendOnPackageInstalled(mContext, mRemoteStatusReceiver, sessionId, 1770 isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked(), userId, null, 1771 failure.error, failure.getLocalizedMessage(), null); 1772 return; 1773 } 1774 mPm.installStage(installingChildSessions); 1775 } else { 1776 mPm.installStage(installingSession); 1777 } 1778 } 1779 1780 /** 1781 * Stages this session for install and returns a 1782 * {@link PackageManagerService.ActiveInstallSession} representing this new staged state or null 1783 * in case permissions need to be requested before install can proceed. 1784 */ 1785 @GuardedBy("mLock") makeSessionActiveLocked()1786 private PackageManagerService.ActiveInstallSession makeSessionActiveLocked() 1787 throws PackageManagerException { 1788 if (mRelinquished) { 1789 throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, 1790 "Session relinquished"); 1791 } 1792 if (mDestroyed) { 1793 throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session destroyed"); 1794 } 1795 if (!mSealed) { 1796 throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session not sealed"); 1797 } 1798 1799 final IPackageInstallObserver2 localObserver; 1800 if (isApexInstallation()) { 1801 localObserver = null; 1802 } else { 1803 if (!params.isMultiPackage) { 1804 Objects.requireNonNull(mPackageName); 1805 Objects.requireNonNull(mSigningDetails); 1806 Objects.requireNonNull(mResolvedBaseFile); 1807 1808 if (needToAskForPermissionsLocked()) { 1809 // User needs to confirm installation; 1810 // give installer an intent they can use to involve 1811 // user. 1812 final Intent intent = new Intent(PackageInstaller.ACTION_CONFIRM_INSTALL); 1813 intent.setPackage(mPm.getPackageInstallerPackageName()); 1814 intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId); 1815 1816 sendOnUserActionRequired(mContext, mRemoteStatusReceiver, sessionId, intent); 1817 1818 // Commit was keeping session marked as active until now; release 1819 // that extra refcount so session appears idle. 1820 closeInternal(false); 1821 return null; 1822 } 1823 1824 // Inherit any packages and native libraries from existing install that 1825 // haven't been overridden. 1826 if (params.mode == SessionParams.MODE_INHERIT_EXISTING) { 1827 try { 1828 final List<File> fromFiles = mResolvedInheritedFiles; 1829 final File toDir = stageDir; 1830 1831 if (LOGD) Slog.d(TAG, "Inherited files: " + mResolvedInheritedFiles); 1832 if (!mResolvedInheritedFiles.isEmpty() && mInheritedFilesBase == null) { 1833 throw new IllegalStateException("mInheritedFilesBase == null"); 1834 } 1835 1836 if (isLinkPossible(fromFiles, toDir)) { 1837 if (!mResolvedInstructionSets.isEmpty()) { 1838 final File oatDir = new File(toDir, "oat"); 1839 createOatDirs(mResolvedInstructionSets, oatDir); 1840 } 1841 // pre-create lib dirs for linking if necessary 1842 if (!mResolvedNativeLibPaths.isEmpty()) { 1843 for (String libPath : mResolvedNativeLibPaths) { 1844 // "/lib/arm64" -> ["lib", "arm64"] 1845 final int splitIndex = libPath.lastIndexOf('/'); 1846 if (splitIndex < 0 || splitIndex >= libPath.length() - 1) { 1847 Slog.e(TAG, 1848 "Skipping native library creation for linking due" 1849 + " to invalid path: " + libPath); 1850 continue; 1851 } 1852 final String libDirPath = libPath.substring(1, splitIndex); 1853 final File libDir = new File(toDir, libDirPath); 1854 if (!libDir.exists()) { 1855 NativeLibraryHelper.createNativeLibrarySubdir(libDir); 1856 } 1857 final String archDirPath = libPath.substring(splitIndex + 1); 1858 NativeLibraryHelper.createNativeLibrarySubdir( 1859 new File(libDir, archDirPath)); 1860 } 1861 } 1862 linkFiles(fromFiles, toDir, mInheritedFilesBase); 1863 } else { 1864 // TODO: this should delegate to DCS so the system process 1865 // avoids holding open FDs into containers. 1866 copyFiles(fromFiles, toDir); 1867 } 1868 } catch (IOException e) { 1869 throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE, 1870 "Failed to inherit existing install", e); 1871 } 1872 } 1873 1874 // TODO: surface more granular state from dexopt 1875 mInternalProgress = 0.5f; 1876 computeProgressLocked(true); 1877 1878 extractNativeLibraries(stageDir, params.abiOverride, mayInheritNativeLibs()); 1879 } 1880 1881 // We've reached point of no return; call into PMS to install the stage. 1882 // Regardless of success or failure we always destroy session. 1883 localObserver = new IPackageInstallObserver2.Stub() { 1884 @Override 1885 public void onUserActionRequired(Intent intent) { 1886 throw new IllegalStateException(); 1887 } 1888 1889 @Override 1890 public void onPackageInstalled(String basePackageName, int returnCode, String msg, 1891 Bundle extras) { 1892 destroyInternal(); 1893 dispatchSessionFinished(returnCode, msg, extras); 1894 } 1895 }; 1896 } 1897 1898 final UserHandle user; 1899 if ((params.installFlags & PackageManager.INSTALL_ALL_USERS) != 0) { 1900 user = UserHandle.ALL; 1901 } else { 1902 user = new UserHandle(userId); 1903 } 1904 1905 mRelinquished = true; 1906 return new PackageManagerService.ActiveInstallSession(mPackageName, stageDir, localObserver, 1907 sessionId, params, mInstallerUid, mInstallSource, user, mSigningDetails); 1908 } 1909 maybeRenameFile(File from, File to)1910 private static void maybeRenameFile(File from, File to) throws PackageManagerException { 1911 if (!from.equals(to)) { 1912 if (!from.renameTo(to)) { 1913 throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, 1914 "Could not rename file " + from + " to " + to); 1915 } 1916 } 1917 } 1918 logDataLoaderInstallationSession(int returnCode)1919 private void logDataLoaderInstallationSession(int returnCode) { 1920 // Skip logging the side-loaded app installations, as those are private and aren't reported 1921 // anywhere; app stores already have a record of the installation and that's why reporting 1922 // it here is fine 1923 final String packageNameToLog = 1924 (params.installFlags & PackageManager.INSTALL_FROM_ADB) == 0 ? mPackageName : ""; 1925 final long currentTimestamp = System.currentTimeMillis(); 1926 FrameworkStatsLog.write(FrameworkStatsLog.PACKAGE_INSTALLER_V2_REPORTED, 1927 isIncrementalInstallation(), 1928 packageNameToLog, 1929 currentTimestamp - createdMillis, 1930 returnCode, 1931 getApksSize()); 1932 } 1933 getApksSize()1934 private long getApksSize() { 1935 final PackageSetting ps = mPm.getPackageSetting(mPackageName); 1936 if (ps == null) { 1937 return 0; 1938 } 1939 final File apkDirOrPath = ps.codePath; 1940 if (apkDirOrPath == null) { 1941 return 0; 1942 } 1943 if (apkDirOrPath.isFile() && apkDirOrPath.getName().toLowerCase().endsWith(".apk")) { 1944 return apkDirOrPath.length(); 1945 } 1946 if (!apkDirOrPath.isDirectory()) { 1947 return 0; 1948 } 1949 final File[] files = apkDirOrPath.listFiles(); 1950 long apksSize = 0; 1951 for (int i = 0; i < files.length; i++) { 1952 if (files[i].getName().toLowerCase().endsWith(".apk")) { 1953 apksSize += files[i].length(); 1954 } 1955 } 1956 return apksSize; 1957 } 1958 1959 /** 1960 * Returns true if the session should attempt to inherit any existing native libraries already 1961 * extracted at the current install location. This is necessary to prevent double loading of 1962 * native libraries already loaded by the running app. 1963 */ mayInheritNativeLibs()1964 private boolean mayInheritNativeLibs() { 1965 return SystemProperties.getBoolean(PROPERTY_NAME_INHERIT_NATIVE, true) && 1966 params.mode == SessionParams.MODE_INHERIT_EXISTING && 1967 (params.installFlags & PackageManager.DONT_KILL_APP) != 0; 1968 } 1969 1970 /** 1971 * Returns true if the session is installing an APEX package. 1972 */ isApexInstallation()1973 private boolean isApexInstallation() { 1974 return (params.installFlags & PackageManager.INSTALL_APEX) != 0; 1975 } 1976 1977 /** 1978 * Validate apex install. 1979 * <p> 1980 * Sets {@link #mResolvedBaseFile} for RollbackManager to use. Sets {@link #mPackageName} for 1981 * StagingManager to use. 1982 */ 1983 @GuardedBy("mLock") validateApexInstallLocked()1984 private void validateApexInstallLocked() 1985 throws PackageManagerException { 1986 final List<File> addedFiles = getAddedApksLocked(); 1987 if (addedFiles.isEmpty()) { 1988 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 1989 String.format("Session: %d. No packages staged in %s", sessionId, 1990 stageDir.getAbsolutePath())); 1991 } 1992 1993 if (ArrayUtils.size(addedFiles) > 1) { 1994 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 1995 "Too many files for apex install"); 1996 } 1997 1998 File addedFile = addedFiles.get(0); // there is only one file 1999 2000 // Ensure file name has proper suffix 2001 final String sourceName = addedFile.getName(); 2002 final String targetName = sourceName.endsWith(APEX_FILE_EXTENSION) 2003 ? sourceName 2004 : sourceName + APEX_FILE_EXTENSION; 2005 if (!FileUtils.isValidExtFilename(targetName)) { 2006 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 2007 "Invalid filename: " + targetName); 2008 } 2009 2010 final File targetFile = new File(stageDir, targetName); 2011 resolveAndStageFile(addedFile, targetFile); 2012 mResolvedBaseFile = targetFile; 2013 2014 // Populate package name of the apex session 2015 mPackageName = null; 2016 final ApkLite apk; 2017 try { 2018 apk = PackageParser.parseApkLite( 2019 mResolvedBaseFile, PackageParser.PARSE_COLLECT_CERTIFICATES); 2020 } catch (PackageParserException e) { 2021 throw PackageManagerException.from(e); 2022 } 2023 2024 if (mPackageName == null) { 2025 mPackageName = apk.packageName; 2026 mVersionCode = apk.getLongVersionCode(); 2027 } 2028 } 2029 2030 /** 2031 * Validate install by confirming that all application packages are have 2032 * consistent package name, version code, and signing certificates. 2033 * <p> 2034 * Clears and populates {@link #mResolvedBaseFile}, 2035 * {@link #mResolvedStagedFiles}, and {@link #mResolvedInheritedFiles}. 2036 * <p> 2037 * Renames package files in stage to match split names defined inside. 2038 * <p> 2039 * Note that upgrade compatibility is still performed by 2040 * {@link PackageManagerService}. 2041 */ 2042 @GuardedBy("mLock") validateApkInstallLocked()2043 private void validateApkInstallLocked() throws PackageManagerException { 2044 ApkLite baseApk = null; 2045 mPackageName = null; 2046 mVersionCode = -1; 2047 mSigningDetails = PackageParser.SigningDetails.UNKNOWN; 2048 2049 mResolvedBaseFile = null; 2050 mResolvedStagedFiles.clear(); 2051 mResolvedInheritedFiles.clear(); 2052 2053 final PackageInfo pkgInfo = mPm.getPackageInfo( 2054 params.appPackageName, PackageManager.GET_SIGNATURES 2055 | PackageManager.MATCH_STATIC_SHARED_LIBRARIES /*flags*/, userId); 2056 2057 // Partial installs must be consistent with existing install 2058 if (params.mode == SessionParams.MODE_INHERIT_EXISTING 2059 && (pkgInfo == null || pkgInfo.applicationInfo == null)) { 2060 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 2061 "Missing existing base package"); 2062 } 2063 // Default to require only if existing base has fs-verity. 2064 mVerityFound = PackageManagerServiceUtils.isApkVerityEnabled() 2065 && params.mode == SessionParams.MODE_INHERIT_EXISTING 2066 && VerityUtils.hasFsverity(pkgInfo.applicationInfo.getBaseCodePath()); 2067 2068 final List<File> removedFiles = getRemovedFilesLocked(); 2069 final List<String> removeSplitList = new ArrayList<>(); 2070 if (!removedFiles.isEmpty()) { 2071 for (File removedFile : removedFiles) { 2072 final String fileName = removedFile.getName(); 2073 final String splitName = fileName.substring( 2074 0, fileName.length() - REMOVE_MARKER_EXTENSION.length()); 2075 removeSplitList.add(splitName); 2076 } 2077 } 2078 2079 final List<File> addedFiles = getAddedApksLocked(); 2080 if (addedFiles.isEmpty() && removeSplitList.size() == 0) { 2081 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 2082 String.format("Session: %d. No packages staged in %s", sessionId, 2083 stageDir.getAbsolutePath())); 2084 } 2085 2086 // Verify that all staged packages are internally consistent 2087 final ArraySet<String> stagedSplits = new ArraySet<>(); 2088 ParseTypeImpl input = ParseTypeImpl.forDefaultParsing(); 2089 for (File addedFile : addedFiles) { 2090 ParseResult<ApkLite> result = ApkLiteParseUtils.parseApkLite(input.reset(), 2091 addedFile, PackageParser.PARSE_COLLECT_CERTIFICATES); 2092 if (result.isError()) { 2093 throw new PackageManagerException(result.getErrorCode(), 2094 result.getErrorMessage(), result.getException()); 2095 } 2096 2097 final ApkLite apk = result.getResult(); 2098 if (!stagedSplits.add(apk.splitName)) { 2099 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 2100 "Split " + apk.splitName + " was defined multiple times"); 2101 } 2102 2103 // Use first package to define unknown values 2104 if (mPackageName == null) { 2105 mPackageName = apk.packageName; 2106 mVersionCode = apk.getLongVersionCode(); 2107 } 2108 if (mSigningDetails == PackageParser.SigningDetails.UNKNOWN) { 2109 mSigningDetails = apk.signingDetails; 2110 } 2111 2112 assertApkConsistentLocked(String.valueOf(addedFile), apk); 2113 2114 // Take this opportunity to enforce uniform naming 2115 final String targetName; 2116 if (apk.splitName == null) { 2117 targetName = "base" + APK_FILE_EXTENSION; 2118 } else { 2119 targetName = "split_" + apk.splitName + APK_FILE_EXTENSION; 2120 } 2121 if (!FileUtils.isValidExtFilename(targetName)) { 2122 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 2123 "Invalid filename: " + targetName); 2124 } 2125 2126 final File targetFile = new File(stageDir, targetName); 2127 resolveAndStageFile(addedFile, targetFile); 2128 2129 // Base is coming from session 2130 if (apk.splitName == null) { 2131 mResolvedBaseFile = targetFile; 2132 baseApk = apk; 2133 } 2134 2135 // Validate and add Dex Metadata (.dm). 2136 final File dexMetadataFile = DexMetadataHelper.findDexMetadataForFile(addedFile); 2137 if (dexMetadataFile != null) { 2138 if (!FileUtils.isValidExtFilename(dexMetadataFile.getName())) { 2139 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 2140 "Invalid filename: " + dexMetadataFile); 2141 } 2142 final File targetDexMetadataFile = new File(stageDir, 2143 DexMetadataHelper.buildDexMetadataPathForApk(targetName)); 2144 resolveAndStageFile(dexMetadataFile, targetDexMetadataFile); 2145 } 2146 } 2147 2148 if (removeSplitList.size() > 0) { 2149 if (pkgInfo == null) { 2150 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 2151 "Missing existing base package for " + mPackageName); 2152 } 2153 2154 // validate split names marked for removal 2155 for (String splitName : removeSplitList) { 2156 if (!ArrayUtils.contains(pkgInfo.splitNames, splitName)) { 2157 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 2158 "Split not found: " + splitName); 2159 } 2160 } 2161 2162 // ensure we've got appropriate package name, version code and signatures 2163 if (mPackageName == null) { 2164 mPackageName = pkgInfo.packageName; 2165 mVersionCode = pkgInfo.getLongVersionCode(); 2166 } 2167 if (mSigningDetails == PackageParser.SigningDetails.UNKNOWN) { 2168 try { 2169 mSigningDetails = ApkSignatureVerifier.unsafeGetCertsWithoutVerification( 2170 pkgInfo.applicationInfo.sourceDir, 2171 PackageParser.SigningDetails.SignatureSchemeVersion.JAR); 2172 } catch (PackageParserException e) { 2173 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 2174 "Couldn't obtain signatures from base APK"); 2175 } 2176 } 2177 } 2178 2179 if (mInstallerUid != mOriginalInstallerUid) { 2180 // Session has been transferred, check package name. 2181 if (TextUtils.isEmpty(mPackageName) || !mPackageName.equals( 2182 mOriginalInstallerPackageName)) { 2183 throw new PackageManagerException(PackageManager.INSTALL_FAILED_PACKAGE_CHANGED, 2184 "Can only transfer sessions that update the original installer"); 2185 } 2186 } 2187 2188 if (params.mode == SessionParams.MODE_FULL_INSTALL) { 2189 // Full installs must include a base package 2190 if (!stagedSplits.contains(null)) { 2191 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 2192 "Full install must include a base package"); 2193 } 2194 2195 } else { 2196 ApplicationInfo appInfo = pkgInfo.applicationInfo; 2197 ParseResult<PackageLite> pkgLiteResult = ApkLiteParseUtils.parsePackageLite( 2198 input.reset(), new File(appInfo.getCodePath()), 0); 2199 if (pkgLiteResult.isError()) { 2200 throw new PackageManagerException(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, 2201 pkgLiteResult.getErrorMessage(), pkgLiteResult.getException()); 2202 } 2203 final PackageLite existing = pkgLiteResult.getResult(); 2204 ParseResult<ApkLite> apkLiteResult = ApkLiteParseUtils.parseApkLite(input.reset(), 2205 new File(appInfo.getBaseCodePath()), 2206 PackageParser.PARSE_COLLECT_CERTIFICATES); 2207 if (apkLiteResult.isError()) { 2208 throw new PackageManagerException(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, 2209 apkLiteResult.getErrorMessage(), apkLiteResult.getException()); 2210 } 2211 final ApkLite existingBase = apkLiteResult.getResult(); 2212 2213 assertApkConsistentLocked("Existing base", existingBase); 2214 2215 // Inherit base if not overridden 2216 if (mResolvedBaseFile == null) { 2217 mResolvedBaseFile = new File(appInfo.getBaseCodePath()); 2218 resolveInheritedFile(mResolvedBaseFile); 2219 // Inherit the dex metadata if present. 2220 final File baseDexMetadataFile = 2221 DexMetadataHelper.findDexMetadataForFile(mResolvedBaseFile); 2222 if (baseDexMetadataFile != null) { 2223 resolveInheritedFile(baseDexMetadataFile); 2224 } 2225 baseApk = existingBase; 2226 } 2227 2228 // Inherit splits if not overridden 2229 if (!ArrayUtils.isEmpty(existing.splitNames)) { 2230 for (int i = 0; i < existing.splitNames.length; i++) { 2231 final String splitName = existing.splitNames[i]; 2232 final File splitFile = new File(existing.splitCodePaths[i]); 2233 final boolean splitRemoved = removeSplitList.contains(splitName); 2234 if (!stagedSplits.contains(splitName) && !splitRemoved) { 2235 resolveInheritedFile(splitFile); 2236 // Inherit the dex metadata if present. 2237 final File splitDexMetadataFile = 2238 DexMetadataHelper.findDexMetadataForFile(splitFile); 2239 if (splitDexMetadataFile != null) { 2240 resolveInheritedFile(splitDexMetadataFile); 2241 } 2242 } 2243 } 2244 } 2245 2246 // Inherit compiled oat directory. 2247 final File packageInstallDir = (new File(appInfo.getBaseCodePath())).getParentFile(); 2248 mInheritedFilesBase = packageInstallDir; 2249 final File oatDir = new File(packageInstallDir, "oat"); 2250 if (oatDir.exists()) { 2251 final File[] archSubdirs = oatDir.listFiles(); 2252 2253 // Keep track of all instruction sets we've seen compiled output for. 2254 // If we're linking (and not copying) inherited files, we can recreate the 2255 // instruction set hierarchy and link compiled output. 2256 if (archSubdirs != null && archSubdirs.length > 0) { 2257 final String[] instructionSets = InstructionSets.getAllDexCodeInstructionSets(); 2258 for (File archSubDir : archSubdirs) { 2259 // Skip any directory that isn't an ISA subdir. 2260 if (!ArrayUtils.contains(instructionSets, archSubDir.getName())) { 2261 continue; 2262 } 2263 2264 File[] files = archSubDir.listFiles(); 2265 if (files == null || files.length == 0) { 2266 continue; 2267 } 2268 2269 mResolvedInstructionSets.add(archSubDir.getName()); 2270 mResolvedInheritedFiles.addAll(Arrays.asList(files)); 2271 } 2272 } 2273 } 2274 2275 // Inherit native libraries for DONT_KILL sessions. 2276 if (mayInheritNativeLibs() && removeSplitList.isEmpty()) { 2277 File[] libDirs = new File[]{ 2278 new File(packageInstallDir, NativeLibraryHelper.LIB_DIR_NAME), 2279 new File(packageInstallDir, NativeLibraryHelper.LIB64_DIR_NAME)}; 2280 for (File libDir : libDirs) { 2281 if (!libDir.exists() || !libDir.isDirectory()) { 2282 continue; 2283 } 2284 final List<String> libDirsToInherit = new ArrayList<>(); 2285 final List<File> libFilesToInherit = new ArrayList<>(); 2286 for (File archSubDir : libDir.listFiles()) { 2287 if (!archSubDir.isDirectory()) { 2288 continue; 2289 } 2290 String relLibPath; 2291 try { 2292 relLibPath = getRelativePath(archSubDir, packageInstallDir); 2293 } catch (IOException e) { 2294 Slog.e(TAG, "Skipping linking of native library directory!", e); 2295 // shouldn't be possible, but let's avoid inheriting these to be safe 2296 libDirsToInherit.clear(); 2297 libFilesToInherit.clear(); 2298 break; 2299 } 2300 2301 File[] files = archSubDir.listFiles(); 2302 if (files == null || files.length == 0) { 2303 continue; 2304 } 2305 2306 libDirsToInherit.add(relLibPath); 2307 libFilesToInherit.addAll(Arrays.asList(files)); 2308 } 2309 for (String subDir : libDirsToInherit) { 2310 if (!mResolvedNativeLibPaths.contains(subDir)) { 2311 mResolvedNativeLibPaths.add(subDir); 2312 } 2313 } 2314 mResolvedInheritedFiles.addAll(libFilesToInherit); 2315 } 2316 } 2317 } 2318 if (baseApk.useEmbeddedDex) { 2319 for (File file : mResolvedStagedFiles) { 2320 if (file.getName().endsWith(".apk") 2321 && !DexManager.auditUncompressedDexInApk(file.getPath())) { 2322 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 2323 "Some dex are not uncompressed and aligned correctly for " 2324 + mPackageName); 2325 } 2326 } 2327 } 2328 if (baseApk.isSplitRequired && stagedSplits.size() <= 1) { 2329 throw new PackageManagerException(INSTALL_FAILED_MISSING_SPLIT, 2330 "Missing split for " + mPackageName); 2331 } 2332 2333 final boolean isInstallerShell = (mInstallerUid == Process.SHELL_UID); 2334 if (isInstallerShell && isIncrementalInstallation() && mIncrementalFileStorages != null) { 2335 if (!baseApk.debuggable && !baseApk.profilableByShell) { 2336 mIncrementalFileStorages.disableReadLogs(); 2337 } 2338 } 2339 } 2340 resolveAndStageFile(File origFile, File targetFile)2341 private void resolveAndStageFile(File origFile, File targetFile) 2342 throws PackageManagerException { 2343 mResolvedStagedFiles.add(targetFile); 2344 maybeRenameFile(origFile, targetFile); 2345 2346 final File originalSignature = new File( 2347 VerityUtils.getFsveritySignatureFilePath(origFile.getPath())); 2348 // Make sure .fsv_sig exists when it should, then resolve and stage it. 2349 if (originalSignature.exists()) { 2350 // mVerityFound can only change from false to true here during the staging loop. Since 2351 // all or none of files should have .fsv_sig, this should only happen in the first time 2352 // (or never), otherwise bail out. 2353 if (!mVerityFound) { 2354 mVerityFound = true; 2355 if (mResolvedStagedFiles.size() > 1) { 2356 throw new PackageManagerException(INSTALL_FAILED_BAD_SIGNATURE, 2357 "Some file is missing fs-verity signature"); 2358 } 2359 } 2360 } else { 2361 if (!mVerityFound) { 2362 return; 2363 } 2364 throw new PackageManagerException(INSTALL_FAILED_BAD_SIGNATURE, 2365 "Missing corresponding fs-verity signature to " + origFile); 2366 } 2367 2368 final File stagedSignature = new File( 2369 VerityUtils.getFsveritySignatureFilePath(targetFile.getPath())); 2370 maybeRenameFile(originalSignature, stagedSignature); 2371 mResolvedStagedFiles.add(stagedSignature); 2372 } 2373 resolveInheritedFile(File origFile)2374 private void resolveInheritedFile(File origFile) { 2375 mResolvedInheritedFiles.add(origFile); 2376 2377 // Inherit the fsverity signature file if present. 2378 final File fsveritySignatureFile = new File( 2379 VerityUtils.getFsveritySignatureFilePath(origFile.getPath())); 2380 if (fsveritySignatureFile.exists()) { 2381 mResolvedInheritedFiles.add(fsveritySignatureFile); 2382 } 2383 } 2384 2385 @GuardedBy("mLock") assertApkConsistentLocked(String tag, ApkLite apk)2386 private void assertApkConsistentLocked(String tag, ApkLite apk) 2387 throws PackageManagerException { 2388 if (!mPackageName.equals(apk.packageName)) { 2389 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag + " package " 2390 + apk.packageName + " inconsistent with " + mPackageName); 2391 } 2392 if (params.appPackageName != null && !params.appPackageName.equals(apk.packageName)) { 2393 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag 2394 + " specified package " + params.appPackageName 2395 + " inconsistent with " + apk.packageName); 2396 } 2397 if (mVersionCode != apk.getLongVersionCode()) { 2398 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag 2399 + " version code " + apk.versionCode + " inconsistent with " 2400 + mVersionCode); 2401 } 2402 if (!mSigningDetails.signaturesMatchExactly(apk.signingDetails)) { 2403 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 2404 tag + " signatures are inconsistent"); 2405 } 2406 } 2407 2408 /** 2409 * Determine if creating hard links between source and destination is 2410 * possible. That is, do they all live on the same underlying device. 2411 */ isLinkPossible(List<File> fromFiles, File toDir)2412 private boolean isLinkPossible(List<File> fromFiles, File toDir) { 2413 try { 2414 final StructStat toStat = Os.stat(toDir.getAbsolutePath()); 2415 for (File fromFile : fromFiles) { 2416 final StructStat fromStat = Os.stat(fromFile.getAbsolutePath()); 2417 if (fromStat.st_dev != toStat.st_dev) { 2418 return false; 2419 } 2420 } 2421 } catch (ErrnoException e) { 2422 Slog.w(TAG, "Failed to detect if linking possible: " + e); 2423 return false; 2424 } 2425 return true; 2426 } 2427 2428 /** 2429 * @return the uid of the owner this session 2430 */ getInstallerUid()2431 public int getInstallerUid() { 2432 synchronized (mLock) { 2433 return mInstallerUid; 2434 } 2435 } 2436 2437 /** 2438 * @return the package name of this session 2439 */ getPackageName()2440 String getPackageName() { 2441 synchronized (mLock) { 2442 return mPackageName; 2443 } 2444 } 2445 2446 /** 2447 * @return the timestamp of when this session last changed state 2448 */ getUpdatedMillis()2449 public long getUpdatedMillis() { 2450 synchronized (mLock) { 2451 return updatedMillis; 2452 } 2453 } 2454 getInstallerPackageName()2455 String getInstallerPackageName() { 2456 return getInstallSource().installerPackageName; 2457 } 2458 getInstallSource()2459 InstallSource getInstallSource() { 2460 synchronized (mLock) { 2461 return mInstallSource; 2462 } 2463 } 2464 getRelativePath(File file, File base)2465 private static String getRelativePath(File file, File base) throws IOException { 2466 final String pathStr = file.getAbsolutePath(); 2467 final String baseStr = base.getAbsolutePath(); 2468 // Don't allow relative paths. 2469 if (pathStr.contains("/.") ) { 2470 throw new IOException("Invalid path (was relative) : " + pathStr); 2471 } 2472 2473 if (pathStr.startsWith(baseStr)) { 2474 return pathStr.substring(baseStr.length()); 2475 } 2476 2477 throw new IOException("File: " + pathStr + " outside base: " + baseStr); 2478 } 2479 createOatDirs(List<String> instructionSets, File fromDir)2480 private void createOatDirs(List<String> instructionSets, File fromDir) 2481 throws PackageManagerException { 2482 for (String instructionSet : instructionSets) { 2483 try { 2484 mPm.mInstaller.createOatDir(fromDir.getAbsolutePath(), instructionSet); 2485 } catch (InstallerException e) { 2486 throw PackageManagerException.from(e); 2487 } 2488 } 2489 } 2490 linkFiles(List<File> fromFiles, File toDir, File fromDir)2491 private void linkFiles(List<File> fromFiles, File toDir, File fromDir) 2492 throws IOException { 2493 for (File fromFile : fromFiles) { 2494 final String relativePath = getRelativePath(fromFile, fromDir); 2495 try { 2496 mPm.mInstaller.linkFile(relativePath, fromDir.getAbsolutePath(), 2497 toDir.getAbsolutePath()); 2498 } catch (InstallerException e) { 2499 throw new IOException("failed linkOrCreateDir(" + relativePath + ", " 2500 + fromDir + ", " + toDir + ")", e); 2501 } 2502 } 2503 2504 Slog.d(TAG, "Linked " + fromFiles.size() + " files into " + toDir); 2505 } 2506 copyFiles(List<File> fromFiles, File toDir)2507 private static void copyFiles(List<File> fromFiles, File toDir) throws IOException { 2508 // Remove any partial files from previous attempt 2509 for (File file : toDir.listFiles()) { 2510 if (file.getName().endsWith(".tmp")) { 2511 file.delete(); 2512 } 2513 } 2514 2515 for (File fromFile : fromFiles) { 2516 final File tmpFile = File.createTempFile("inherit", ".tmp", toDir); 2517 if (LOGD) Slog.d(TAG, "Copying " + fromFile + " to " + tmpFile); 2518 if (!FileUtils.copyFile(fromFile, tmpFile)) { 2519 throw new IOException("Failed to copy " + fromFile + " to " + tmpFile); 2520 } 2521 try { 2522 Os.chmod(tmpFile.getAbsolutePath(), 0644); 2523 } catch (ErrnoException e) { 2524 throw new IOException("Failed to chmod " + tmpFile); 2525 } 2526 final File toFile = new File(toDir, fromFile.getName()); 2527 if (LOGD) Slog.d(TAG, "Renaming " + tmpFile + " to " + toFile); 2528 if (!tmpFile.renameTo(toFile)) { 2529 throw new IOException("Failed to rename " + tmpFile + " to " + toFile); 2530 } 2531 } 2532 Slog.d(TAG, "Copied " + fromFiles.size() + " files into " + toDir); 2533 } 2534 extractNativeLibraries(File packageDir, String abiOverride, boolean inherit)2535 private void extractNativeLibraries(File packageDir, String abiOverride, boolean inherit) 2536 throws PackageManagerException { 2537 final File libDir = new File(packageDir, NativeLibraryHelper.LIB_DIR_NAME); 2538 if (!inherit) { 2539 // Start from a clean slate 2540 NativeLibraryHelper.removeNativeBinariesFromDirLI(libDir, true); 2541 } 2542 2543 NativeLibraryHelper.Handle handle = null; 2544 try { 2545 handle = NativeLibraryHelper.Handle.create(packageDir); 2546 final int res = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libDir, 2547 abiOverride, isIncrementalInstallation()); 2548 if (res != PackageManager.INSTALL_SUCCEEDED) { 2549 throw new PackageManagerException(res, 2550 "Failed to extract native libraries, res=" + res); 2551 } 2552 } catch (IOException e) { 2553 throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, 2554 "Failed to extract native libraries", e); 2555 } finally { 2556 IoUtils.closeQuietly(handle); 2557 } 2558 } 2559 setPermissionsResult(boolean accepted)2560 void setPermissionsResult(boolean accepted) { 2561 if (!mSealed) { 2562 throw new SecurityException("Must be sealed to accept permissions"); 2563 } 2564 2565 if (accepted) { 2566 // Mark and kick off another install pass 2567 synchronized (mLock) { 2568 mPermissionsManuallyAccepted = true; 2569 mHandler.obtainMessage(MSG_INSTALL).sendToTarget(); 2570 } 2571 } else { 2572 destroyInternal(); 2573 dispatchSessionFinished(INSTALL_FAILED_ABORTED, "User rejected permissions", null); 2574 } 2575 } 2576 2577 /** 2578 * Adds a child session ID without any safety / sanity checks. This should only be used to 2579 * build a session from XML or similar. 2580 */ addChildSessionIdInternal(int sessionId)2581 void addChildSessionIdInternal(int sessionId) { 2582 mChildSessionIds.put(sessionId, 0); 2583 } 2584 open()2585 public void open() throws IOException { 2586 if (mActiveCount.getAndIncrement() == 0) { 2587 mCallback.onSessionActiveChanged(this, true); 2588 } 2589 2590 boolean wasPrepared; 2591 synchronized (mLock) { 2592 wasPrepared = mPrepared; 2593 if (!mPrepared) { 2594 if (stageDir != null) { 2595 prepareStageDir(stageDir); 2596 } else if (params.isMultiPackage) { 2597 // it's all ok 2598 } else { 2599 throw new IllegalArgumentException("stageDir must be set"); 2600 } 2601 2602 mPrepared = true; 2603 } 2604 } 2605 2606 if (!wasPrepared) { 2607 mCallback.onSessionPrepared(this); 2608 } 2609 } 2610 2611 @Override close()2612 public void close() { 2613 closeInternal(true); 2614 } 2615 closeInternal(boolean checkCaller)2616 private void closeInternal(boolean checkCaller) { 2617 int activeCount; 2618 synchronized (mLock) { 2619 if (checkCaller) { 2620 assertCallerIsOwnerOrRootLocked(); 2621 } 2622 2623 activeCount = mActiveCount.decrementAndGet(); 2624 } 2625 2626 if (activeCount == 0) { 2627 mCallback.onSessionActiveChanged(this, false); 2628 } 2629 } 2630 2631 @Override abandon()2632 public void abandon() { 2633 if (hasParentSessionId()) { 2634 throw new IllegalStateException( 2635 "Session " + sessionId + " is a child of multi-package session " 2636 + mParentSessionId + " and may not be abandoned directly."); 2637 } 2638 2639 List<PackageInstallerSession> childSessions = getChildSessionsNotLocked(); 2640 synchronized (mLock) { 2641 if (params.isStaged && mDestroyed) { 2642 // If a user abandons staged session in an unsafe state, then system will try to 2643 // abandon the destroyed staged session when it is safe on behalf of the user. 2644 assertCallerIsOwnerOrRootOrSystemLocked(); 2645 } else { 2646 assertCallerIsOwnerOrRootLocked(); 2647 } 2648 2649 if (isStagedAndInTerminalState()) { 2650 // We keep the session in the database if it's in a finalized state. It will be 2651 // removed by PackageInstallerService when the last update time is old enough. 2652 // Also, in such cases cleanStageDir() has already been executed so no need to 2653 // do it now. 2654 return; 2655 } 2656 if (mCommitted && params.isStaged) { 2657 mDestroyed = true; 2658 if (!mStagingManager.abortCommittedSessionLocked(this)) { 2659 // Do not clean up the staged session from system. It is not safe yet. 2660 mCallback.onStagedSessionChanged(this); 2661 return; 2662 } 2663 cleanStageDir(childSessions); 2664 } 2665 2666 if (mRelinquished) { 2667 Slog.d(TAG, "Ignoring abandon after commit relinquished control"); 2668 return; 2669 } 2670 destroyInternal(); 2671 } 2672 dispatchSessionFinished(INSTALL_FAILED_ABORTED, "Session was abandoned", null); 2673 } 2674 2675 @Override isMultiPackage()2676 public boolean isMultiPackage() { 2677 return params.isMultiPackage; 2678 } 2679 2680 @Override isStaged()2681 public boolean isStaged() { 2682 return params.isStaged; 2683 } 2684 2685 @Override getDataLoaderParams()2686 public DataLoaderParamsParcel getDataLoaderParams() { 2687 mContext.enforceCallingOrSelfPermission(Manifest.permission.USE_INSTALLER_V2, null); 2688 return params.dataLoaderParams != null ? params.dataLoaderParams.getData() : null; 2689 } 2690 2691 @Override addFile(int location, String name, long lengthBytes, byte[] metadata, byte[] signature)2692 public void addFile(int location, String name, long lengthBytes, byte[] metadata, 2693 byte[] signature) { 2694 mContext.enforceCallingOrSelfPermission(Manifest.permission.USE_INSTALLER_V2, null); 2695 if (!isDataLoaderInstallation()) { 2696 throw new IllegalStateException( 2697 "Cannot add files to non-data loader installation session."); 2698 } 2699 if (isStreamingInstallation()) { 2700 if (location != LOCATION_DATA_APP) { 2701 throw new IllegalArgumentException( 2702 "Non-incremental installation only supports /data/app placement: " + name); 2703 } 2704 } 2705 if (metadata == null) { 2706 throw new IllegalArgumentException( 2707 "DataLoader installation requires valid metadata: " + name); 2708 } 2709 // Use installer provided name for now; we always rename later 2710 if (!FileUtils.isValidExtFilename(name)) { 2711 throw new IllegalArgumentException("Invalid name: " + name); 2712 } 2713 2714 synchronized (mLock) { 2715 assertCallerIsOwnerOrRootLocked(); 2716 assertPreparedAndNotSealedLocked("addFile"); 2717 2718 if (!mFiles.add(new FileEntry(mFiles.size(), 2719 new InstallationFile(location, name, lengthBytes, metadata, signature)))) { 2720 throw new IllegalArgumentException("File already added: " + name); 2721 } 2722 } 2723 } 2724 2725 @Override removeFile(int location, String name)2726 public void removeFile(int location, String name) { 2727 mContext.enforceCallingOrSelfPermission(Manifest.permission.USE_INSTALLER_V2, null); 2728 if (!isDataLoaderInstallation()) { 2729 throw new IllegalStateException( 2730 "Cannot add files to non-data loader installation session."); 2731 } 2732 if (TextUtils.isEmpty(params.appPackageName)) { 2733 throw new IllegalStateException("Must specify package name to remove a split"); 2734 } 2735 2736 synchronized (mLock) { 2737 assertCallerIsOwnerOrRootLocked(); 2738 assertPreparedAndNotSealedLocked("removeFile"); 2739 2740 if (!mFiles.add(new FileEntry(mFiles.size(), 2741 new InstallationFile(location, getRemoveMarkerName(name), -1, null, null)))) { 2742 throw new IllegalArgumentException("File already removed: " + name); 2743 } 2744 } 2745 } 2746 2747 /** 2748 * Makes sure files are present in staging location. 2749 * @return if the image is ready for installation 2750 */ 2751 @GuardedBy("mLock") prepareDataLoaderLocked()2752 private boolean prepareDataLoaderLocked() 2753 throws PackageManagerException { 2754 if (!isDataLoaderInstallation()) { 2755 return true; 2756 } 2757 if (mDataLoaderFinished) { 2758 return true; 2759 } 2760 2761 // Retrying commit. 2762 if (mIncrementalFileStorages != null) { 2763 try { 2764 mIncrementalFileStorages.startLoading(); 2765 } catch (IOException e) { 2766 throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE, e.getMessage(), 2767 e.getCause()); 2768 } 2769 return false; 2770 } 2771 2772 final List<InstallationFileParcel> addedFiles = new ArrayList<>(); 2773 final List<String> removedFiles = new ArrayList<>(); 2774 2775 final InstallationFile[] files = getInstallationFilesLocked(); 2776 for (InstallationFile file : files) { 2777 if (sAddedFilter.accept(new File(this.stageDir, file.getName()))) { 2778 addedFiles.add(file.getData()); 2779 continue; 2780 } 2781 if (sRemovedFilter.accept(new File(this.stageDir, file.getName()))) { 2782 String name = file.getName().substring( 2783 0, file.getName().length() - REMOVE_MARKER_EXTENSION.length()); 2784 removedFiles.add(name); 2785 } 2786 } 2787 2788 final DataLoaderManager dataLoaderManager = mContext.getSystemService( 2789 DataLoaderManager.class); 2790 if (dataLoaderManager == null) { 2791 throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE, 2792 "Failed to find data loader manager service"); 2793 } 2794 2795 final DataLoaderParams params = this.params.dataLoaderParams; 2796 final boolean manualStartAndDestroy = !isIncrementalInstallation(); 2797 final IDataLoaderStatusListener statusListener = new IDataLoaderStatusListener.Stub() { 2798 @Override 2799 public void onStatusChanged(int dataLoaderId, int status) { 2800 switch (status) { 2801 case IDataLoaderStatusListener.DATA_LOADER_STOPPED: 2802 case IDataLoaderStatusListener.DATA_LOADER_DESTROYED: 2803 return; 2804 } 2805 2806 if (mDestroyed || mDataLoaderFinished) { 2807 switch (status) { 2808 case IDataLoaderStatusListener.DATA_LOADER_UNRECOVERABLE: 2809 onStorageUnhealthy(); 2810 return; 2811 } 2812 return; 2813 } 2814 2815 try { 2816 IDataLoader dataLoader = dataLoaderManager.getDataLoader(dataLoaderId); 2817 if (dataLoader == null) { 2818 mDataLoaderFinished = true; 2819 dispatchSessionVerificationFailure(INSTALL_FAILED_MEDIA_UNAVAILABLE, 2820 "Failure to obtain data loader"); 2821 return; 2822 } 2823 2824 switch (status) { 2825 case IDataLoaderStatusListener.DATA_LOADER_BOUND: { 2826 if (manualStartAndDestroy) { 2827 FileSystemControlParcel control = new FileSystemControlParcel(); 2828 control.callback = new FileSystemConnector(addedFiles); 2829 dataLoader.create(dataLoaderId, params.getData(), control, this); 2830 } 2831 2832 break; 2833 } 2834 case IDataLoaderStatusListener.DATA_LOADER_CREATED: { 2835 if (manualStartAndDestroy) { 2836 // IncrementalFileStorages will call start after all files are 2837 // created in IncFS. 2838 dataLoader.start(dataLoaderId); 2839 } 2840 break; 2841 } 2842 case IDataLoaderStatusListener.DATA_LOADER_STARTED: { 2843 dataLoader.prepareImage( 2844 dataLoaderId, 2845 addedFiles.toArray( 2846 new InstallationFileParcel[addedFiles.size()]), 2847 removedFiles.toArray(new String[removedFiles.size()])); 2848 break; 2849 } 2850 case IDataLoaderStatusListener.DATA_LOADER_IMAGE_READY: { 2851 mDataLoaderFinished = true; 2852 if (hasParentSessionId()) { 2853 mSessionProvider.getSession( 2854 mParentSessionId).dispatchStreamValidateAndCommit(); 2855 } else { 2856 dispatchStreamValidateAndCommit(); 2857 } 2858 if (manualStartAndDestroy) { 2859 dataLoader.destroy(dataLoaderId); 2860 } 2861 break; 2862 } 2863 case IDataLoaderStatusListener.DATA_LOADER_IMAGE_NOT_READY: { 2864 mDataLoaderFinished = true; 2865 dispatchSessionVerificationFailure(INSTALL_FAILED_MEDIA_UNAVAILABLE, 2866 "Failed to prepare image."); 2867 if (manualStartAndDestroy) { 2868 dataLoader.destroy(dataLoaderId); 2869 } 2870 break; 2871 } 2872 case IDataLoaderStatusListener.DATA_LOADER_UNAVAILABLE: { 2873 // Don't fail or commit the session. Allow caller to commit again. 2874 sendPendingStreaming("DataLoader unavailable"); 2875 break; 2876 } 2877 case IDataLoaderStatusListener.DATA_LOADER_UNRECOVERABLE: 2878 mDataLoaderFinished = true; 2879 dispatchSessionVerificationFailure(INSTALL_FAILED_MEDIA_UNAVAILABLE, 2880 "DataLoader reported unrecoverable failure."); 2881 break; 2882 } 2883 } catch (RemoteException e) { 2884 // In case of streaming failure we don't want to fail or commit the session. 2885 // Just return from this method and allow caller to commit again. 2886 sendPendingStreaming(e.getMessage()); 2887 } 2888 } 2889 }; 2890 2891 if (!manualStartAndDestroy) { 2892 final StorageHealthCheckParams healthCheckParams = new StorageHealthCheckParams(); 2893 healthCheckParams.blockedTimeoutMs = INCREMENTAL_STORAGE_BLOCKED_TIMEOUT_MS; 2894 healthCheckParams.unhealthyTimeoutMs = INCREMENTAL_STORAGE_UNHEALTHY_TIMEOUT_MS; 2895 healthCheckParams.unhealthyMonitoringMs = INCREMENTAL_STORAGE_UNHEALTHY_MONITORING_MS; 2896 2897 final boolean systemDataLoader = 2898 params.getComponentName().getPackageName() == SYSTEM_DATA_LOADER_PACKAGE; 2899 final IStorageHealthListener healthListener = new IStorageHealthListener.Stub() { 2900 @Override 2901 public void onHealthStatus(int storageId, int status) { 2902 if (mDestroyed || mDataLoaderFinished) { 2903 // App's installed. 2904 switch (status) { 2905 case IStorageHealthListener.HEALTH_STATUS_UNHEALTHY: 2906 onStorageUnhealthy(); 2907 return; 2908 } 2909 return; 2910 } 2911 2912 switch (status) { 2913 case IStorageHealthListener.HEALTH_STATUS_OK: 2914 break; 2915 case IStorageHealthListener.HEALTH_STATUS_READS_PENDING: 2916 case IStorageHealthListener.HEALTH_STATUS_BLOCKED: 2917 if (systemDataLoader) { 2918 // It's OK for ADB data loader to wait for pages. 2919 break; 2920 } 2921 // fallthrough 2922 case IStorageHealthListener.HEALTH_STATUS_UNHEALTHY: 2923 // Even ADB installation can't wait for missing pages for too long. 2924 mDataLoaderFinished = true; 2925 dispatchSessionVerificationFailure(INSTALL_FAILED_MEDIA_UNAVAILABLE, 2926 "Image is missing pages required for installation."); 2927 break; 2928 } 2929 } 2930 }; 2931 2932 try { 2933 mIncrementalFileStorages = IncrementalFileStorages.initialize(mContext, stageDir, 2934 params, statusListener, healthCheckParams, healthListener, addedFiles); 2935 return false; 2936 } catch (IOException e) { 2937 throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE, e.getMessage(), 2938 e.getCause()); 2939 } 2940 } 2941 2942 if (!dataLoaderManager.bindToDataLoader(sessionId, params.getData(), statusListener)) { 2943 throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE, 2944 "Failed to initialize data loader"); 2945 } 2946 2947 return false; 2948 } 2949 dispatchSessionVerificationFailure(int error, String detailMessage)2950 private void dispatchSessionVerificationFailure(int error, String detailMessage) { 2951 mHandler.obtainMessage(MSG_SESSION_VERIFICATION_FAILURE, error, -1, 2952 detailMessage).sendToTarget(); 2953 } 2954 2955 @Override getChildSessionIds()2956 public int[] getChildSessionIds() { 2957 final int[] childSessionIds = mChildSessionIds.copyKeys(); 2958 if (childSessionIds != null) { 2959 return childSessionIds; 2960 } 2961 return EMPTY_CHILD_SESSION_ARRAY; 2962 } 2963 2964 @Override addChildSessionId(int childSessionId)2965 public void addChildSessionId(int childSessionId) { 2966 final PackageInstallerSession childSession = mSessionProvider.getSession(childSessionId); 2967 if (childSession == null 2968 || (childSession.hasParentSessionId() && childSession.mParentSessionId != sessionId) 2969 || childSession.mCommitted 2970 || childSession.mDestroyed) { 2971 throw new IllegalStateException("Unable to add child session " + childSessionId 2972 + " as it does not exist or is in an invalid state."); 2973 } 2974 synchronized (mLock) { 2975 assertCallerIsOwnerOrRootLocked(); 2976 assertPreparedAndNotSealedLocked("addChildSessionId"); 2977 2978 final int indexOfSession = mChildSessionIds.indexOfKey(childSessionId); 2979 if (indexOfSession >= 0) { 2980 return; 2981 } 2982 childSession.setParentSessionId(this.sessionId); 2983 addChildSessionIdInternal(childSessionId); 2984 } 2985 } 2986 2987 @Override removeChildSessionId(int sessionId)2988 public void removeChildSessionId(int sessionId) { 2989 final PackageInstallerSession session = mSessionProvider.getSession(sessionId); 2990 synchronized (mLock) { 2991 final int indexOfSession = mChildSessionIds.indexOfKey(sessionId); 2992 if (session != null) { 2993 session.setParentSessionId(SessionInfo.INVALID_ID); 2994 } 2995 if (indexOfSession < 0) { 2996 // not added in the first place; no-op 2997 return; 2998 } 2999 mChildSessionIds.removeAt(indexOfSession); 3000 } 3001 } 3002 3003 /** 3004 * Sets the parent session ID if not already set. 3005 * If {@link SessionInfo#INVALID_ID} is passed, it will be unset. 3006 */ setParentSessionId(int parentSessionId)3007 void setParentSessionId(int parentSessionId) { 3008 synchronized (mLock) { 3009 if (parentSessionId != SessionInfo.INVALID_ID 3010 && mParentSessionId != SessionInfo.INVALID_ID) { 3011 throw new IllegalStateException("The parent of " + sessionId + " is" + " already" 3012 + "set to " + mParentSessionId); 3013 } 3014 this.mParentSessionId = parentSessionId; 3015 } 3016 } 3017 hasParentSessionId()3018 boolean hasParentSessionId() { 3019 return mParentSessionId != SessionInfo.INVALID_ID; 3020 } 3021 3022 @Override getParentSessionId()3023 public int getParentSessionId() { 3024 return mParentSessionId; 3025 } 3026 dispatchSessionFinished(int returnCode, String msg, Bundle extras)3027 private void dispatchSessionFinished(int returnCode, String msg, Bundle extras) { 3028 final IntentSender statusReceiver; 3029 final String packageName; 3030 synchronized (mLock) { 3031 mFinalStatus = returnCode; 3032 mFinalMessage = msg; 3033 3034 statusReceiver = mRemoteStatusReceiver; 3035 packageName = mPackageName; 3036 } 3037 3038 if (statusReceiver != null) { 3039 // Execute observer.onPackageInstalled on different thread as we don't want callers 3040 // inside the system server have to worry about catching the callbacks while they are 3041 // calling into the session 3042 final SomeArgs args = SomeArgs.obtain(); 3043 args.arg1 = packageName; 3044 args.arg2 = msg; 3045 args.arg3 = extras; 3046 args.arg4 = statusReceiver; 3047 args.argi1 = returnCode; 3048 3049 mHandler.obtainMessage(MSG_ON_PACKAGE_INSTALLED, args).sendToTarget(); 3050 } 3051 3052 final boolean success = (returnCode == PackageManager.INSTALL_SUCCEEDED); 3053 3054 // Send broadcast to default launcher only if it's a new install 3055 // TODO(b/144270665): Secure the usage of this broadcast. 3056 final boolean isNewInstall = extras == null || !extras.getBoolean(Intent.EXTRA_REPLACING); 3057 if (success && isNewInstall && mPm.mInstallerService.okToSendBroadcasts() 3058 && (params.installFlags & PackageManager.INSTALL_DRY_RUN) == 0) { 3059 mPm.sendSessionCommitBroadcast(generateInfoScrubbed(true /*icon*/), userId); 3060 } 3061 3062 mCallback.onSessionFinished(this, success); 3063 if (isDataLoaderInstallation()) { 3064 logDataLoaderInstallationSession(returnCode); 3065 } 3066 } 3067 3068 /** {@hide} */ setStagedSessionReady()3069 void setStagedSessionReady() { 3070 synchronized (mLock) { 3071 if (mDestroyed) return; // Do not allow destroyed staged session to change state 3072 mStagedSessionReady = true; 3073 mStagedSessionApplied = false; 3074 mStagedSessionFailed = false; 3075 mStagedSessionErrorCode = SessionInfo.STAGED_SESSION_NO_ERROR; 3076 mStagedSessionErrorMessage = ""; 3077 } 3078 mCallback.onStagedSessionChanged(this); 3079 } 3080 3081 /** {@hide} */ setStagedSessionFailed(@tagedSessionErrorCode int errorCode, String errorMessage)3082 void setStagedSessionFailed(@StagedSessionErrorCode int errorCode, 3083 String errorMessage) { 3084 synchronized (mLock) { 3085 if (mDestroyed) return; // Do not allow destroyed staged session to change state 3086 mStagedSessionReady = false; 3087 mStagedSessionApplied = false; 3088 mStagedSessionFailed = true; 3089 mStagedSessionErrorCode = errorCode; 3090 mStagedSessionErrorMessage = errorMessage; 3091 Slog.d(TAG, "Marking session " + sessionId + " as failed: " + errorMessage); 3092 } 3093 cleanStageDirNotLocked(); 3094 mCallback.onStagedSessionChanged(this); 3095 } 3096 3097 /** {@hide} */ setStagedSessionApplied()3098 void setStagedSessionApplied() { 3099 synchronized (mLock) { 3100 if (mDestroyed) return; // Do not allow destroyed staged session to change state 3101 mStagedSessionReady = false; 3102 mStagedSessionApplied = true; 3103 mStagedSessionFailed = false; 3104 mStagedSessionErrorCode = SessionInfo.STAGED_SESSION_NO_ERROR; 3105 mStagedSessionErrorMessage = ""; 3106 Slog.d(TAG, "Marking session " + sessionId + " as applied"); 3107 } 3108 cleanStageDirNotLocked(); 3109 mCallback.onStagedSessionChanged(this); 3110 } 3111 3112 /** {@hide} */ isStagedSessionReady()3113 boolean isStagedSessionReady() { 3114 return mStagedSessionReady; 3115 } 3116 3117 /** {@hide} */ isStagedSessionApplied()3118 boolean isStagedSessionApplied() { 3119 return mStagedSessionApplied; 3120 } 3121 3122 /** {@hide} */ isStagedSessionFailed()3123 boolean isStagedSessionFailed() { 3124 return mStagedSessionFailed; 3125 } 3126 3127 /** {@hide} */ getStagedSessionErrorCode()3128 @StagedSessionErrorCode int getStagedSessionErrorCode() { 3129 return mStagedSessionErrorCode; 3130 } 3131 3132 /** {@hide} */ getStagedSessionErrorMessage()3133 String getStagedSessionErrorMessage() { 3134 return mStagedSessionErrorMessage; 3135 } 3136 destroyInternal()3137 private void destroyInternal() { 3138 synchronized (mLock) { 3139 mSealed = true; 3140 if (!params.isStaged || isStagedAndInTerminalState()) { 3141 mDestroyed = true; 3142 } 3143 // Force shut down all bridges 3144 for (RevocableFileDescriptor fd : mFds) { 3145 fd.revoke(); 3146 } 3147 for (FileBridge bridge : mBridges) { 3148 bridge.forceClose(); 3149 } 3150 } 3151 if (mIncrementalFileStorages != null) { 3152 mIncrementalFileStorages.cleanUp(); 3153 mIncrementalFileStorages = null; 3154 } 3155 // For staged sessions, we don't delete the directory where the packages have been copied, 3156 // since these packages are supposed to be read on reboot. 3157 // Those dirs are deleted when the staged session has reached a final state. 3158 if (stageDir != null && !params.isStaged) { 3159 try { 3160 mPm.mInstaller.rmPackageDir(stageDir.getAbsolutePath()); 3161 } catch (InstallerException ignored) { 3162 } 3163 } 3164 } 3165 3166 /** 3167 * <b>must not hold {@link #mLock}</b> 3168 */ cleanStageDirNotLocked()3169 private void cleanStageDirNotLocked() { 3170 if (Thread.holdsLock(mLock)) { 3171 Slog.wtf(TAG, "Calling thread " + Thread.currentThread().getName() 3172 + " is holding mLock", new Throwable()); 3173 } 3174 cleanStageDir(getChildSessionsNotLocked()); 3175 } 3176 cleanStageDir(List<PackageInstallerSession> childSessions)3177 private void cleanStageDir(List<PackageInstallerSession> childSessions) { 3178 if (childSessions != null) { 3179 for (PackageInstallerSession childSession : childSessions) { 3180 if (childSession != null) { 3181 childSession.cleanStageDir(); 3182 } 3183 } 3184 } else { 3185 cleanStageDir(); 3186 } 3187 } 3188 cleanStageDir()3189 private void cleanStageDir() { 3190 if (mIncrementalFileStorages != null) { 3191 mIncrementalFileStorages.cleanUp(); 3192 mIncrementalFileStorages = null; 3193 } 3194 try { 3195 mPm.mInstaller.rmPackageDir(stageDir.getAbsolutePath()); 3196 } catch (InstallerException ignored) { 3197 } 3198 } 3199 dump(IndentingPrintWriter pw)3200 void dump(IndentingPrintWriter pw) { 3201 synchronized (mLock) { 3202 dumpLocked(pw); 3203 } 3204 } 3205 3206 @GuardedBy("mLock") dumpLocked(IndentingPrintWriter pw)3207 private void dumpLocked(IndentingPrintWriter pw) { 3208 pw.println("Session " + sessionId + ":"); 3209 pw.increaseIndent(); 3210 3211 pw.printPair("userId", userId); 3212 pw.printPair("mOriginalInstallerUid", mOriginalInstallerUid); 3213 pw.printPair("mOriginalInstallerPackageName", mOriginalInstallerPackageName); 3214 pw.printPair("installerPackageName", mInstallSource.installerPackageName); 3215 pw.printPair("installInitiatingPackageName", mInstallSource.initiatingPackageName); 3216 pw.printPair("installOriginatingPackageName", mInstallSource.originatingPackageName); 3217 pw.printPair("mInstallerUid", mInstallerUid); 3218 pw.printPair("createdMillis", createdMillis); 3219 pw.printPair("updatedMillis", updatedMillis); 3220 pw.printPair("stageDir", stageDir); 3221 pw.printPair("stageCid", stageCid); 3222 pw.println(); 3223 3224 params.dump(pw); 3225 3226 pw.printPair("mClientProgress", mClientProgress); 3227 pw.printPair("mProgress", mProgress); 3228 pw.printPair("mCommitted", mCommitted); 3229 pw.printPair("mSealed", mSealed); 3230 pw.printPair("mPermissionsManuallyAccepted", mPermissionsManuallyAccepted); 3231 pw.printPair("mRelinquished", mRelinquished); 3232 pw.printPair("mDestroyed", mDestroyed); 3233 pw.printPair("mFds", mFds.size()); 3234 pw.printPair("mBridges", mBridges.size()); 3235 pw.printPair("mFinalStatus", mFinalStatus); 3236 pw.printPair("mFinalMessage", mFinalMessage); 3237 pw.printPair("params.isMultiPackage", params.isMultiPackage); 3238 pw.printPair("params.isStaged", params.isStaged); 3239 pw.printPair("mParentSessionId", mParentSessionId); 3240 pw.printPair("mChildSessionIds", mChildSessionIds); 3241 pw.printPair("mStagedSessionApplied", mStagedSessionApplied); 3242 pw.printPair("mStagedSessionFailed", mStagedSessionFailed); 3243 pw.printPair("mStagedSessionReady", mStagedSessionReady); 3244 pw.printPair("mStagedSessionErrorCode", mStagedSessionErrorCode); 3245 pw.printPair("mStagedSessionErrorMessage", mStagedSessionErrorMessage); 3246 pw.println(); 3247 3248 pw.decreaseIndent(); 3249 } 3250 sendOnUserActionRequired(Context context, IntentSender target, int sessionId, Intent intent)3251 private static void sendOnUserActionRequired(Context context, IntentSender target, 3252 int sessionId, Intent intent) { 3253 final Intent fillIn = new Intent(); 3254 fillIn.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId); 3255 fillIn.putExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_PENDING_USER_ACTION); 3256 fillIn.putExtra(Intent.EXTRA_INTENT, intent); 3257 try { 3258 target.sendIntent(context, 0, fillIn, null, null); 3259 } catch (IntentSender.SendIntentException ignored) { 3260 } 3261 } 3262 sendOnPackageInstalled(Context context, IntentSender target, int sessionId, boolean showNotification, int userId, String basePackageName, int returnCode, String msg, Bundle extras)3263 private static void sendOnPackageInstalled(Context context, IntentSender target, int sessionId, 3264 boolean showNotification, int userId, String basePackageName, int returnCode, 3265 String msg, Bundle extras) { 3266 if (PackageManager.INSTALL_SUCCEEDED == returnCode && showNotification) { 3267 boolean update = (extras != null) && extras.getBoolean(Intent.EXTRA_REPLACING); 3268 Notification notification = PackageInstallerService.buildSuccessNotification(context, 3269 context.getResources() 3270 .getString(update ? R.string.package_updated_device_owner : 3271 R.string.package_installed_device_owner), 3272 basePackageName, 3273 userId); 3274 if (notification != null) { 3275 NotificationManager notificationManager = (NotificationManager) 3276 context.getSystemService(Context.NOTIFICATION_SERVICE); 3277 notificationManager.notify(basePackageName, 3278 SystemMessageProto.SystemMessage.NOTE_PACKAGE_STATE, 3279 notification); 3280 } 3281 } 3282 final Intent fillIn = new Intent(); 3283 fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, basePackageName); 3284 fillIn.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId); 3285 fillIn.putExtra(PackageInstaller.EXTRA_STATUS, 3286 PackageManager.installStatusToPublicStatus(returnCode)); 3287 fillIn.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE, 3288 PackageManager.installStatusToString(returnCode, msg)); 3289 fillIn.putExtra(PackageInstaller.EXTRA_LEGACY_STATUS, returnCode); 3290 if (extras != null) { 3291 final String existing = extras.getString( 3292 PackageManager.EXTRA_FAILURE_EXISTING_PACKAGE); 3293 if (!TextUtils.isEmpty(existing)) { 3294 fillIn.putExtra(PackageInstaller.EXTRA_OTHER_PACKAGE_NAME, existing); 3295 } 3296 } 3297 try { 3298 target.sendIntent(context, 0, fillIn, null, null); 3299 } catch (IntentSender.SendIntentException ignored) { 3300 } 3301 } 3302 sendPendingStreaming(@ullable String cause)3303 private void sendPendingStreaming(@Nullable String cause) { 3304 final IntentSender statusReceiver; 3305 synchronized (mLock) { 3306 statusReceiver = mRemoteStatusReceiver; 3307 } 3308 3309 if (statusReceiver == null) { 3310 Slog.e(TAG, "Missing receiver for pending streaming status."); 3311 return; 3312 } 3313 3314 final Intent intent = new Intent(); 3315 intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId); 3316 intent.putExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_PENDING_STREAMING); 3317 if (!TextUtils.isEmpty(cause)) { 3318 intent.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE, 3319 "Staging Image Not Ready [" + cause + "]"); 3320 } else { 3321 intent.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE, "Staging Image Not Ready"); 3322 } 3323 try { 3324 statusReceiver.sendIntent(mContext, 0, intent, null, null); 3325 } catch (IntentSender.SendIntentException ignored) { 3326 } 3327 } 3328 writeGrantedRuntimePermissionsLocked(XmlSerializer out, String[] grantedRuntimePermissions)3329 private static void writeGrantedRuntimePermissionsLocked(XmlSerializer out, 3330 String[] grantedRuntimePermissions) throws IOException { 3331 if (grantedRuntimePermissions != null) { 3332 for (String permission : grantedRuntimePermissions) { 3333 out.startTag(null, TAG_GRANTED_RUNTIME_PERMISSION); 3334 writeStringAttribute(out, ATTR_NAME, permission); 3335 out.endTag(null, TAG_GRANTED_RUNTIME_PERMISSION); 3336 } 3337 } 3338 } 3339 writeWhitelistedRestrictedPermissionsLocked(@onNull XmlSerializer out, @Nullable List<String> whitelistedRestrictedPermissions)3340 private static void writeWhitelistedRestrictedPermissionsLocked(@NonNull XmlSerializer out, 3341 @Nullable List<String> whitelistedRestrictedPermissions) throws IOException { 3342 if (whitelistedRestrictedPermissions != null) { 3343 final int permissionCount = whitelistedRestrictedPermissions.size(); 3344 for (int i = 0; i < permissionCount; i++) { 3345 out.startTag(null, TAG_WHITELISTED_RESTRICTED_PERMISSION); 3346 writeStringAttribute(out, ATTR_NAME, whitelistedRestrictedPermissions.get(i)); 3347 out.endTag(null, TAG_WHITELISTED_RESTRICTED_PERMISSION); 3348 } 3349 } 3350 } 3351 writeAutoRevokePermissionsMode(@onNull XmlSerializer out, int mode)3352 private static void writeAutoRevokePermissionsMode(@NonNull XmlSerializer out, int mode) 3353 throws IOException { 3354 out.startTag(null, TAG_AUTO_REVOKE_PERMISSIONS_MODE); 3355 writeIntAttribute(out, ATTR_MODE, mode); 3356 out.endTag(null, TAG_AUTO_REVOKE_PERMISSIONS_MODE); 3357 } 3358 3359 buildAppIconFile(int sessionId, @NonNull File sessionsDir)3360 private static File buildAppIconFile(int sessionId, @NonNull File sessionsDir) { 3361 return new File(sessionsDir, "app_icon." + sessionId + ".png"); 3362 } 3363 3364 /** 3365 * Write this session to a {@link XmlSerializer}. 3366 * 3367 * @param out Where to write the session to 3368 * @param sessionsDir The directory containing the sessions 3369 */ write(@onNull XmlSerializer out, @NonNull File sessionsDir)3370 void write(@NonNull XmlSerializer out, @NonNull File sessionsDir) throws IOException { 3371 synchronized (mLock) { 3372 if (mDestroyed && !params.isStaged) { 3373 return; 3374 } 3375 3376 out.startTag(null, TAG_SESSION); 3377 3378 writeIntAttribute(out, ATTR_SESSION_ID, sessionId); 3379 writeIntAttribute(out, ATTR_USER_ID, userId); 3380 writeStringAttribute(out, ATTR_INSTALLER_PACKAGE_NAME, 3381 mInstallSource.installerPackageName); 3382 writeIntAttribute(out, ATTR_INSTALLER_UID, mInstallerUid); 3383 writeStringAttribute(out, ATTR_INITIATING_PACKAGE_NAME, 3384 mInstallSource.initiatingPackageName); 3385 writeStringAttribute(out, ATTR_ORIGINATING_PACKAGE_NAME, 3386 mInstallSource.originatingPackageName); 3387 writeLongAttribute(out, ATTR_CREATED_MILLIS, createdMillis); 3388 writeLongAttribute(out, ATTR_UPDATED_MILLIS, updatedMillis); 3389 if (stageDir != null) { 3390 writeStringAttribute(out, ATTR_SESSION_STAGE_DIR, 3391 stageDir.getAbsolutePath()); 3392 } 3393 if (stageCid != null) { 3394 writeStringAttribute(out, ATTR_SESSION_STAGE_CID, stageCid); 3395 } 3396 writeBooleanAttribute(out, ATTR_PREPARED, isPrepared()); 3397 writeBooleanAttribute(out, ATTR_COMMITTED, isCommitted()); 3398 writeBooleanAttribute(out, ATTR_DESTROYED, isDestroyed()); 3399 writeBooleanAttribute(out, ATTR_SEALED, isSealed()); 3400 3401 writeBooleanAttribute(out, ATTR_MULTI_PACKAGE, params.isMultiPackage); 3402 writeBooleanAttribute(out, ATTR_STAGED_SESSION, params.isStaged); 3403 writeBooleanAttribute(out, ATTR_IS_READY, mStagedSessionReady); 3404 writeBooleanAttribute(out, ATTR_IS_FAILED, mStagedSessionFailed); 3405 writeBooleanAttribute(out, ATTR_IS_APPLIED, mStagedSessionApplied); 3406 writeIntAttribute(out, ATTR_STAGED_SESSION_ERROR_CODE, mStagedSessionErrorCode); 3407 writeStringAttribute(out, ATTR_STAGED_SESSION_ERROR_MESSAGE, 3408 mStagedSessionErrorMessage); 3409 // TODO(patb,109941548): avoid writing to xml and instead infer / validate this after 3410 // we've read all sessions. 3411 writeIntAttribute(out, ATTR_PARENT_SESSION_ID, mParentSessionId); 3412 writeIntAttribute(out, ATTR_MODE, params.mode); 3413 writeIntAttribute(out, ATTR_INSTALL_FLAGS, params.installFlags); 3414 writeIntAttribute(out, ATTR_INSTALL_LOCATION, params.installLocation); 3415 writeLongAttribute(out, ATTR_SIZE_BYTES, params.sizeBytes); 3416 writeStringAttribute(out, ATTR_APP_PACKAGE_NAME, params.appPackageName); 3417 writeStringAttribute(out, ATTR_APP_LABEL, params.appLabel); 3418 writeUriAttribute(out, ATTR_ORIGINATING_URI, params.originatingUri); 3419 writeIntAttribute(out, ATTR_ORIGINATING_UID, params.originatingUid); 3420 writeUriAttribute(out, ATTR_REFERRER_URI, params.referrerUri); 3421 writeStringAttribute(out, ATTR_ABI_OVERRIDE, params.abiOverride); 3422 writeStringAttribute(out, ATTR_VOLUME_UUID, params.volumeUuid); 3423 writeIntAttribute(out, ATTR_INSTALL_REASON, params.installReason); 3424 3425 final boolean isDataLoader = params.dataLoaderParams != null; 3426 writeBooleanAttribute(out, ATTR_IS_DATALOADER, isDataLoader); 3427 if (isDataLoader) { 3428 writeIntAttribute(out, ATTR_DATALOADER_TYPE, params.dataLoaderParams.getType()); 3429 writeStringAttribute(out, ATTR_DATALOADER_PACKAGE_NAME, 3430 params.dataLoaderParams.getComponentName().getPackageName()); 3431 writeStringAttribute(out, ATTR_DATALOADER_CLASS_NAME, 3432 params.dataLoaderParams.getComponentName().getClassName()); 3433 writeStringAttribute(out, ATTR_DATALOADER_ARGUMENTS, 3434 params.dataLoaderParams.getArguments()); 3435 } 3436 3437 writeGrantedRuntimePermissionsLocked(out, params.grantedRuntimePermissions); 3438 writeWhitelistedRestrictedPermissionsLocked(out, 3439 params.whitelistedRestrictedPermissions); 3440 writeAutoRevokePermissionsMode(out, params.autoRevokePermissionsMode); 3441 3442 // Persist app icon if changed since last written 3443 File appIconFile = buildAppIconFile(sessionId, sessionsDir); 3444 if (params.appIcon == null && appIconFile.exists()) { 3445 appIconFile.delete(); 3446 } else if (params.appIcon != null 3447 && appIconFile.lastModified() != params.appIconLastModified) { 3448 if (LOGD) Slog.w(TAG, "Writing changed icon " + appIconFile); 3449 FileOutputStream os = null; 3450 try { 3451 os = new FileOutputStream(appIconFile); 3452 params.appIcon.compress(Bitmap.CompressFormat.PNG, 90, os); 3453 } catch (IOException e) { 3454 Slog.w(TAG, "Failed to write icon " + appIconFile + ": " + e.getMessage()); 3455 } finally { 3456 IoUtils.closeQuietly(os); 3457 } 3458 3459 params.appIconLastModified = appIconFile.lastModified(); 3460 } 3461 final int[] childSessionIds = getChildSessionIds(); 3462 for (int childSessionId : childSessionIds) { 3463 out.startTag(null, TAG_CHILD_SESSION); 3464 writeIntAttribute(out, ATTR_SESSION_ID, childSessionId); 3465 out.endTag(null, TAG_CHILD_SESSION); 3466 } 3467 3468 final InstallationFile[] files = getInstallationFilesLocked(); 3469 for (InstallationFile file : getInstallationFilesLocked()) { 3470 out.startTag(null, TAG_SESSION_FILE); 3471 writeIntAttribute(out, ATTR_LOCATION, file.getLocation()); 3472 writeStringAttribute(out, ATTR_NAME, file.getName()); 3473 writeLongAttribute(out, ATTR_LENGTH_BYTES, file.getLengthBytes()); 3474 writeByteArrayAttribute(out, ATTR_METADATA, file.getMetadata()); 3475 writeByteArrayAttribute(out, ATTR_SIGNATURE, file.getSignature()); 3476 out.endTag(null, TAG_SESSION_FILE); 3477 } 3478 } 3479 3480 out.endTag(null, TAG_SESSION); 3481 } 3482 3483 // Sanity check to be performed when the session is restored from an external file. Only one 3484 // of the session states should be true, or none of them. isStagedSessionStateValid(boolean isReady, boolean isApplied, boolean isFailed)3485 private static boolean isStagedSessionStateValid(boolean isReady, boolean isApplied, 3486 boolean isFailed) { 3487 return (!isReady && !isApplied && !isFailed) 3488 || (isReady && !isApplied && !isFailed) 3489 || (!isReady && isApplied && !isFailed) 3490 || (!isReady && !isApplied && isFailed); 3491 } 3492 3493 /** 3494 * Read new session from a {@link XmlPullParser xml description} and create it. 3495 * 3496 * @param in The source of the description 3497 * @param callback Callback the session uses to notify about changes of it's state 3498 * @param context Context to be used by the session 3499 * @param pm PackageManager to use by the session 3500 * @param installerThread Thread to be used for callbacks of this session 3501 * @param sessionsDir The directory the sessions are stored in 3502 * 3503 * @param sessionProvider 3504 * @return The newly created session 3505 */ 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)3506 public static PackageInstallerSession readFromXml(@NonNull XmlPullParser in, 3507 @NonNull PackageInstallerService.InternalCallback callback, @NonNull Context context, 3508 @NonNull PackageManagerService pm, Looper installerThread, 3509 @NonNull StagingManager stagingManager, @NonNull File sessionsDir, 3510 @NonNull PackageSessionProvider sessionProvider) 3511 throws IOException, XmlPullParserException { 3512 final int sessionId = readIntAttribute(in, ATTR_SESSION_ID); 3513 final int userId = readIntAttribute(in, ATTR_USER_ID); 3514 final String installerPackageName = readStringAttribute(in, ATTR_INSTALLER_PACKAGE_NAME); 3515 final int installerUid = readIntAttribute(in, ATTR_INSTALLER_UID, pm.getPackageUid( 3516 installerPackageName, PackageManager.MATCH_UNINSTALLED_PACKAGES, userId)); 3517 final String installInitiatingPackageName = 3518 readStringAttribute(in, ATTR_INITIATING_PACKAGE_NAME); 3519 final String installOriginatingPackageName = 3520 readStringAttribute(in, ATTR_ORIGINATING_PACKAGE_NAME); 3521 final long createdMillis = readLongAttribute(in, ATTR_CREATED_MILLIS); 3522 long updatedMillis = readLongAttribute(in, ATTR_UPDATED_MILLIS); 3523 final String stageDirRaw = readStringAttribute(in, ATTR_SESSION_STAGE_DIR); 3524 final File stageDir = (stageDirRaw != null) ? new File(stageDirRaw) : null; 3525 final String stageCid = readStringAttribute(in, ATTR_SESSION_STAGE_CID); 3526 final boolean prepared = readBooleanAttribute(in, ATTR_PREPARED, true); 3527 final boolean committed = readBooleanAttribute(in, ATTR_COMMITTED); 3528 final boolean destroyed = readBooleanAttribute(in, ATTR_DESTROYED); 3529 final boolean sealed = readBooleanAttribute(in, ATTR_SEALED); 3530 final int parentSessionId = readIntAttribute(in, ATTR_PARENT_SESSION_ID, 3531 SessionInfo.INVALID_ID); 3532 3533 final SessionParams params = new SessionParams( 3534 SessionParams.MODE_INVALID); 3535 params.isMultiPackage = readBooleanAttribute(in, ATTR_MULTI_PACKAGE, false); 3536 params.isStaged = readBooleanAttribute(in, ATTR_STAGED_SESSION, false); 3537 params.mode = readIntAttribute(in, ATTR_MODE); 3538 params.installFlags = readIntAttribute(in, ATTR_INSTALL_FLAGS); 3539 params.installLocation = readIntAttribute(in, ATTR_INSTALL_LOCATION); 3540 params.sizeBytes = readLongAttribute(in, ATTR_SIZE_BYTES); 3541 params.appPackageName = readStringAttribute(in, ATTR_APP_PACKAGE_NAME); 3542 params.appIcon = readBitmapAttribute(in, ATTR_APP_ICON); 3543 params.appLabel = readStringAttribute(in, ATTR_APP_LABEL); 3544 params.originatingUri = readUriAttribute(in, ATTR_ORIGINATING_URI); 3545 params.originatingUid = 3546 readIntAttribute(in, ATTR_ORIGINATING_UID, SessionParams.UID_UNKNOWN); 3547 params.referrerUri = readUriAttribute(in, ATTR_REFERRER_URI); 3548 params.abiOverride = readStringAttribute(in, ATTR_ABI_OVERRIDE); 3549 params.volumeUuid = readStringAttribute(in, ATTR_VOLUME_UUID); 3550 params.installReason = readIntAttribute(in, ATTR_INSTALL_REASON); 3551 3552 if (readBooleanAttribute(in, ATTR_IS_DATALOADER)) { 3553 params.dataLoaderParams = new DataLoaderParams( 3554 readIntAttribute(in, ATTR_DATALOADER_TYPE), 3555 new ComponentName( 3556 readStringAttribute(in, ATTR_DATALOADER_PACKAGE_NAME), 3557 readStringAttribute(in, ATTR_DATALOADER_CLASS_NAME)), 3558 readStringAttribute(in, ATTR_DATALOADER_ARGUMENTS)); 3559 } 3560 3561 final File appIconFile = buildAppIconFile(sessionId, sessionsDir); 3562 if (appIconFile.exists()) { 3563 params.appIcon = BitmapFactory.decodeFile(appIconFile.getAbsolutePath()); 3564 params.appIconLastModified = appIconFile.lastModified(); 3565 } 3566 final boolean isReady = readBooleanAttribute(in, ATTR_IS_READY); 3567 final boolean isFailed = readBooleanAttribute(in, ATTR_IS_FAILED); 3568 final boolean isApplied = readBooleanAttribute(in, ATTR_IS_APPLIED); 3569 final int stagedSessionErrorCode = readIntAttribute(in, ATTR_STAGED_SESSION_ERROR_CODE, 3570 SessionInfo.STAGED_SESSION_NO_ERROR); 3571 final String stagedSessionErrorMessage = readStringAttribute(in, 3572 ATTR_STAGED_SESSION_ERROR_MESSAGE); 3573 3574 if (!isStagedSessionStateValid(isReady, isApplied, isFailed)) { 3575 throw new IllegalArgumentException("Can't restore staged session with invalid state."); 3576 } 3577 3578 // Parse sub tags of this session, typically used for repeated values / arrays. 3579 // Sub tags can come in any order, therefore we need to keep track of what we find while 3580 // parsing and only set the right values at the end. 3581 3582 // Store the current depth. We should stop parsing when we reach an end tag at the same 3583 // depth. 3584 List<String> grantedRuntimePermissions = new ArrayList<>(); 3585 List<String> whitelistedRestrictedPermissions = new ArrayList<>(); 3586 int autoRevokePermissionsMode = MODE_DEFAULT; 3587 List<Integer> childSessionIds = new ArrayList<>(); 3588 List<InstallationFile> files = new ArrayList<>(); 3589 int outerDepth = in.getDepth(); 3590 int type; 3591 while ((type = in.next()) != XmlPullParser.END_DOCUMENT 3592 && (type != XmlPullParser.END_TAG || in.getDepth() > outerDepth)) { 3593 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { 3594 continue; 3595 } 3596 if (TAG_GRANTED_RUNTIME_PERMISSION.equals(in.getName())) { 3597 grantedRuntimePermissions.add(readStringAttribute(in, ATTR_NAME)); 3598 } 3599 if (TAG_WHITELISTED_RESTRICTED_PERMISSION.equals(in.getName())) { 3600 whitelistedRestrictedPermissions.add(readStringAttribute(in, ATTR_NAME)); 3601 3602 } 3603 if (TAG_AUTO_REVOKE_PERMISSIONS_MODE.equals(in.getName())) { 3604 autoRevokePermissionsMode = readIntAttribute(in, ATTR_MODE); 3605 } 3606 if (TAG_CHILD_SESSION.equals(in.getName())) { 3607 childSessionIds.add(readIntAttribute(in, ATTR_SESSION_ID, SessionInfo.INVALID_ID)); 3608 } 3609 if (TAG_SESSION_FILE.equals(in.getName())) { 3610 files.add(new InstallationFile( 3611 readIntAttribute(in, ATTR_LOCATION, 0), 3612 readStringAttribute(in, ATTR_NAME), 3613 readLongAttribute(in, ATTR_LENGTH_BYTES, -1), 3614 readByteArrayAttribute(in, ATTR_METADATA), 3615 readByteArrayAttribute(in, ATTR_SIGNATURE))); 3616 } 3617 } 3618 3619 if (grantedRuntimePermissions.size() > 0) { 3620 params.grantedRuntimePermissions = 3621 grantedRuntimePermissions.toArray(EmptyArray.STRING); 3622 } 3623 3624 if (whitelistedRestrictedPermissions.size() > 0) { 3625 params.whitelistedRestrictedPermissions = whitelistedRestrictedPermissions; 3626 } 3627 3628 params.autoRevokePermissionsMode = autoRevokePermissionsMode; 3629 3630 int[] childSessionIdsArray; 3631 if (childSessionIds.size() > 0) { 3632 childSessionIdsArray = new int[childSessionIds.size()]; 3633 for (int i = 0, size = childSessionIds.size(); i < size; ++i) { 3634 childSessionIdsArray[i] = childSessionIds.get(i); 3635 } 3636 } else { 3637 childSessionIdsArray = EMPTY_CHILD_SESSION_ARRAY; 3638 } 3639 3640 InstallationFile[] fileArray = null; 3641 if (!files.isEmpty()) { 3642 fileArray = files.toArray(EMPTY_INSTALLATION_FILE_ARRAY); 3643 } 3644 3645 InstallSource installSource = InstallSource.create(installInitiatingPackageName, 3646 installOriginatingPackageName, installerPackageName); 3647 return new PackageInstallerSession(callback, context, pm, sessionProvider, 3648 installerThread, stagingManager, sessionId, userId, installerUid, 3649 installSource, params, createdMillis, stageDir, stageCid, fileArray, 3650 prepared, committed, destroyed, sealed, childSessionIdsArray, parentSessionId, 3651 isReady, isFailed, isApplied, stagedSessionErrorCode, stagedSessionErrorMessage); 3652 } 3653 } 3654