1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.pm; 18 19 import static android.content.pm.PackageManager.INSTALL_FAILED_ABORTED; 20 import static android.content.pm.PackageManager.INSTALL_FAILED_CONTAINER_ERROR; 21 import static android.content.pm.PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; 22 import static android.content.pm.PackageManager.INSTALL_FAILED_INTERNAL_ERROR; 23 import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK; 24 import static android.content.pm.PackageParser.APK_FILE_EXTENSION; 25 import static android.system.OsConstants.O_CREAT; 26 import static android.system.OsConstants.O_RDONLY; 27 import static android.system.OsConstants.O_WRONLY; 28 29 import static com.android.internal.util.XmlUtils.readBitmapAttribute; 30 import static com.android.internal.util.XmlUtils.readBooleanAttribute; 31 import static com.android.internal.util.XmlUtils.readIntAttribute; 32 import static com.android.internal.util.XmlUtils.readLongAttribute; 33 import static com.android.internal.util.XmlUtils.readStringAttribute; 34 import static com.android.internal.util.XmlUtils.readUriAttribute; 35 import static com.android.internal.util.XmlUtils.writeBooleanAttribute; 36 import static com.android.internal.util.XmlUtils.writeIntAttribute; 37 import static com.android.internal.util.XmlUtils.writeLongAttribute; 38 import static com.android.internal.util.XmlUtils.writeStringAttribute; 39 import static com.android.internal.util.XmlUtils.writeUriAttribute; 40 import static com.android.server.pm.PackageInstallerService.prepareStageDir; 41 42 import android.Manifest; 43 import android.annotation.NonNull; 44 import android.annotation.Nullable; 45 import android.app.admin.DeviceAdminInfo; 46 import android.app.admin.DevicePolicyManagerInternal; 47 import android.content.Context; 48 import android.content.Intent; 49 import android.content.IntentSender; 50 import android.content.pm.ApplicationInfo; 51 import android.content.pm.IPackageInstallObserver2; 52 import android.content.pm.IPackageInstallerSession; 53 import android.content.pm.PackageInfo; 54 import android.content.pm.PackageInstaller; 55 import android.content.pm.PackageInstaller.SessionInfo; 56 import android.content.pm.PackageInstaller.SessionParams; 57 import android.content.pm.PackageManager; 58 import android.content.pm.PackageParser; 59 import android.content.pm.PackageParser.ApkLite; 60 import android.content.pm.PackageParser.PackageLite; 61 import android.content.pm.PackageParser.PackageParserException; 62 import android.graphics.Bitmap; 63 import android.graphics.BitmapFactory; 64 import android.os.Binder; 65 import android.os.Bundle; 66 import android.os.FileBridge; 67 import android.os.FileUtils; 68 import android.os.Handler; 69 import android.os.Looper; 70 import android.os.Message; 71 import android.os.ParcelFileDescriptor; 72 import android.os.ParcelableException; 73 import android.os.Process; 74 import android.os.RemoteException; 75 import android.os.RevocableFileDescriptor; 76 import android.os.SystemProperties; 77 import android.os.UserHandle; 78 import android.os.storage.StorageManager; 79 import android.system.ErrnoException; 80 import android.system.Int64Ref; 81 import android.system.Os; 82 import android.system.OsConstants; 83 import android.system.StructStat; 84 import android.text.TextUtils; 85 import android.util.ArraySet; 86 import android.util.ExceptionUtils; 87 import android.util.MathUtils; 88 import android.util.Slog; 89 import android.util.apk.ApkSignatureVerifier; 90 91 import com.android.internal.annotations.GuardedBy; 92 import com.android.internal.content.NativeLibraryHelper; 93 import com.android.internal.content.PackageHelper; 94 import com.android.internal.os.SomeArgs; 95 import com.android.internal.util.ArrayUtils; 96 import com.android.internal.util.IndentingPrintWriter; 97 import com.android.internal.util.Preconditions; 98 import com.android.server.LocalServices; 99 import com.android.server.pm.Installer.InstallerException; 100 import com.android.server.pm.PackageInstallerService.PackageInstallObserverAdapter; 101 102 import android.content.pm.dex.DexMetadataHelper; 103 import libcore.io.IoUtils; 104 105 import org.xmlpull.v1.XmlPullParser; 106 import org.xmlpull.v1.XmlPullParserException; 107 import org.xmlpull.v1.XmlSerializer; 108 109 import java.io.File; 110 import java.io.FileDescriptor; 111 import java.io.FileFilter; 112 import java.io.FileOutputStream; 113 import java.io.IOException; 114 import java.util.ArrayList; 115 import java.util.Arrays; 116 import java.util.LinkedList; 117 import java.util.List; 118 import java.util.concurrent.atomic.AtomicInteger; 119 120 public class PackageInstallerSession extends IPackageInstallerSession.Stub { 121 private static final String TAG = "PackageInstaller"; 122 private static final boolean LOGD = true; 123 private static final String REMOVE_SPLIT_MARKER_EXTENSION = ".removed"; 124 125 private static final int MSG_EARLY_BIND = 0; 126 private static final int MSG_COMMIT = 1; 127 private static final int MSG_ON_PACKAGE_INSTALLED = 2; 128 129 /** XML constants used for persisting a session */ 130 static final String TAG_SESSION = "session"; 131 private static final String TAG_GRANTED_RUNTIME_PERMISSION = "granted-runtime-permission"; 132 private static final String ATTR_SESSION_ID = "sessionId"; 133 private static final String ATTR_USER_ID = "userId"; 134 private static final String ATTR_INSTALLER_PACKAGE_NAME = "installerPackageName"; 135 private static final String ATTR_INSTALLER_UID = "installerUid"; 136 private static final String ATTR_CREATED_MILLIS = "createdMillis"; 137 private static final String ATTR_SESSION_STAGE_DIR = "sessionStageDir"; 138 private static final String ATTR_SESSION_STAGE_CID = "sessionStageCid"; 139 private static final String ATTR_PREPARED = "prepared"; 140 private static final String ATTR_SEALED = "sealed"; 141 private static final String ATTR_MODE = "mode"; 142 private static final String ATTR_INSTALL_FLAGS = "installFlags"; 143 private static final String ATTR_INSTALL_LOCATION = "installLocation"; 144 private static final String ATTR_SIZE_BYTES = "sizeBytes"; 145 private static final String ATTR_APP_PACKAGE_NAME = "appPackageName"; 146 @Deprecated 147 private static final String ATTR_APP_ICON = "appIcon"; 148 private static final String ATTR_APP_LABEL = "appLabel"; 149 private static final String ATTR_ORIGINATING_URI = "originatingUri"; 150 private static final String ATTR_ORIGINATING_UID = "originatingUid"; 151 private static final String ATTR_REFERRER_URI = "referrerUri"; 152 private static final String ATTR_ABI_OVERRIDE = "abiOverride"; 153 private static final String ATTR_VOLUME_UUID = "volumeUuid"; 154 private static final String ATTR_NAME = "name"; 155 private static final String ATTR_INSTALL_REASON = "installRason"; 156 157 private static final String PROPERTY_NAME_INHERIT_NATIVE = "pi.inherit_native_on_dont_kill"; 158 159 // TODO: enforce INSTALL_ALLOW_TEST 160 // TODO: enforce INSTALL_ALLOW_DOWNGRADE 161 162 private final PackageInstallerService.InternalCallback mCallback; 163 private final Context mContext; 164 private final PackageManagerService mPm; 165 private final Handler mHandler; 166 167 final int sessionId; 168 final int userId; 169 final SessionParams params; 170 final long createdMillis; 171 final int defaultContainerGid; 172 173 /** Staging location where client data is written. */ 174 final File stageDir; 175 final String stageCid; 176 177 private final AtomicInteger mActiveCount = new AtomicInteger(); 178 179 private final Object mLock = new Object(); 180 181 /** Uid of the creator of this session. */ 182 private final int mOriginalInstallerUid; 183 184 /** Package of the owner of the installer session */ 185 @GuardedBy("mLock") 186 private String mInstallerPackageName; 187 188 /** Uid of the owner of the installer session */ 189 @GuardedBy("mLock") 190 private int mInstallerUid; 191 192 @GuardedBy("mLock") 193 private float mClientProgress = 0; 194 @GuardedBy("mLock") 195 private float mInternalProgress = 0; 196 197 @GuardedBy("mLock") 198 private float mProgress = 0; 199 @GuardedBy("mLock") 200 private float mReportedProgress = -1; 201 202 /** State of the session. */ 203 @GuardedBy("mLock") 204 private boolean mPrepared = false; 205 @GuardedBy("mLock") 206 private boolean mSealed = false; 207 @GuardedBy("mLock") 208 private boolean mCommitted = false; 209 @GuardedBy("mLock") 210 private boolean mRelinquished = false; 211 @GuardedBy("mLock") 212 private boolean mDestroyed = false; 213 214 /** Permissions have been accepted by the user (see {@link #setPermissionsResult}) */ 215 @GuardedBy("mLock") 216 private boolean mPermissionsManuallyAccepted = false; 217 218 @GuardedBy("mLock") 219 private int mFinalStatus; 220 @GuardedBy("mLock") 221 private String mFinalMessage; 222 223 @GuardedBy("mLock") 224 private final ArrayList<RevocableFileDescriptor> mFds = new ArrayList<>(); 225 @GuardedBy("mLock") 226 private final ArrayList<FileBridge> mBridges = new ArrayList<>(); 227 228 @GuardedBy("mLock") 229 private IPackageInstallObserver2 mRemoteObserver; 230 231 /** Fields derived from commit parsing */ 232 @GuardedBy("mLock") 233 private String mPackageName; 234 @GuardedBy("mLock") 235 private long mVersionCode; 236 @GuardedBy("mLock") 237 private PackageParser.SigningDetails mSigningDetails; 238 239 /** 240 * Path to the validated base APK for this session, which may point at an 241 * APK inside the session (when the session defines the base), or it may 242 * point at the existing base APK (when adding splits to an existing app). 243 * <p> 244 * This is used when confirming permissions, since we can't fully stage the 245 * session inside an ASEC before confirming with user. 246 */ 247 @GuardedBy("mLock") 248 private File mResolvedBaseFile; 249 250 @GuardedBy("mLock") 251 private File mResolvedStageDir; 252 253 @GuardedBy("mLock") 254 private final List<File> mResolvedStagedFiles = new ArrayList<>(); 255 @GuardedBy("mLock") 256 private final List<File> mResolvedInheritedFiles = new ArrayList<>(); 257 @GuardedBy("mLock") 258 private final List<String> mResolvedInstructionSets = new ArrayList<>(); 259 @GuardedBy("mLock") 260 private final List<String> mResolvedNativeLibPaths = new ArrayList<>(); 261 @GuardedBy("mLock") 262 private File mInheritedFilesBase; 263 264 private static final FileFilter sAddedFilter = new FileFilter() { 265 @Override 266 public boolean accept(File file) { 267 // Installers can't stage directories, so it's fine to ignore 268 // entries like "lost+found". 269 if (file.isDirectory()) return false; 270 if (file.getName().endsWith(REMOVE_SPLIT_MARKER_EXTENSION)) return false; 271 if (DexMetadataHelper.isDexMetadataFile(file)) return false; 272 return true; 273 } 274 }; 275 private static final FileFilter sRemovedFilter = new FileFilter() { 276 @Override 277 public boolean accept(File file) { 278 if (file.isDirectory()) return false; 279 if (!file.getName().endsWith(REMOVE_SPLIT_MARKER_EXTENSION)) return false; 280 return true; 281 } 282 }; 283 284 private final Handler.Callback mHandlerCallback = new Handler.Callback() { 285 @Override 286 public boolean handleMessage(Message msg) { 287 switch (msg.what) { 288 case MSG_EARLY_BIND: 289 earlyBindToDefContainer(); 290 break; 291 case MSG_COMMIT: 292 synchronized (mLock) { 293 try { 294 commitLocked(); 295 } catch (PackageManagerException e) { 296 final String completeMsg = ExceptionUtils.getCompleteMessage(e); 297 Slog.e(TAG, 298 "Commit of session " + sessionId + " failed: " + completeMsg); 299 destroyInternal(); 300 dispatchSessionFinished(e.error, completeMsg, null); 301 } 302 } 303 304 break; 305 case MSG_ON_PACKAGE_INSTALLED: 306 final SomeArgs args = (SomeArgs) msg.obj; 307 final String packageName = (String) args.arg1; 308 final String message = (String) args.arg2; 309 final Bundle extras = (Bundle) args.arg3; 310 final IPackageInstallObserver2 observer = (IPackageInstallObserver2) args.arg4; 311 final int returnCode = args.argi1; 312 args.recycle(); 313 314 try { 315 observer.onPackageInstalled(packageName, returnCode, message, extras); 316 } catch (RemoteException ignored) { 317 } 318 319 break; 320 } 321 322 return true; 323 } 324 }; 325 earlyBindToDefContainer()326 private void earlyBindToDefContainer() { 327 mPm.earlyBindToDefContainer(); 328 } 329 330 /** 331 * @return {@code true} iff the installing is app an device owner or affiliated profile owner. 332 */ 333 @GuardedBy("mLock") isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked()334 private boolean isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked() { 335 DevicePolicyManagerInternal dpmi = 336 LocalServices.getService(DevicePolicyManagerInternal.class); 337 return dpmi != null && dpmi.isActiveAdminWithPolicy(mInstallerUid, 338 DeviceAdminInfo.USES_POLICY_PROFILE_OWNER) && dpmi.isUserAffiliatedWithDevice( 339 userId); 340 } 341 342 /** 343 * Checks if the permissions still need to be confirmed. 344 * 345 * <p>This is dependant on the identity of the installer, hence this cannot be cached if the 346 * installer might still {@link #transfer(String) change}. 347 * 348 * @return {@code true} iff we need to ask to confirm the permissions? 349 */ 350 @GuardedBy("mLock") needToAskForPermissionsLocked()351 private boolean needToAskForPermissionsLocked() { 352 if (mPermissionsManuallyAccepted) { 353 return false; 354 } 355 356 final boolean isInstallPermissionGranted = 357 (mPm.checkUidPermission(android.Manifest.permission.INSTALL_PACKAGES, 358 mInstallerUid) == PackageManager.PERMISSION_GRANTED); 359 final boolean isSelfUpdatePermissionGranted = 360 (mPm.checkUidPermission(android.Manifest.permission.INSTALL_SELF_UPDATES, 361 mInstallerUid) == PackageManager.PERMISSION_GRANTED); 362 final boolean isUpdatePermissionGranted = 363 (mPm.checkUidPermission(android.Manifest.permission.INSTALL_PACKAGE_UPDATES, 364 mInstallerUid) == PackageManager.PERMISSION_GRANTED); 365 final int targetPackageUid = mPm.getPackageUid(mPackageName, 0, userId); 366 final boolean isPermissionGranted = isInstallPermissionGranted 367 || (isUpdatePermissionGranted && targetPackageUid != -1) 368 || (isSelfUpdatePermissionGranted && targetPackageUid == mInstallerUid); 369 final boolean isInstallerRoot = (mInstallerUid == Process.ROOT_UID); 370 final boolean isInstallerSystem = (mInstallerUid == Process.SYSTEM_UID); 371 final boolean forcePermissionPrompt = 372 (params.installFlags & PackageManager.INSTALL_FORCE_PERMISSION_PROMPT) != 0; 373 374 // Device owners and affiliated profile owners are allowed to silently install packages, so 375 // the permission check is waived if the installer is the device owner. 376 return forcePermissionPrompt || !(isPermissionGranted || isInstallerRoot 377 || isInstallerSystem || isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked()); 378 } 379 PackageInstallerSession(PackageInstallerService.InternalCallback callback, Context context, PackageManagerService pm, Looper looper, int sessionId, int userId, String installerPackageName, int installerUid, SessionParams params, long createdMillis, File stageDir, String stageCid, boolean prepared, boolean sealed)380 public PackageInstallerSession(PackageInstallerService.InternalCallback callback, 381 Context context, PackageManagerService pm, Looper looper, int sessionId, int userId, 382 String installerPackageName, int installerUid, SessionParams params, long createdMillis, 383 File stageDir, String stageCid, boolean prepared, boolean sealed) { 384 mCallback = callback; 385 mContext = context; 386 mPm = pm; 387 mHandler = new Handler(looper, mHandlerCallback); 388 389 this.sessionId = sessionId; 390 this.userId = userId; 391 mOriginalInstallerUid = installerUid; 392 mInstallerPackageName = installerPackageName; 393 mInstallerUid = installerUid; 394 this.params = params; 395 this.createdMillis = createdMillis; 396 this.stageDir = stageDir; 397 this.stageCid = stageCid; 398 399 if ((stageDir == null) == (stageCid == null)) { 400 throw new IllegalArgumentException( 401 "Exactly one of stageDir or stageCid stage must be set"); 402 } 403 404 mPrepared = prepared; 405 406 if (sealed) { 407 synchronized (mLock) { 408 try { 409 sealAndValidateLocked(); 410 } catch (PackageManagerException | IOException e) { 411 destroyInternal(); 412 throw new IllegalArgumentException(e); 413 } 414 } 415 } 416 417 final long identity = Binder.clearCallingIdentity(); 418 try { 419 final int uid = mPm.getPackageUid(PackageManagerService.DEFAULT_CONTAINER_PACKAGE, 420 PackageManager.MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM); 421 defaultContainerGid = UserHandle.getSharedAppGid(uid); 422 } finally { 423 Binder.restoreCallingIdentity(identity); 424 } 425 // attempt to bind to the DefContainer as early as possible 426 if ((params.installFlags & PackageManager.INSTALL_INSTANT_APP) != 0) { 427 mHandler.sendMessage(mHandler.obtainMessage(MSG_EARLY_BIND)); 428 } 429 } 430 generateInfo()431 public SessionInfo generateInfo() { 432 return generateInfo(true); 433 } 434 generateInfo(boolean includeIcon)435 public SessionInfo generateInfo(boolean includeIcon) { 436 final SessionInfo info = new SessionInfo(); 437 synchronized (mLock) { 438 info.sessionId = sessionId; 439 info.installerPackageName = mInstallerPackageName; 440 info.resolvedBaseCodePath = (mResolvedBaseFile != null) ? 441 mResolvedBaseFile.getAbsolutePath() : null; 442 info.progress = mProgress; 443 info.sealed = mSealed; 444 info.active = mActiveCount.get() > 0; 445 446 info.mode = params.mode; 447 info.installReason = params.installReason; 448 info.sizeBytes = params.sizeBytes; 449 info.appPackageName = params.appPackageName; 450 if (includeIcon) { 451 info.appIcon = params.appIcon; 452 } 453 info.appLabel = params.appLabel; 454 455 info.installLocation = params.installLocation; 456 info.originatingUri = params.originatingUri; 457 info.originatingUid = params.originatingUid; 458 info.referrerUri = params.referrerUri; 459 info.grantedRuntimePermissions = params.grantedRuntimePermissions; 460 info.installFlags = params.installFlags; 461 } 462 return info; 463 } 464 isPrepared()465 public boolean isPrepared() { 466 synchronized (mLock) { 467 return mPrepared; 468 } 469 } 470 isSealed()471 public boolean isSealed() { 472 synchronized (mLock) { 473 return mSealed; 474 } 475 } 476 477 @GuardedBy("mLock") assertPreparedAndNotSealedLocked(String cookie)478 private void assertPreparedAndNotSealedLocked(String cookie) { 479 assertPreparedAndNotCommittedOrDestroyedLocked(cookie); 480 if (mSealed) { 481 throw new SecurityException(cookie + " not allowed after sealing"); 482 } 483 } 484 485 @GuardedBy("mLock") assertPreparedAndNotCommittedOrDestroyedLocked(String cookie)486 private void assertPreparedAndNotCommittedOrDestroyedLocked(String cookie) { 487 assertPreparedAndNotDestroyedLocked(cookie); 488 if (mCommitted) { 489 throw new SecurityException(cookie + " not allowed after commit"); 490 } 491 } 492 493 @GuardedBy("mLock") assertPreparedAndNotDestroyedLocked(String cookie)494 private void assertPreparedAndNotDestroyedLocked(String cookie) { 495 if (!mPrepared) { 496 throw new IllegalStateException(cookie + " before prepared"); 497 } 498 if (mDestroyed) { 499 throw new SecurityException(cookie + " not allowed after destruction"); 500 } 501 } 502 503 /** 504 * Resolve the actual location where staged data should be written. This 505 * might point at an ASEC mount point, which is why we delay path resolution 506 * until someone actively works with the session. 507 */ 508 @GuardedBy("mLock") resolveStageDirLocked()509 private File resolveStageDirLocked() throws IOException { 510 if (mResolvedStageDir == null) { 511 if (stageDir != null) { 512 mResolvedStageDir = stageDir; 513 } else { 514 throw new IOException("Missing stageDir"); 515 } 516 } 517 return mResolvedStageDir; 518 } 519 520 @Override setClientProgress(float progress)521 public void setClientProgress(float progress) { 522 synchronized (mLock) { 523 assertCallerIsOwnerOrRootLocked(); 524 525 // Always publish first staging movement 526 final boolean forcePublish = (mClientProgress == 0); 527 mClientProgress = progress; 528 computeProgressLocked(forcePublish); 529 } 530 } 531 532 @Override addClientProgress(float progress)533 public void addClientProgress(float progress) { 534 synchronized (mLock) { 535 assertCallerIsOwnerOrRootLocked(); 536 537 setClientProgress(mClientProgress + progress); 538 } 539 } 540 541 @GuardedBy("mLock") computeProgressLocked(boolean forcePublish)542 private void computeProgressLocked(boolean forcePublish) { 543 mProgress = MathUtils.constrain(mClientProgress * 0.8f, 0f, 0.8f) 544 + MathUtils.constrain(mInternalProgress * 0.2f, 0f, 0.2f); 545 546 // Only publish when meaningful change 547 if (forcePublish || Math.abs(mProgress - mReportedProgress) >= 0.01) { 548 mReportedProgress = mProgress; 549 mCallback.onSessionProgressChanged(this, mProgress); 550 } 551 } 552 553 @Override getNames()554 public String[] getNames() { 555 synchronized (mLock) { 556 assertCallerIsOwnerOrRootLocked(); 557 assertPreparedAndNotCommittedOrDestroyedLocked("getNames"); 558 559 try { 560 return resolveStageDirLocked().list(); 561 } catch (IOException e) { 562 throw ExceptionUtils.wrap(e); 563 } 564 } 565 } 566 567 @Override removeSplit(String splitName)568 public void removeSplit(String splitName) { 569 if (TextUtils.isEmpty(params.appPackageName)) { 570 throw new IllegalStateException("Must specify package name to remove a split"); 571 } 572 573 synchronized (mLock) { 574 assertCallerIsOwnerOrRootLocked(); 575 assertPreparedAndNotCommittedOrDestroyedLocked("removeSplit"); 576 577 try { 578 createRemoveSplitMarkerLocked(splitName); 579 } catch (IOException e) { 580 throw ExceptionUtils.wrap(e); 581 } 582 } 583 } 584 createRemoveSplitMarkerLocked(String splitName)585 private void createRemoveSplitMarkerLocked(String splitName) throws IOException { 586 try { 587 final String markerName = splitName + REMOVE_SPLIT_MARKER_EXTENSION; 588 if (!FileUtils.isValidExtFilename(markerName)) { 589 throw new IllegalArgumentException("Invalid marker: " + markerName); 590 } 591 final File target = new File(resolveStageDirLocked(), markerName); 592 target.createNewFile(); 593 Os.chmod(target.getAbsolutePath(), 0 /*mode*/); 594 } catch (ErrnoException e) { 595 throw e.rethrowAsIOException(); 596 } 597 } 598 599 @Override openWrite(String name, long offsetBytes, long lengthBytes)600 public ParcelFileDescriptor openWrite(String name, long offsetBytes, long lengthBytes) { 601 try { 602 return doWriteInternal(name, offsetBytes, lengthBytes, null); 603 } catch (IOException e) { 604 throw ExceptionUtils.wrap(e); 605 } 606 } 607 608 @Override write(String name, long offsetBytes, long lengthBytes, ParcelFileDescriptor fd)609 public void write(String name, long offsetBytes, long lengthBytes, 610 ParcelFileDescriptor fd) { 611 try { 612 doWriteInternal(name, offsetBytes, lengthBytes, fd); 613 } catch (IOException e) { 614 throw ExceptionUtils.wrap(e); 615 } 616 } 617 doWriteInternal(String name, long offsetBytes, long lengthBytes, ParcelFileDescriptor incomingFd)618 private ParcelFileDescriptor doWriteInternal(String name, long offsetBytes, long lengthBytes, 619 ParcelFileDescriptor incomingFd) throws IOException { 620 // Quick sanity check of state, and allocate a pipe for ourselves. We 621 // then do heavy disk allocation outside the lock, but this open pipe 622 // will block any attempted install transitions. 623 final RevocableFileDescriptor fd; 624 final FileBridge bridge; 625 final File stageDir; 626 synchronized (mLock) { 627 assertCallerIsOwnerOrRootLocked(); 628 assertPreparedAndNotSealedLocked("openWrite"); 629 630 if (PackageInstaller.ENABLE_REVOCABLE_FD) { 631 fd = new RevocableFileDescriptor(); 632 bridge = null; 633 mFds.add(fd); 634 } else { 635 fd = null; 636 bridge = new FileBridge(); 637 mBridges.add(bridge); 638 } 639 640 stageDir = resolveStageDirLocked(); 641 } 642 643 try { 644 // Use installer provided name for now; we always rename later 645 if (!FileUtils.isValidExtFilename(name)) { 646 throw new IllegalArgumentException("Invalid name: " + name); 647 } 648 final File target; 649 final long identity = Binder.clearCallingIdentity(); 650 try { 651 target = new File(stageDir, name); 652 } finally { 653 Binder.restoreCallingIdentity(identity); 654 } 655 656 // TODO: this should delegate to DCS so the system process avoids 657 // holding open FDs into containers. 658 final FileDescriptor targetFd = Os.open(target.getAbsolutePath(), 659 O_CREAT | O_WRONLY, 0644); 660 Os.chmod(target.getAbsolutePath(), 0644); 661 662 // If caller specified a total length, allocate it for them. Free up 663 // cache space to grow, if needed. 664 if (stageDir != null && lengthBytes > 0) { 665 mContext.getSystemService(StorageManager.class).allocateBytes(targetFd, lengthBytes, 666 PackageHelper.translateAllocateFlags(params.installFlags)); 667 } 668 669 if (offsetBytes > 0) { 670 Os.lseek(targetFd, offsetBytes, OsConstants.SEEK_SET); 671 } 672 673 if (incomingFd != null) { 674 switch (Binder.getCallingUid()) { 675 case android.os.Process.SHELL_UID: 676 case android.os.Process.ROOT_UID: 677 break; 678 default: 679 throw new SecurityException("Reverse mode only supported from shell"); 680 } 681 682 // In "reverse" mode, we're streaming data ourselves from the 683 // incoming FD, which means we never have to hand out our 684 // sensitive internal FD. We still rely on a "bridge" being 685 // inserted above to hold the session active. 686 try { 687 final Int64Ref last = new Int64Ref(0); 688 FileUtils.copy(incomingFd.getFileDescriptor(), targetFd, (long progress) -> { 689 if (params.sizeBytes > 0) { 690 final long delta = progress - last.value; 691 last.value = progress; 692 addClientProgress((float) delta / (float) params.sizeBytes); 693 } 694 }, null, lengthBytes); 695 } finally { 696 IoUtils.closeQuietly(targetFd); 697 IoUtils.closeQuietly(incomingFd); 698 699 // We're done here, so remove the "bridge" that was holding 700 // the session active. 701 synchronized (mLock) { 702 if (PackageInstaller.ENABLE_REVOCABLE_FD) { 703 mFds.remove(fd); 704 } else { 705 mBridges.remove(bridge); 706 } 707 } 708 } 709 return null; 710 } else if (PackageInstaller.ENABLE_REVOCABLE_FD) { 711 fd.init(mContext, targetFd); 712 return fd.getRevocableFileDescriptor(); 713 } else { 714 bridge.setTargetFile(targetFd); 715 bridge.start(); 716 return new ParcelFileDescriptor(bridge.getClientSocket()); 717 } 718 719 } catch (ErrnoException e) { 720 throw e.rethrowAsIOException(); 721 } 722 } 723 724 @Override openRead(String name)725 public ParcelFileDescriptor openRead(String name) { 726 synchronized (mLock) { 727 assertCallerIsOwnerOrRootLocked(); 728 assertPreparedAndNotCommittedOrDestroyedLocked("openRead"); 729 try { 730 return openReadInternalLocked(name); 731 } catch (IOException e) { 732 throw ExceptionUtils.wrap(e); 733 } 734 } 735 } 736 openReadInternalLocked(String name)737 private ParcelFileDescriptor openReadInternalLocked(String name) throws IOException { 738 try { 739 if (!FileUtils.isValidExtFilename(name)) { 740 throw new IllegalArgumentException("Invalid name: " + name); 741 } 742 final File target = new File(resolveStageDirLocked(), name); 743 final FileDescriptor targetFd = Os.open(target.getAbsolutePath(), O_RDONLY, 0); 744 return new ParcelFileDescriptor(targetFd); 745 } catch (ErrnoException e) { 746 throw e.rethrowAsIOException(); 747 } 748 } 749 750 /** 751 * Check if the caller is the owner of this session. Otherwise throw a 752 * {@link SecurityException}. 753 */ 754 @GuardedBy("mLock") assertCallerIsOwnerOrRootLocked()755 private void assertCallerIsOwnerOrRootLocked() { 756 final int callingUid = Binder.getCallingUid(); 757 if (callingUid != Process.ROOT_UID && callingUid != mInstallerUid) { 758 throw new SecurityException("Session does not belong to uid " + callingUid); 759 } 760 } 761 762 /** 763 * If anybody is reading or writing data of the session, throw an {@link SecurityException}. 764 */ 765 @GuardedBy("mLock") assertNoWriteFileTransfersOpenLocked()766 private void assertNoWriteFileTransfersOpenLocked() { 767 // Verify that all writers are hands-off 768 for (RevocableFileDescriptor fd : mFds) { 769 if (!fd.isRevoked()) { 770 throw new SecurityException("Files still open"); 771 } 772 } 773 for (FileBridge bridge : mBridges) { 774 if (!bridge.isClosed()) { 775 throw new SecurityException("Files still open"); 776 } 777 } 778 } 779 780 @Override commit(@onNull IntentSender statusReceiver, boolean forTransfer)781 public void commit(@NonNull IntentSender statusReceiver, boolean forTransfer) { 782 Preconditions.checkNotNull(statusReceiver); 783 784 final boolean wasSealed; 785 synchronized (mLock) { 786 assertCallerIsOwnerOrRootLocked(); 787 assertPreparedAndNotDestroyedLocked("commit"); 788 789 final PackageInstallObserverAdapter adapter = new PackageInstallObserverAdapter( 790 mContext, statusReceiver, sessionId, 791 isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked(), userId); 792 mRemoteObserver = adapter.getBinder(); 793 794 if (forTransfer) { 795 mContext.enforceCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES, null); 796 797 if (mInstallerUid == mOriginalInstallerUid) { 798 throw new IllegalArgumentException("Session has not been transferred"); 799 } 800 } else { 801 if (mInstallerUid != mOriginalInstallerUid) { 802 throw new IllegalArgumentException("Session has been transferred"); 803 } 804 } 805 806 wasSealed = mSealed; 807 if (!mSealed) { 808 try { 809 sealAndValidateLocked(); 810 } catch (IOException e) { 811 throw new IllegalArgumentException(e); 812 } catch (PackageManagerException e) { 813 // Do now throw an exception here to stay compatible with O and older 814 destroyInternal(); 815 dispatchSessionFinished(e.error, ExceptionUtils.getCompleteMessage(e), null); 816 return; 817 } 818 } 819 820 // Client staging is fully done at this point 821 mClientProgress = 1f; 822 computeProgressLocked(true); 823 824 // This ongoing commit should keep session active, even though client 825 // will probably close their end. 826 mActiveCount.incrementAndGet(); 827 828 mCommitted = true; 829 mHandler.obtainMessage(MSG_COMMIT).sendToTarget(); 830 } 831 832 if (!wasSealed) { 833 // Persist the fact that we've sealed ourselves to prevent 834 // mutations of any hard links we create. We do this without holding 835 // the session lock, since otherwise it's a lock inversion. 836 mCallback.onSessionSealedBlocking(this); 837 } 838 } 839 840 /** 841 * Seal the session to prevent further modification and validate the contents of it. 842 * 843 * <p>The session will be sealed after calling this method even if it failed. 844 * 845 * @throws PackageManagerException if the session was sealed but something went wrong. If the 846 * session was sealed this is the only possible exception. 847 */ 848 @GuardedBy("mLock") sealAndValidateLocked()849 private void sealAndValidateLocked() throws PackageManagerException, IOException { 850 assertNoWriteFileTransfersOpenLocked(); 851 assertPreparedAndNotDestroyedLocked("sealing of session"); 852 853 final PackageInfo pkgInfo = mPm.getPackageInfo( 854 params.appPackageName, PackageManager.GET_SIGNATURES 855 | PackageManager.MATCH_STATIC_SHARED_LIBRARIES /*flags*/, userId); 856 857 resolveStageDirLocked(); 858 859 mSealed = true; 860 861 // Verify that stage looks sane with respect to existing application. 862 // This currently only ensures packageName, versionCode, and certificate 863 // consistency. 864 try { 865 validateInstallLocked(pkgInfo); 866 } catch (PackageManagerException e) { 867 throw e; 868 } catch (Throwable e) { 869 // Convert all exceptions into package manager exceptions as only those are handled 870 // in the code above 871 throw new PackageManagerException(e); 872 } 873 874 // Read transfers from the original owner stay open, but as the session's data 875 // cannot be modified anymore, there is no leak of information. 876 } 877 878 @Override transfer(String packageName)879 public void transfer(String packageName) { 880 Preconditions.checkNotNull(packageName); 881 882 ApplicationInfo newOwnerAppInfo = mPm.getApplicationInfo(packageName, 0, userId); 883 if (newOwnerAppInfo == null) { 884 throw new ParcelableException(new PackageManager.NameNotFoundException(packageName)); 885 } 886 887 if (PackageManager.PERMISSION_GRANTED != mPm.checkUidPermission( 888 Manifest.permission.INSTALL_PACKAGES, newOwnerAppInfo.uid)) { 889 throw new SecurityException("Destination package " + packageName + " does not have " 890 + "the " + Manifest.permission.INSTALL_PACKAGES + " permission"); 891 } 892 893 // Only install flags that can be verified by the app the session is transferred to are 894 // allowed. The parameters can be read via PackageInstaller.SessionInfo. 895 if (!params.areHiddenOptionsSet()) { 896 throw new SecurityException("Can only transfer sessions that use public options"); 897 } 898 899 synchronized (mLock) { 900 assertCallerIsOwnerOrRootLocked(); 901 assertPreparedAndNotSealedLocked("transfer"); 902 903 try { 904 sealAndValidateLocked(); 905 } catch (IOException e) { 906 throw new IllegalStateException(e); 907 } catch (PackageManagerException e) { 908 // Session is sealed but could not be verified, we need to destroy it 909 destroyInternal(); 910 dispatchSessionFinished(e.error, ExceptionUtils.getCompleteMessage(e), null); 911 912 throw new IllegalArgumentException("Package is not valid", e); 913 } 914 915 if (!mPackageName.equals(mInstallerPackageName)) { 916 throw new SecurityException("Can only transfer sessions that update the original " 917 + "installer"); 918 } 919 920 mInstallerPackageName = packageName; 921 mInstallerUid = newOwnerAppInfo.uid; 922 } 923 924 // Persist the fact that we've sealed ourselves to prevent 925 // mutations of any hard links we create. We do this without holding 926 // the session lock, since otherwise it's a lock inversion. 927 mCallback.onSessionSealedBlocking(this); 928 } 929 930 @GuardedBy("mLock") commitLocked()931 private void commitLocked() 932 throws PackageManagerException { 933 if (mDestroyed) { 934 throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session destroyed"); 935 } 936 if (!mSealed) { 937 throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session not sealed"); 938 } 939 940 Preconditions.checkNotNull(mPackageName); 941 Preconditions.checkNotNull(mSigningDetails); 942 Preconditions.checkNotNull(mResolvedBaseFile); 943 944 if (needToAskForPermissionsLocked()) { 945 // User needs to accept permissions; give installer an intent they 946 // can use to involve user. 947 final Intent intent = new Intent(PackageInstaller.ACTION_CONFIRM_PERMISSIONS); 948 intent.setPackage(mContext.getPackageManager().getPermissionControllerPackageName()); 949 intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId); 950 try { 951 mRemoteObserver.onUserActionRequired(intent); 952 } catch (RemoteException ignored) { 953 } 954 955 // Commit was keeping session marked as active until now; release 956 // that extra refcount so session appears idle. 957 closeInternal(false); 958 return; 959 } 960 961 // Inherit any packages and native libraries from existing install that 962 // haven't been overridden. 963 if (params.mode == SessionParams.MODE_INHERIT_EXISTING) { 964 try { 965 final List<File> fromFiles = mResolvedInheritedFiles; 966 final File toDir = resolveStageDirLocked(); 967 968 if (LOGD) Slog.d(TAG, "Inherited files: " + mResolvedInheritedFiles); 969 if (!mResolvedInheritedFiles.isEmpty() && mInheritedFilesBase == null) { 970 throw new IllegalStateException("mInheritedFilesBase == null"); 971 } 972 973 if (isLinkPossible(fromFiles, toDir)) { 974 if (!mResolvedInstructionSets.isEmpty()) { 975 final File oatDir = new File(toDir, "oat"); 976 createOatDirs(mResolvedInstructionSets, oatDir); 977 } 978 // pre-create lib dirs for linking if necessary 979 if (!mResolvedNativeLibPaths.isEmpty()) { 980 for (String libPath : mResolvedNativeLibPaths) { 981 // "/lib/arm64" -> ["lib", "arm64"] 982 final int splitIndex = libPath.lastIndexOf('/'); 983 if (splitIndex < 0 || splitIndex >= libPath.length() - 1) { 984 Slog.e(TAG, "Skipping native library creation for linking due to " 985 + "invalid path: " + libPath); 986 continue; 987 } 988 final String libDirPath = libPath.substring(1, splitIndex); 989 final File libDir = new File(toDir, libDirPath); 990 if (!libDir.exists()) { 991 NativeLibraryHelper.createNativeLibrarySubdir(libDir); 992 } 993 final String archDirPath = libPath.substring(splitIndex + 1); 994 NativeLibraryHelper.createNativeLibrarySubdir( 995 new File(libDir, archDirPath)); 996 } 997 } 998 linkFiles(fromFiles, toDir, mInheritedFilesBase); 999 } else { 1000 // TODO: this should delegate to DCS so the system process 1001 // avoids holding open FDs into containers. 1002 copyFiles(fromFiles, toDir); 1003 } 1004 } catch (IOException e) { 1005 throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE, 1006 "Failed to inherit existing install", e); 1007 } 1008 } 1009 1010 // TODO: surface more granular state from dexopt 1011 mInternalProgress = 0.5f; 1012 computeProgressLocked(true); 1013 1014 // Unpack native libraries 1015 extractNativeLibraries(mResolvedStageDir, params.abiOverride, mayInheritNativeLibs()); 1016 1017 // We've reached point of no return; call into PMS to install the stage. 1018 // Regardless of success or failure we always destroy session. 1019 final IPackageInstallObserver2 localObserver = new IPackageInstallObserver2.Stub() { 1020 @Override 1021 public void onUserActionRequired(Intent intent) { 1022 throw new IllegalStateException(); 1023 } 1024 1025 @Override 1026 public void onPackageInstalled(String basePackageName, int returnCode, String msg, 1027 Bundle extras) { 1028 destroyInternal(); 1029 dispatchSessionFinished(returnCode, msg, extras); 1030 } 1031 }; 1032 1033 final UserHandle user; 1034 if ((params.installFlags & PackageManager.INSTALL_ALL_USERS) != 0) { 1035 user = UserHandle.ALL; 1036 } else { 1037 user = new UserHandle(userId); 1038 } 1039 1040 mRelinquished = true; 1041 mPm.installStage(mPackageName, stageDir, localObserver, params, 1042 mInstallerPackageName, mInstallerUid, user, mSigningDetails); 1043 } 1044 maybeRenameFile(File from, File to)1045 private static void maybeRenameFile(File from, File to) throws PackageManagerException { 1046 if (!from.equals(to)) { 1047 if (!from.renameTo(to)) { 1048 throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, 1049 "Could not rename file " + from + " to " + to); 1050 } 1051 } 1052 } 1053 1054 /** 1055 * Returns true if the session should attempt to inherit any existing native libraries already 1056 * extracted at the current install location. This is necessary to prevent double loading of 1057 * native libraries already loaded by the running app. 1058 */ mayInheritNativeLibs()1059 private boolean mayInheritNativeLibs() { 1060 return SystemProperties.getBoolean(PROPERTY_NAME_INHERIT_NATIVE, true) && 1061 params.mode == SessionParams.MODE_INHERIT_EXISTING && 1062 (params.installFlags & PackageManager.DONT_KILL_APP) != 0; 1063 } 1064 1065 /** 1066 * Validate install by confirming that all application packages are have 1067 * consistent package name, version code, and signing certificates. 1068 * <p> 1069 * Clears and populates {@link #mResolvedBaseFile}, 1070 * {@link #mResolvedStagedFiles}, and {@link #mResolvedInheritedFiles}. 1071 * <p> 1072 * Renames package files in stage to match split names defined inside. 1073 * <p> 1074 * Note that upgrade compatibility is still performed by 1075 * {@link PackageManagerService}. 1076 */ 1077 @GuardedBy("mLock") validateInstallLocked(@ullable PackageInfo pkgInfo)1078 private void validateInstallLocked(@Nullable PackageInfo pkgInfo) 1079 throws PackageManagerException { 1080 mPackageName = null; 1081 mVersionCode = -1; 1082 mSigningDetails = PackageParser.SigningDetails.UNKNOWN; 1083 1084 mResolvedBaseFile = null; 1085 mResolvedStagedFiles.clear(); 1086 mResolvedInheritedFiles.clear(); 1087 1088 try { 1089 resolveStageDirLocked(); 1090 } catch (IOException e) { 1091 throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR, 1092 "Failed to resolve stage location", e); 1093 } 1094 1095 final File[] removedFiles = mResolvedStageDir.listFiles(sRemovedFilter); 1096 final List<String> removeSplitList = new ArrayList<>(); 1097 if (!ArrayUtils.isEmpty(removedFiles)) { 1098 for (File removedFile : removedFiles) { 1099 final String fileName = removedFile.getName(); 1100 final String splitName = fileName.substring( 1101 0, fileName.length() - REMOVE_SPLIT_MARKER_EXTENSION.length()); 1102 removeSplitList.add(splitName); 1103 } 1104 } 1105 1106 final File[] addedFiles = mResolvedStageDir.listFiles(sAddedFilter); 1107 if (ArrayUtils.isEmpty(addedFiles) && removeSplitList.size() == 0) { 1108 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "No packages staged"); 1109 } 1110 1111 // Verify that all staged packages are internally consistent 1112 final ArraySet<String> stagedSplits = new ArraySet<>(); 1113 for (File addedFile : addedFiles) { 1114 final ApkLite apk; 1115 try { 1116 apk = PackageParser.parseApkLite( 1117 addedFile, PackageParser.PARSE_COLLECT_CERTIFICATES); 1118 } catch (PackageParserException e) { 1119 throw PackageManagerException.from(e); 1120 } 1121 1122 if (!stagedSplits.add(apk.splitName)) { 1123 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 1124 "Split " + apk.splitName + " was defined multiple times"); 1125 } 1126 1127 // Use first package to define unknown values 1128 if (mPackageName == null) { 1129 mPackageName = apk.packageName; 1130 mVersionCode = apk.getLongVersionCode(); 1131 } 1132 if (mSigningDetails == PackageParser.SigningDetails.UNKNOWN) { 1133 mSigningDetails = apk.signingDetails; 1134 } 1135 1136 assertApkConsistentLocked(String.valueOf(addedFile), apk); 1137 1138 // Take this opportunity to enforce uniform naming 1139 final String targetName; 1140 if (apk.splitName == null) { 1141 targetName = "base" + APK_FILE_EXTENSION; 1142 } else { 1143 targetName = "split_" + apk.splitName + APK_FILE_EXTENSION; 1144 } 1145 if (!FileUtils.isValidExtFilename(targetName)) { 1146 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 1147 "Invalid filename: " + targetName); 1148 } 1149 1150 final File targetFile = new File(mResolvedStageDir, targetName); 1151 maybeRenameFile(addedFile, targetFile); 1152 1153 // Base is coming from session 1154 if (apk.splitName == null) { 1155 mResolvedBaseFile = targetFile; 1156 } 1157 1158 mResolvedStagedFiles.add(targetFile); 1159 1160 final File dexMetadataFile = DexMetadataHelper.findDexMetadataForFile(addedFile); 1161 if (dexMetadataFile != null) { 1162 if (!FileUtils.isValidExtFilename(dexMetadataFile.getName())) { 1163 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 1164 "Invalid filename: " + dexMetadataFile); 1165 } 1166 final File targetDexMetadataFile = new File(mResolvedStageDir, 1167 DexMetadataHelper.buildDexMetadataPathForApk(targetName)); 1168 mResolvedStagedFiles.add(targetDexMetadataFile); 1169 maybeRenameFile(dexMetadataFile, targetDexMetadataFile); 1170 } 1171 } 1172 1173 if (removeSplitList.size() > 0) { 1174 if (pkgInfo == null) { 1175 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 1176 "Missing existing base package for " + mPackageName); 1177 } 1178 1179 // validate split names marked for removal 1180 for (String splitName : removeSplitList) { 1181 if (!ArrayUtils.contains(pkgInfo.splitNames, splitName)) { 1182 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 1183 "Split not found: " + splitName); 1184 } 1185 } 1186 1187 // ensure we've got appropriate package name, version code and signatures 1188 if (mPackageName == null) { 1189 mPackageName = pkgInfo.packageName; 1190 mVersionCode = pkgInfo.getLongVersionCode(); 1191 } 1192 if (mSigningDetails == PackageParser.SigningDetails.UNKNOWN) { 1193 try { 1194 mSigningDetails = ApkSignatureVerifier.plsCertsNoVerifyOnlyCerts( 1195 pkgInfo.applicationInfo.sourceDir, 1196 PackageParser.SigningDetails.SignatureSchemeVersion.JAR); 1197 } catch (PackageParserException e) { 1198 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 1199 "Couldn't obtain signatures from base APK"); 1200 } 1201 } 1202 } 1203 1204 if (params.mode == SessionParams.MODE_FULL_INSTALL) { 1205 // Full installs must include a base package 1206 if (!stagedSplits.contains(null)) { 1207 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 1208 "Full install must include a base package"); 1209 } 1210 1211 } else { 1212 // Partial installs must be consistent with existing install 1213 if (pkgInfo == null || pkgInfo.applicationInfo == null) { 1214 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 1215 "Missing existing base package for " + mPackageName); 1216 } 1217 1218 final PackageLite existing; 1219 final ApkLite existingBase; 1220 ApplicationInfo appInfo = pkgInfo.applicationInfo; 1221 try { 1222 existing = PackageParser.parsePackageLite(new File(appInfo.getCodePath()), 0); 1223 existingBase = PackageParser.parseApkLite(new File(appInfo.getBaseCodePath()), 1224 PackageParser.PARSE_COLLECT_CERTIFICATES); 1225 } catch (PackageParserException e) { 1226 throw PackageManagerException.from(e); 1227 } 1228 1229 assertApkConsistentLocked("Existing base", existingBase); 1230 1231 // Inherit base if not overridden 1232 if (mResolvedBaseFile == null) { 1233 mResolvedBaseFile = new File(appInfo.getBaseCodePath()); 1234 mResolvedInheritedFiles.add(mResolvedBaseFile); 1235 // Inherit the dex metadata if present. 1236 final File baseDexMetadataFile = 1237 DexMetadataHelper.findDexMetadataForFile(mResolvedBaseFile); 1238 if (baseDexMetadataFile != null) { 1239 mResolvedInheritedFiles.add(baseDexMetadataFile); 1240 } 1241 } 1242 1243 // Inherit splits if not overridden 1244 if (!ArrayUtils.isEmpty(existing.splitNames)) { 1245 for (int i = 0; i < existing.splitNames.length; i++) { 1246 final String splitName = existing.splitNames[i]; 1247 final File splitFile = new File(existing.splitCodePaths[i]); 1248 final boolean splitRemoved = removeSplitList.contains(splitName); 1249 if (!stagedSplits.contains(splitName) && !splitRemoved) { 1250 mResolvedInheritedFiles.add(splitFile); 1251 // Inherit the dex metadata if present. 1252 final File splitDexMetadataFile = 1253 DexMetadataHelper.findDexMetadataForFile(splitFile); 1254 if (splitDexMetadataFile != null) { 1255 mResolvedInheritedFiles.add(splitDexMetadataFile); 1256 } 1257 } 1258 } 1259 } 1260 1261 // Inherit compiled oat directory. 1262 final File packageInstallDir = (new File(appInfo.getBaseCodePath())).getParentFile(); 1263 mInheritedFilesBase = packageInstallDir; 1264 final File oatDir = new File(packageInstallDir, "oat"); 1265 if (oatDir.exists()) { 1266 final File[] archSubdirs = oatDir.listFiles(); 1267 1268 // Keep track of all instruction sets we've seen compiled output for. 1269 // If we're linking (and not copying) inherited files, we can recreate the 1270 // instruction set hierarchy and link compiled output. 1271 if (archSubdirs != null && archSubdirs.length > 0) { 1272 final String[] instructionSets = InstructionSets.getAllDexCodeInstructionSets(); 1273 for (File archSubDir : archSubdirs) { 1274 // Skip any directory that isn't an ISA subdir. 1275 if (!ArrayUtils.contains(instructionSets, archSubDir.getName())) { 1276 continue; 1277 } 1278 1279 mResolvedInstructionSets.add(archSubDir.getName()); 1280 List<File> oatFiles = Arrays.asList(archSubDir.listFiles()); 1281 if (!oatFiles.isEmpty()) { 1282 mResolvedInheritedFiles.addAll(oatFiles); 1283 } 1284 } 1285 } 1286 } 1287 1288 // Inherit native libraries for DONT_KILL sessions. 1289 if (mayInheritNativeLibs() && removeSplitList.isEmpty()) { 1290 File[] libDirs = new File[]{ 1291 new File(packageInstallDir, NativeLibraryHelper.LIB_DIR_NAME), 1292 new File(packageInstallDir, NativeLibraryHelper.LIB64_DIR_NAME)}; 1293 for (File libDir : libDirs) { 1294 if (!libDir.exists() || !libDir.isDirectory()) { 1295 continue; 1296 } 1297 final List<File> libDirsToInherit = new LinkedList<>(); 1298 for (File archSubDir : libDir.listFiles()) { 1299 if (!archSubDir.isDirectory()) { 1300 continue; 1301 } 1302 String relLibPath; 1303 try { 1304 relLibPath = getRelativePath(archSubDir, packageInstallDir); 1305 } catch (IOException e) { 1306 Slog.e(TAG, "Skipping linking of native library directory!", e); 1307 // shouldn't be possible, but let's avoid inheriting these to be safe 1308 libDirsToInherit.clear(); 1309 break; 1310 } 1311 if (!mResolvedNativeLibPaths.contains(relLibPath)) { 1312 mResolvedNativeLibPaths.add(relLibPath); 1313 } 1314 libDirsToInherit.addAll(Arrays.asList(archSubDir.listFiles())); 1315 } 1316 mResolvedInheritedFiles.addAll(libDirsToInherit); 1317 } 1318 } 1319 } 1320 } 1321 1322 @GuardedBy("mLock") assertApkConsistentLocked(String tag, ApkLite apk)1323 private void assertApkConsistentLocked(String tag, ApkLite apk) 1324 throws PackageManagerException { 1325 if (!mPackageName.equals(apk.packageName)) { 1326 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag + " package " 1327 + apk.packageName + " inconsistent with " + mPackageName); 1328 } 1329 if (params.appPackageName != null && !params.appPackageName.equals(apk.packageName)) { 1330 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag 1331 + " specified package " + params.appPackageName 1332 + " inconsistent with " + apk.packageName); 1333 } 1334 if (mVersionCode != apk.getLongVersionCode()) { 1335 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag 1336 + " version code " + apk.versionCode + " inconsistent with " 1337 + mVersionCode); 1338 } 1339 if (!mSigningDetails.signaturesMatchExactly(apk.signingDetails)) { 1340 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 1341 tag + " signatures are inconsistent"); 1342 } 1343 } 1344 1345 /** 1346 * Determine if creating hard links between source and destination is 1347 * possible. That is, do they all live on the same underlying device. 1348 */ isLinkPossible(List<File> fromFiles, File toDir)1349 private boolean isLinkPossible(List<File> fromFiles, File toDir) { 1350 try { 1351 final StructStat toStat = Os.stat(toDir.getAbsolutePath()); 1352 for (File fromFile : fromFiles) { 1353 final StructStat fromStat = Os.stat(fromFile.getAbsolutePath()); 1354 if (fromStat.st_dev != toStat.st_dev) { 1355 return false; 1356 } 1357 } 1358 } catch (ErrnoException e) { 1359 Slog.w(TAG, "Failed to detect if linking possible: " + e); 1360 return false; 1361 } 1362 return true; 1363 } 1364 1365 /** 1366 * @return the uid of the owner this session 1367 */ getInstallerUid()1368 public int getInstallerUid() { 1369 synchronized (mLock) { 1370 return mInstallerUid; 1371 } 1372 } 1373 getRelativePath(File file, File base)1374 private static String getRelativePath(File file, File base) throws IOException { 1375 final String pathStr = file.getAbsolutePath(); 1376 final String baseStr = base.getAbsolutePath(); 1377 // Don't allow relative paths. 1378 if (pathStr.contains("/.") ) { 1379 throw new IOException("Invalid path (was relative) : " + pathStr); 1380 } 1381 1382 if (pathStr.startsWith(baseStr)) { 1383 return pathStr.substring(baseStr.length()); 1384 } 1385 1386 throw new IOException("File: " + pathStr + " outside base: " + baseStr); 1387 } 1388 createOatDirs(List<String> instructionSets, File fromDir)1389 private void createOatDirs(List<String> instructionSets, File fromDir) 1390 throws PackageManagerException { 1391 for (String instructionSet : instructionSets) { 1392 try { 1393 mPm.mInstaller.createOatDir(fromDir.getAbsolutePath(), instructionSet); 1394 } catch (InstallerException e) { 1395 throw PackageManagerException.from(e); 1396 } 1397 } 1398 } 1399 linkFiles(List<File> fromFiles, File toDir, File fromDir)1400 private void linkFiles(List<File> fromFiles, File toDir, File fromDir) 1401 throws IOException { 1402 for (File fromFile : fromFiles) { 1403 final String relativePath = getRelativePath(fromFile, fromDir); 1404 try { 1405 mPm.mInstaller.linkFile(relativePath, fromDir.getAbsolutePath(), 1406 toDir.getAbsolutePath()); 1407 } catch (InstallerException e) { 1408 throw new IOException("failed linkOrCreateDir(" + relativePath + ", " 1409 + fromDir + ", " + toDir + ")", e); 1410 } 1411 } 1412 1413 Slog.d(TAG, "Linked " + fromFiles.size() + " files into " + toDir); 1414 } 1415 copyFiles(List<File> fromFiles, File toDir)1416 private static void copyFiles(List<File> fromFiles, File toDir) throws IOException { 1417 // Remove any partial files from previous attempt 1418 for (File file : toDir.listFiles()) { 1419 if (file.getName().endsWith(".tmp")) { 1420 file.delete(); 1421 } 1422 } 1423 1424 for (File fromFile : fromFiles) { 1425 final File tmpFile = File.createTempFile("inherit", ".tmp", toDir); 1426 if (LOGD) Slog.d(TAG, "Copying " + fromFile + " to " + tmpFile); 1427 if (!FileUtils.copyFile(fromFile, tmpFile)) { 1428 throw new IOException("Failed to copy " + fromFile + " to " + tmpFile); 1429 } 1430 try { 1431 Os.chmod(tmpFile.getAbsolutePath(), 0644); 1432 } catch (ErrnoException e) { 1433 throw new IOException("Failed to chmod " + tmpFile); 1434 } 1435 final File toFile = new File(toDir, fromFile.getName()); 1436 if (LOGD) Slog.d(TAG, "Renaming " + tmpFile + " to " + toFile); 1437 if (!tmpFile.renameTo(toFile)) { 1438 throw new IOException("Failed to rename " + tmpFile + " to " + toFile); 1439 } 1440 } 1441 Slog.d(TAG, "Copied " + fromFiles.size() + " files into " + toDir); 1442 } 1443 extractNativeLibraries(File packageDir, String abiOverride, boolean inherit)1444 private static void extractNativeLibraries(File packageDir, String abiOverride, boolean inherit) 1445 throws PackageManagerException { 1446 final File libDir = new File(packageDir, NativeLibraryHelper.LIB_DIR_NAME); 1447 if (!inherit) { 1448 // Start from a clean slate 1449 NativeLibraryHelper.removeNativeBinariesFromDirLI(libDir, true); 1450 } 1451 1452 NativeLibraryHelper.Handle handle = null; 1453 try { 1454 handle = NativeLibraryHelper.Handle.create(packageDir); 1455 final int res = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libDir, 1456 abiOverride); 1457 if (res != PackageManager.INSTALL_SUCCEEDED) { 1458 throw new PackageManagerException(res, 1459 "Failed to extract native libraries, res=" + res); 1460 } 1461 } catch (IOException e) { 1462 throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, 1463 "Failed to extract native libraries", e); 1464 } finally { 1465 IoUtils.closeQuietly(handle); 1466 } 1467 } 1468 setPermissionsResult(boolean accepted)1469 void setPermissionsResult(boolean accepted) { 1470 if (!mSealed) { 1471 throw new SecurityException("Must be sealed to accept permissions"); 1472 } 1473 1474 if (accepted) { 1475 // Mark and kick off another install pass 1476 synchronized (mLock) { 1477 mPermissionsManuallyAccepted = true; 1478 mHandler.obtainMessage(MSG_COMMIT).sendToTarget(); 1479 } 1480 } else { 1481 destroyInternal(); 1482 dispatchSessionFinished(INSTALL_FAILED_ABORTED, "User rejected permissions", null); 1483 } 1484 } 1485 open()1486 public void open() throws IOException { 1487 if (mActiveCount.getAndIncrement() == 0) { 1488 mCallback.onSessionActiveChanged(this, true); 1489 } 1490 1491 boolean wasPrepared; 1492 synchronized (mLock) { 1493 wasPrepared = mPrepared; 1494 if (!mPrepared) { 1495 if (stageDir != null) { 1496 prepareStageDir(stageDir); 1497 } else { 1498 throw new IllegalArgumentException("stageDir must be set"); 1499 } 1500 1501 mPrepared = true; 1502 } 1503 } 1504 1505 if (!wasPrepared) { 1506 mCallback.onSessionPrepared(this); 1507 } 1508 } 1509 1510 @Override close()1511 public void close() { 1512 closeInternal(true); 1513 } 1514 closeInternal(boolean checkCaller)1515 private void closeInternal(boolean checkCaller) { 1516 int activeCount; 1517 synchronized (mLock) { 1518 if (checkCaller) { 1519 assertCallerIsOwnerOrRootLocked(); 1520 } 1521 1522 activeCount = mActiveCount.decrementAndGet(); 1523 } 1524 1525 if (activeCount == 0) { 1526 mCallback.onSessionActiveChanged(this, false); 1527 } 1528 } 1529 1530 @Override abandon()1531 public void abandon() { 1532 synchronized (mLock) { 1533 assertCallerIsOwnerOrRootLocked(); 1534 1535 if (mRelinquished) { 1536 Slog.d(TAG, "Ignoring abandon after commit relinquished control"); 1537 return; 1538 } 1539 destroyInternal(); 1540 } 1541 1542 dispatchSessionFinished(INSTALL_FAILED_ABORTED, "Session was abandoned", null); 1543 } 1544 dispatchSessionFinished(int returnCode, String msg, Bundle extras)1545 private void dispatchSessionFinished(int returnCode, String msg, Bundle extras) { 1546 final IPackageInstallObserver2 observer; 1547 final String packageName; 1548 synchronized (mLock) { 1549 mFinalStatus = returnCode; 1550 mFinalMessage = msg; 1551 1552 observer = mRemoteObserver; 1553 packageName = mPackageName; 1554 } 1555 1556 if (observer != null) { 1557 // Execute observer.onPackageInstalled on different tread as we don't want callers 1558 // inside the system server have to worry about catching the callbacks while they are 1559 // calling into the session 1560 final SomeArgs args = SomeArgs.obtain(); 1561 args.arg1 = packageName; 1562 args.arg2 = msg; 1563 args.arg3 = extras; 1564 args.arg4 = observer; 1565 args.argi1 = returnCode; 1566 1567 mHandler.obtainMessage(MSG_ON_PACKAGE_INSTALLED, args).sendToTarget(); 1568 } 1569 1570 final boolean success = (returnCode == PackageManager.INSTALL_SUCCEEDED); 1571 1572 // Send broadcast to default launcher only if it's a new install 1573 final boolean isNewInstall = extras == null || !extras.getBoolean(Intent.EXTRA_REPLACING); 1574 if (success && isNewInstall) { 1575 mPm.sendSessionCommitBroadcast(generateInfo(), userId); 1576 } 1577 1578 mCallback.onSessionFinished(this, success); 1579 } 1580 destroyInternal()1581 private void destroyInternal() { 1582 synchronized (mLock) { 1583 mSealed = true; 1584 mDestroyed = true; 1585 1586 // Force shut down all bridges 1587 for (RevocableFileDescriptor fd : mFds) { 1588 fd.revoke(); 1589 } 1590 for (FileBridge bridge : mBridges) { 1591 bridge.forceClose(); 1592 } 1593 } 1594 if (stageDir != null) { 1595 try { 1596 mPm.mInstaller.rmPackageDir(stageDir.getAbsolutePath()); 1597 } catch (InstallerException ignored) { 1598 } 1599 } 1600 } 1601 dump(IndentingPrintWriter pw)1602 void dump(IndentingPrintWriter pw) { 1603 synchronized (mLock) { 1604 dumpLocked(pw); 1605 } 1606 } 1607 1608 @GuardedBy("mLock") dumpLocked(IndentingPrintWriter pw)1609 private void dumpLocked(IndentingPrintWriter pw) { 1610 pw.println("Session " + sessionId + ":"); 1611 pw.increaseIndent(); 1612 1613 pw.printPair("userId", userId); 1614 pw.printPair("mOriginalInstallerUid", mOriginalInstallerUid); 1615 pw.printPair("mInstallerPackageName", mInstallerPackageName); 1616 pw.printPair("mInstallerUid", mInstallerUid); 1617 pw.printPair("createdMillis", createdMillis); 1618 pw.printPair("stageDir", stageDir); 1619 pw.printPair("stageCid", stageCid); 1620 pw.println(); 1621 1622 params.dump(pw); 1623 1624 pw.printPair("mClientProgress", mClientProgress); 1625 pw.printPair("mProgress", mProgress); 1626 pw.printPair("mSealed", mSealed); 1627 pw.printPair("mPermissionsManuallyAccepted", mPermissionsManuallyAccepted); 1628 pw.printPair("mRelinquished", mRelinquished); 1629 pw.printPair("mDestroyed", mDestroyed); 1630 pw.printPair("mFds", mFds.size()); 1631 pw.printPair("mBridges", mBridges.size()); 1632 pw.printPair("mFinalStatus", mFinalStatus); 1633 pw.printPair("mFinalMessage", mFinalMessage); 1634 pw.println(); 1635 1636 pw.decreaseIndent(); 1637 } 1638 writeGrantedRuntimePermissionsLocked(XmlSerializer out, String[] grantedRuntimePermissions)1639 private static void writeGrantedRuntimePermissionsLocked(XmlSerializer out, 1640 String[] grantedRuntimePermissions) throws IOException { 1641 if (grantedRuntimePermissions != null) { 1642 for (String permission : grantedRuntimePermissions) { 1643 out.startTag(null, TAG_GRANTED_RUNTIME_PERMISSION); 1644 writeStringAttribute(out, ATTR_NAME, permission); 1645 out.endTag(null, TAG_GRANTED_RUNTIME_PERMISSION); 1646 } 1647 } 1648 } 1649 buildAppIconFile(int sessionId, @NonNull File sessionsDir)1650 private static File buildAppIconFile(int sessionId, @NonNull File sessionsDir) { 1651 return new File(sessionsDir, "app_icon." + sessionId + ".png"); 1652 } 1653 1654 /** 1655 * Write this session to a {@link XmlSerializer}. 1656 * 1657 * @param out Where to write the session to 1658 * @param sessionsDir The directory containing the sessions 1659 */ write(@onNull XmlSerializer out, @NonNull File sessionsDir)1660 void write(@NonNull XmlSerializer out, @NonNull File sessionsDir) throws IOException { 1661 synchronized (mLock) { 1662 if (mDestroyed) { 1663 return; 1664 } 1665 1666 out.startTag(null, TAG_SESSION); 1667 1668 writeIntAttribute(out, ATTR_SESSION_ID, sessionId); 1669 writeIntAttribute(out, ATTR_USER_ID, userId); 1670 writeStringAttribute(out, ATTR_INSTALLER_PACKAGE_NAME, 1671 mInstallerPackageName); 1672 writeIntAttribute(out, ATTR_INSTALLER_UID, mInstallerUid); 1673 writeLongAttribute(out, ATTR_CREATED_MILLIS, createdMillis); 1674 if (stageDir != null) { 1675 writeStringAttribute(out, ATTR_SESSION_STAGE_DIR, 1676 stageDir.getAbsolutePath()); 1677 } 1678 if (stageCid != null) { 1679 writeStringAttribute(out, ATTR_SESSION_STAGE_CID, stageCid); 1680 } 1681 writeBooleanAttribute(out, ATTR_PREPARED, isPrepared()); 1682 writeBooleanAttribute(out, ATTR_SEALED, isSealed()); 1683 1684 writeIntAttribute(out, ATTR_MODE, params.mode); 1685 writeIntAttribute(out, ATTR_INSTALL_FLAGS, params.installFlags); 1686 writeIntAttribute(out, ATTR_INSTALL_LOCATION, params.installLocation); 1687 writeLongAttribute(out, ATTR_SIZE_BYTES, params.sizeBytes); 1688 writeStringAttribute(out, ATTR_APP_PACKAGE_NAME, params.appPackageName); 1689 writeStringAttribute(out, ATTR_APP_LABEL, params.appLabel); 1690 writeUriAttribute(out, ATTR_ORIGINATING_URI, params.originatingUri); 1691 writeIntAttribute(out, ATTR_ORIGINATING_UID, params.originatingUid); 1692 writeUriAttribute(out, ATTR_REFERRER_URI, params.referrerUri); 1693 writeStringAttribute(out, ATTR_ABI_OVERRIDE, params.abiOverride); 1694 writeStringAttribute(out, ATTR_VOLUME_UUID, params.volumeUuid); 1695 writeIntAttribute(out, ATTR_INSTALL_REASON, params.installReason); 1696 1697 writeGrantedRuntimePermissionsLocked(out, params.grantedRuntimePermissions); 1698 1699 // Persist app icon if changed since last written 1700 File appIconFile = buildAppIconFile(sessionId, sessionsDir); 1701 if (params.appIcon == null && appIconFile.exists()) { 1702 appIconFile.delete(); 1703 } else if (params.appIcon != null 1704 && appIconFile.lastModified() != params.appIconLastModified) { 1705 if (LOGD) Slog.w(TAG, "Writing changed icon " + appIconFile); 1706 FileOutputStream os = null; 1707 try { 1708 os = new FileOutputStream(appIconFile); 1709 params.appIcon.compress(Bitmap.CompressFormat.PNG, 90, os); 1710 } catch (IOException e) { 1711 Slog.w(TAG, "Failed to write icon " + appIconFile + ": " + e.getMessage()); 1712 } finally { 1713 IoUtils.closeQuietly(os); 1714 } 1715 1716 params.appIconLastModified = appIconFile.lastModified(); 1717 } 1718 } 1719 1720 out.endTag(null, TAG_SESSION); 1721 } 1722 readGrantedRuntimePermissions(XmlPullParser in)1723 private static String[] readGrantedRuntimePermissions(XmlPullParser in) 1724 throws IOException, XmlPullParserException { 1725 List<String> permissions = null; 1726 1727 final int outerDepth = in.getDepth(); 1728 int type; 1729 while ((type = in.next()) != XmlPullParser.END_DOCUMENT 1730 && (type != XmlPullParser.END_TAG || in.getDepth() > outerDepth)) { 1731 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { 1732 continue; 1733 } 1734 if (TAG_GRANTED_RUNTIME_PERMISSION.equals(in.getName())) { 1735 String permission = readStringAttribute(in, ATTR_NAME); 1736 if (permissions == null) { 1737 permissions = new ArrayList<>(); 1738 } 1739 permissions.add(permission); 1740 } 1741 } 1742 1743 if (permissions == null) { 1744 return null; 1745 } 1746 1747 String[] permissionsArray = new String[permissions.size()]; 1748 permissions.toArray(permissionsArray); 1749 return permissionsArray; 1750 } 1751 1752 /** 1753 * Read new session from a {@link XmlPullParser xml description} and create it. 1754 * 1755 * @param in The source of the description 1756 * @param callback Callback the session uses to notify about changes of it's state 1757 * @param context Context to be used by the session 1758 * @param pm PackageManager to use by the session 1759 * @param installerThread Thread to be used for callbacks of this session 1760 * @param sessionsDir The directory the sessions are stored in 1761 * 1762 * @return The newly created session 1763 */ readFromXml(@onNull XmlPullParser in, @NonNull PackageInstallerService.InternalCallback callback, @NonNull Context context, @NonNull PackageManagerService pm, Looper installerThread, @NonNull File sessionsDir)1764 public static PackageInstallerSession readFromXml(@NonNull XmlPullParser in, 1765 @NonNull PackageInstallerService.InternalCallback callback, @NonNull Context context, 1766 @NonNull PackageManagerService pm, Looper installerThread, @NonNull File sessionsDir) 1767 throws IOException, XmlPullParserException { 1768 final int sessionId = readIntAttribute(in, ATTR_SESSION_ID); 1769 final int userId = readIntAttribute(in, ATTR_USER_ID); 1770 final String installerPackageName = readStringAttribute(in, ATTR_INSTALLER_PACKAGE_NAME); 1771 final int installerUid = readIntAttribute(in, ATTR_INSTALLER_UID, pm.getPackageUid( 1772 installerPackageName, PackageManager.MATCH_UNINSTALLED_PACKAGES, userId)); 1773 final long createdMillis = readLongAttribute(in, ATTR_CREATED_MILLIS); 1774 final String stageDirRaw = readStringAttribute(in, ATTR_SESSION_STAGE_DIR); 1775 final File stageDir = (stageDirRaw != null) ? new File(stageDirRaw) : null; 1776 final String stageCid = readStringAttribute(in, ATTR_SESSION_STAGE_CID); 1777 final boolean prepared = readBooleanAttribute(in, ATTR_PREPARED, true); 1778 final boolean sealed = readBooleanAttribute(in, ATTR_SEALED); 1779 1780 final SessionParams params = new SessionParams( 1781 SessionParams.MODE_INVALID); 1782 params.mode = readIntAttribute(in, ATTR_MODE); 1783 params.installFlags = readIntAttribute(in, ATTR_INSTALL_FLAGS); 1784 params.installLocation = readIntAttribute(in, ATTR_INSTALL_LOCATION); 1785 params.sizeBytes = readLongAttribute(in, ATTR_SIZE_BYTES); 1786 params.appPackageName = readStringAttribute(in, ATTR_APP_PACKAGE_NAME); 1787 params.appIcon = readBitmapAttribute(in, ATTR_APP_ICON); 1788 params.appLabel = readStringAttribute(in, ATTR_APP_LABEL); 1789 params.originatingUri = readUriAttribute(in, ATTR_ORIGINATING_URI); 1790 params.originatingUid = 1791 readIntAttribute(in, ATTR_ORIGINATING_UID, SessionParams.UID_UNKNOWN); 1792 params.referrerUri = readUriAttribute(in, ATTR_REFERRER_URI); 1793 params.abiOverride = readStringAttribute(in, ATTR_ABI_OVERRIDE); 1794 params.volumeUuid = readStringAttribute(in, ATTR_VOLUME_UUID); 1795 params.installReason = readIntAttribute(in, ATTR_INSTALL_REASON); 1796 1797 params.grantedRuntimePermissions = readGrantedRuntimePermissions(in); 1798 1799 final File appIconFile = buildAppIconFile(sessionId, sessionsDir); 1800 if (appIconFile.exists()) { 1801 params.appIcon = BitmapFactory.decodeFile(appIconFile.getAbsolutePath()); 1802 params.appIconLastModified = appIconFile.lastModified(); 1803 } 1804 1805 return new PackageInstallerSession(callback, context, pm, 1806 installerThread, sessionId, userId, installerPackageName, installerUid, 1807 params, createdMillis, stageDir, stageCid, prepared, sealed); 1808 } 1809 } 1810