PackageInstallerSession.java revision e980804df16c968c14a56b8853886bf5f049f46e
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 17package com.android.server.pm; 18 19import static android.content.pm.PackageManager.INSTALL_FAILED_ABORTED; 20import static android.content.pm.PackageManager.INSTALL_FAILED_CONTAINER_ERROR; 21import static android.content.pm.PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; 22import static android.content.pm.PackageManager.INSTALL_FAILED_INTERNAL_ERROR; 23import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK; 24import static android.system.OsConstants.O_CREAT; 25import static android.system.OsConstants.O_RDONLY; 26import static android.system.OsConstants.O_WRONLY; 27import static com.android.server.pm.PackageInstallerService.prepareExternalStageCid; 28import static com.android.server.pm.PackageInstallerService.prepareInternalStageDir; 29 30import android.content.Context; 31import android.content.Intent; 32import android.content.IntentSender; 33import android.content.pm.ApplicationInfo; 34import android.content.pm.IPackageInstallObserver2; 35import android.content.pm.IPackageInstallerSession; 36import android.content.pm.PackageInstaller; 37import android.content.pm.PackageInstaller.SessionInfo; 38import android.content.pm.PackageInstaller.SessionParams; 39import android.content.pm.PackageManager; 40import android.content.pm.PackageParser; 41import android.content.pm.PackageParser.ApkLite; 42import android.content.pm.PackageParser.PackageLite; 43import android.content.pm.PackageParser.PackageParserException; 44import android.content.pm.Signature; 45import android.os.Bundle; 46import android.os.FileBridge; 47import android.os.FileUtils; 48import android.os.Handler; 49import android.os.Looper; 50import android.os.Message; 51import android.os.ParcelFileDescriptor; 52import android.os.Process; 53import android.os.RemoteException; 54import android.os.UserHandle; 55import android.system.ErrnoException; 56import android.system.Os; 57import android.system.OsConstants; 58import android.system.StructStat; 59import android.util.ArraySet; 60import android.util.ExceptionUtils; 61import android.util.MathUtils; 62import android.util.Slog; 63 64import com.android.internal.annotations.GuardedBy; 65import com.android.internal.content.NativeLibraryHelper; 66import com.android.internal.content.PackageHelper; 67import com.android.internal.util.ArrayUtils; 68import com.android.internal.util.IndentingPrintWriter; 69import com.android.internal.util.Preconditions; 70import com.android.server.pm.PackageInstallerService.PackageInstallObserverAdapter; 71 72import libcore.io.IoUtils; 73import libcore.io.Libcore; 74 75import java.io.File; 76import java.io.FileDescriptor; 77import java.io.IOException; 78import java.util.ArrayList; 79import java.util.List; 80import java.util.concurrent.atomic.AtomicInteger; 81 82public class PackageInstallerSession extends IPackageInstallerSession.Stub { 83 private static final String TAG = "PackageInstaller"; 84 private static final boolean LOGD = true; 85 86 private static final int MSG_COMMIT = 0; 87 88 // TODO: enforce INSTALL_ALLOW_TEST 89 // TODO: enforce INSTALL_ALLOW_DOWNGRADE 90 91 private final PackageInstallerService.InternalCallback mCallback; 92 private final Context mContext; 93 private final PackageManagerService mPm; 94 private final Handler mHandler; 95 96 final int sessionId; 97 final int userId; 98 final String installerPackageName; 99 final int installerUid; 100 final SessionParams params; 101 final long createdMillis; 102 103 /** Staging location where client data is written. */ 104 final File stageDir; 105 final String stageCid; 106 107 private final AtomicInteger mActiveCount = new AtomicInteger(); 108 109 private final Object mLock = new Object(); 110 111 @GuardedBy("mLock") 112 private float mClientProgress = 0; 113 @GuardedBy("mLock") 114 private float mInternalProgress = 0; 115 116 @GuardedBy("mLock") 117 private float mProgress = 0; 118 @GuardedBy("mLock") 119 private float mReportedProgress = -1; 120 121 @GuardedBy("mLock") 122 private boolean mPrepared = false; 123 @GuardedBy("mLock") 124 private boolean mSealed = false; 125 @GuardedBy("mLock") 126 private boolean mPermissionsAccepted = false; 127 @GuardedBy("mLock") 128 private boolean mDestroyed = false; 129 130 private int mFinalStatus; 131 private String mFinalMessage; 132 133 @GuardedBy("mLock") 134 private ArrayList<FileBridge> mBridges = new ArrayList<>(); 135 136 @GuardedBy("mLock") 137 private IPackageInstallObserver2 mRemoteObserver; 138 139 /** Fields derived from commit parsing */ 140 private String mPackageName; 141 private int mVersionCode; 142 private Signature[] mSignatures; 143 144 /** 145 * Path to the validated base APK for this session, which may point at an 146 * APK inside the session (when the session defines the base), or it may 147 * point at the existing base APK (when adding splits to an existing app). 148 * <p> 149 * This is used when confirming permissions, since we can't fully stage the 150 * session inside an ASEC before confirming with user. 151 */ 152 @GuardedBy("mLock") 153 private File mResolvedBaseFile; 154 155 @GuardedBy("mLock") 156 private File mResolvedStageDir; 157 158 @GuardedBy("mLock") 159 private final List<File> mResolvedStagedFiles = new ArrayList<>(); 160 @GuardedBy("mLock") 161 private final List<File> mResolvedInheritedFiles = new ArrayList<>(); 162 163 private final Handler.Callback mHandlerCallback = new Handler.Callback() { 164 @Override 165 public boolean handleMessage(Message msg) { 166 synchronized (mLock) { 167 if (msg.obj != null) { 168 mRemoteObserver = (IPackageInstallObserver2) msg.obj; 169 } 170 171 try { 172 commitLocked(); 173 } catch (PackageManagerException e) { 174 final String completeMsg = ExceptionUtils.getCompleteMessage(e); 175 Slog.e(TAG, "Commit of session " + sessionId + " failed: " + completeMsg); 176 destroyInternal(); 177 dispatchSessionFinished(e.error, completeMsg, null); 178 } 179 180 return true; 181 } 182 } 183 }; 184 185 public PackageInstallerSession(PackageInstallerService.InternalCallback callback, 186 Context context, PackageManagerService pm, Looper looper, int sessionId, int userId, 187 String installerPackageName, int installerUid, SessionParams params, long createdMillis, 188 File stageDir, String stageCid, boolean prepared, boolean sealed) { 189 mCallback = callback; 190 mContext = context; 191 mPm = pm; 192 mHandler = new Handler(looper, mHandlerCallback); 193 194 this.sessionId = sessionId; 195 this.userId = userId; 196 this.installerPackageName = installerPackageName; 197 this.installerUid = installerUid; 198 this.params = params; 199 this.createdMillis = createdMillis; 200 this.stageDir = stageDir; 201 this.stageCid = stageCid; 202 203 if ((stageDir == null) == (stageCid == null)) { 204 throw new IllegalArgumentException( 205 "Exactly one of stageDir or stageCid stage must be set"); 206 } 207 208 mPrepared = prepared; 209 mSealed = sealed; 210 211 if ((mPm.checkUidPermission(android.Manifest.permission.INSTALL_PACKAGES, installerUid) 212 == PackageManager.PERMISSION_GRANTED) || (installerUid == Process.ROOT_UID)) { 213 mPermissionsAccepted = true; 214 } else { 215 mPermissionsAccepted = false; 216 } 217 } 218 219 public SessionInfo generateInfo() { 220 final SessionInfo info = new SessionInfo(); 221 synchronized (mLock) { 222 info.sessionId = sessionId; 223 info.installerPackageName = installerPackageName; 224 info.resolvedBaseCodePath = (mResolvedBaseFile != null) ? 225 mResolvedBaseFile.getAbsolutePath() : null; 226 info.progress = mProgress; 227 info.sealed = mSealed; 228 info.active = mActiveCount.get() > 0; 229 230 info.mode = params.mode; 231 info.sizeBytes = params.sizeBytes; 232 info.appPackageName = params.appPackageName; 233 info.appIcon = params.appIcon; 234 info.appLabel = params.appLabel; 235 } 236 return info; 237 } 238 239 public boolean isPrepared() { 240 synchronized (mLock) { 241 return mPrepared; 242 } 243 } 244 245 public boolean isSealed() { 246 synchronized (mLock) { 247 return mSealed; 248 } 249 } 250 251 private void assertPreparedAndNotSealed(String cookie) { 252 synchronized (mLock) { 253 if (!mPrepared) { 254 throw new IllegalStateException(cookie + " before prepared"); 255 } 256 if (mSealed) { 257 throw new SecurityException(cookie + " not allowed after commit"); 258 } 259 } 260 } 261 262 /** 263 * Resolve the actual location where staged data should be written. This 264 * might point at an ASEC mount point, which is why we delay path resolution 265 * until someone actively works with the session. 266 */ 267 private File resolveStageDir() throws IOException { 268 synchronized (mLock) { 269 if (mResolvedStageDir == null) { 270 if (stageDir != null) { 271 mResolvedStageDir = stageDir; 272 } else { 273 final String path = PackageHelper.getSdDir(stageCid); 274 if (path != null) { 275 mResolvedStageDir = new File(path); 276 } else { 277 throw new IOException("Failed to resolve path to container " + stageCid); 278 } 279 } 280 } 281 return mResolvedStageDir; 282 } 283 } 284 285 @Override 286 public void setClientProgress(float progress) { 287 synchronized (mLock) { 288 // Always publish first staging movement 289 final boolean forcePublish = (mClientProgress == 0); 290 mClientProgress = progress; 291 computeProgressLocked(forcePublish); 292 } 293 } 294 295 @Override 296 public void addClientProgress(float progress) { 297 synchronized (mLock) { 298 setClientProgress(mClientProgress + progress); 299 } 300 } 301 302 private void computeProgressLocked(boolean forcePublish) { 303 mProgress = MathUtils.constrain(mClientProgress * 0.8f, 0f, 0.8f) 304 + MathUtils.constrain(mInternalProgress * 0.2f, 0f, 0.2f); 305 306 // Only publish when meaningful change 307 if (forcePublish || Math.abs(mProgress - mReportedProgress) >= 0.01) { 308 mReportedProgress = mProgress; 309 mCallback.onSessionProgressChanged(this, mProgress); 310 } 311 } 312 313 @Override 314 public String[] getNames() { 315 assertPreparedAndNotSealed("getNames"); 316 try { 317 return resolveStageDir().list(); 318 } catch (IOException e) { 319 throw ExceptionUtils.wrap(e); 320 } 321 } 322 323 @Override 324 public ParcelFileDescriptor openWrite(String name, long offsetBytes, long lengthBytes) { 325 try { 326 return openWriteInternal(name, offsetBytes, lengthBytes); 327 } catch (IOException e) { 328 throw ExceptionUtils.wrap(e); 329 } 330 } 331 332 private ParcelFileDescriptor openWriteInternal(String name, long offsetBytes, long lengthBytes) 333 throws IOException { 334 // Quick sanity check of state, and allocate a pipe for ourselves. We 335 // then do heavy disk allocation outside the lock, but this open pipe 336 // will block any attempted install transitions. 337 final FileBridge bridge; 338 synchronized (mLock) { 339 assertPreparedAndNotSealed("openWrite"); 340 341 bridge = new FileBridge(); 342 mBridges.add(bridge); 343 } 344 345 try { 346 // Use installer provided name for now; we always rename later 347 if (!FileUtils.isValidExtFilename(name)) { 348 throw new IllegalArgumentException("Invalid name: " + name); 349 } 350 final File target = new File(resolveStageDir(), name); 351 352 // TODO: this should delegate to DCS so the system process avoids 353 // holding open FDs into containers. 354 final FileDescriptor targetFd = Libcore.os.open(target.getAbsolutePath(), 355 O_CREAT | O_WRONLY, 0644); 356 Os.chmod(target.getAbsolutePath(), 0644); 357 358 // If caller specified a total length, allocate it for them. Free up 359 // cache space to grow, if needed. 360 if (lengthBytes > 0) { 361 final StructStat stat = Libcore.os.fstat(targetFd); 362 final long deltaBytes = lengthBytes - stat.st_size; 363 // Only need to free up space when writing to internal stage 364 if (stageDir != null && deltaBytes > 0) { 365 mPm.freeStorage(deltaBytes); 366 } 367 Libcore.os.posix_fallocate(targetFd, 0, lengthBytes); 368 } 369 370 if (offsetBytes > 0) { 371 Libcore.os.lseek(targetFd, offsetBytes, OsConstants.SEEK_SET); 372 } 373 374 bridge.setTargetFile(targetFd); 375 bridge.start(); 376 return new ParcelFileDescriptor(bridge.getClientSocket()); 377 378 } catch (ErrnoException e) { 379 throw e.rethrowAsIOException(); 380 } 381 } 382 383 @Override 384 public ParcelFileDescriptor openRead(String name) { 385 try { 386 return openReadInternal(name); 387 } catch (IOException e) { 388 throw ExceptionUtils.wrap(e); 389 } 390 } 391 392 private ParcelFileDescriptor openReadInternal(String name) throws IOException { 393 assertPreparedAndNotSealed("openRead"); 394 395 try { 396 if (!FileUtils.isValidExtFilename(name)) { 397 throw new IllegalArgumentException("Invalid name: " + name); 398 } 399 final File target = new File(resolveStageDir(), name); 400 401 final FileDescriptor targetFd = Libcore.os.open(target.getAbsolutePath(), O_RDONLY, 0); 402 return new ParcelFileDescriptor(targetFd); 403 404 } catch (ErrnoException e) { 405 throw e.rethrowAsIOException(); 406 } 407 } 408 409 @Override 410 public void commit(IntentSender statusReceiver) { 411 Preconditions.checkNotNull(statusReceiver); 412 413 synchronized (mLock) { 414 if (!mSealed) { 415 // Verify that all writers are hands-off 416 for (FileBridge bridge : mBridges) { 417 if (!bridge.isClosed()) { 418 throw new SecurityException("Files still open"); 419 } 420 } 421 422 // Persist the fact that we've sealed ourselves to prevent 423 // mutations of any hard links we create. 424 mSealed = true; 425 mCallback.onSessionSealed(this); 426 } 427 } 428 429 // Client staging is fully done at this point 430 mClientProgress = 1f; 431 computeProgressLocked(true); 432 433 // This ongoing commit should keep session active, even though client 434 // will probably close their end. 435 mActiveCount.incrementAndGet(); 436 437 final PackageInstallObserverAdapter adapter = new PackageInstallObserverAdapter(mContext, 438 statusReceiver, sessionId); 439 mHandler.obtainMessage(MSG_COMMIT, adapter.getBinder()).sendToTarget(); 440 } 441 442 private void commitLocked() throws PackageManagerException { 443 if (mDestroyed) { 444 throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session destroyed"); 445 } 446 if (!mSealed) { 447 throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session not sealed"); 448 } 449 450 try { 451 resolveStageDir(); 452 } catch (IOException e) { 453 throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR, 454 "Failed to resolve stage location", e); 455 } 456 457 // Verify that stage looks sane with respect to existing application. 458 // This currently only ensures packageName, versionCode, and certificate 459 // consistency. 460 validateInstallLocked(); 461 462 Preconditions.checkNotNull(mPackageName); 463 Preconditions.checkNotNull(mSignatures); 464 Preconditions.checkNotNull(mResolvedBaseFile); 465 466 if (!mPermissionsAccepted) { 467 // User needs to accept permissions; give installer an intent they 468 // can use to involve user. 469 final Intent intent = new Intent(PackageInstaller.ACTION_CONFIRM_PERMISSIONS); 470 intent.setPackage("com.android.packageinstaller"); 471 intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId); 472 try { 473 mRemoteObserver.onUserActionRequired(intent); 474 } catch (RemoteException ignored) { 475 } 476 477 // Commit was keeping session marked as active until now; release 478 // that extra refcount so session appears idle. 479 close(); 480 return; 481 } 482 483 if (stageCid != null) { 484 // Figure out the final installed size and resize the container once 485 // and for all. Internally the parser handles straddling between two 486 // locations when inheriting. 487 final long finalSize = calculateInstalledSize(); 488 resizeContainer(stageCid, finalSize); 489 } 490 491 // Inherit any packages and native libraries from existing install that 492 // haven't been overridden. 493 if (params.mode == SessionParams.MODE_INHERIT_EXISTING) { 494 try { 495 if (stageCid != null) { 496 // TODO: this should delegate to DCS so the system process 497 // avoids holding open FDs into containers. 498 copyFiles(mResolvedInheritedFiles, resolveStageDir()); 499 } else { 500 linkFiles(mResolvedInheritedFiles, resolveStageDir()); 501 } 502 } catch (IOException e) { 503 throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE, 504 "Failed to inherit existing install", e); 505 } 506 } 507 508 // TODO: surface more granular state from dexopt 509 mInternalProgress = 0.5f; 510 computeProgressLocked(true); 511 512 // Unpack native libraries 513 extractNativeLibraries(mResolvedStageDir, params.abiOverride); 514 515 // Container is ready to go, let's seal it up! 516 if (stageCid != null) { 517 finalizeAndFixContainer(stageCid); 518 } 519 520 // We've reached point of no return; call into PMS to install the stage. 521 // Regardless of success or failure we always destroy session. 522 final IPackageInstallObserver2 localObserver = new IPackageInstallObserver2.Stub() { 523 @Override 524 public void onUserActionRequired(Intent intent) { 525 throw new IllegalStateException(); 526 } 527 528 @Override 529 public void onPackageInstalled(String basePackageName, int returnCode, String msg, 530 Bundle extras) { 531 destroyInternal(); 532 dispatchSessionFinished(returnCode, msg, extras); 533 } 534 }; 535 536 final UserHandle user; 537 if ((params.installFlags & PackageManager.INSTALL_ALL_USERS) != 0) { 538 user = UserHandle.ALL; 539 } else { 540 user = new UserHandle(userId); 541 } 542 543 mPm.installStage(mPackageName, stageDir, stageCid, localObserver, params, 544 installerPackageName, installerUid, user); 545 } 546 547 /** 548 * Validate install by confirming that all application packages are have 549 * consistent package name, version code, and signing certificates. 550 * <p> 551 * Clears and populates {@link #mResolvedBaseFile}, 552 * {@link #mResolvedStagedFiles}, and {@link #mResolvedInheritedFiles}. 553 * <p> 554 * Renames package files in stage to match split names defined inside. 555 * <p> 556 * Note that upgrade compatibility is still performed by 557 * {@link PackageManagerService}. 558 */ 559 private void validateInstallLocked() throws PackageManagerException { 560 mPackageName = null; 561 mVersionCode = -1; 562 mSignatures = null; 563 564 mResolvedBaseFile = null; 565 mResolvedStagedFiles.clear(); 566 mResolvedInheritedFiles.clear(); 567 568 final File[] files = mResolvedStageDir.listFiles(); 569 if (ArrayUtils.isEmpty(files)) { 570 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "No packages staged"); 571 } 572 573 // Verify that all staged packages are internally consistent 574 final ArraySet<String> stagedSplits = new ArraySet<>(); 575 for (File file : files) { 576 577 // Installers can't stage directories, so it's fine to ignore 578 // entries like "lost+found". 579 if (file.isDirectory()) continue; 580 581 final ApkLite apk; 582 try { 583 apk = PackageParser.parseApkLite(file, PackageParser.PARSE_COLLECT_CERTIFICATES); 584 } catch (PackageParserException e) { 585 throw PackageManagerException.from(e); 586 } 587 588 if (!stagedSplits.add(apk.splitName)) { 589 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 590 "Split " + apk.splitName + " was defined multiple times"); 591 } 592 593 // Use first package to define unknown values 594 if (mPackageName == null) { 595 mPackageName = apk.packageName; 596 mVersionCode = apk.versionCode; 597 } 598 if (mSignatures == null) { 599 mSignatures = apk.signatures; 600 } 601 602 assertApkConsistent(String.valueOf(file), apk); 603 604 // Take this opportunity to enforce uniform naming 605 final String targetName; 606 if (apk.splitName == null) { 607 targetName = "base.apk"; 608 } else { 609 targetName = "split_" + apk.splitName + ".apk"; 610 } 611 if (!FileUtils.isValidExtFilename(targetName)) { 612 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 613 "Invalid filename: " + targetName); 614 } 615 616 final File targetFile = new File(mResolvedStageDir, targetName); 617 if (!file.equals(targetFile)) { 618 file.renameTo(targetFile); 619 } 620 621 // Base is coming from session 622 if (apk.splitName == null) { 623 mResolvedBaseFile = targetFile; 624 } 625 626 mResolvedStagedFiles.add(targetFile); 627 } 628 629 if (params.mode == SessionParams.MODE_FULL_INSTALL) { 630 // Full installs must include a base package 631 if (!stagedSplits.contains(null)) { 632 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 633 "Full install must include a base package"); 634 } 635 636 } else { 637 // Partial installs must be consistent with existing install 638 final ApplicationInfo app = mPm.getApplicationInfo(mPackageName, 0, userId); 639 if (app == null) { 640 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 641 "Missing existing base package for " + mPackageName); 642 } 643 644 final PackageLite existing; 645 final ApkLite existingBase; 646 try { 647 existing = PackageParser.parsePackageLite(new File(app.getCodePath()), 0); 648 existingBase = PackageParser.parseApkLite(new File(app.getBaseCodePath()), 649 PackageParser.PARSE_COLLECT_CERTIFICATES); 650 } catch (PackageParserException e) { 651 throw PackageManagerException.from(e); 652 } 653 654 assertApkConsistent("Existing base", existingBase); 655 656 // Inherit base if not overridden 657 if (mResolvedBaseFile == null) { 658 mResolvedBaseFile = new File(app.getBaseCodePath()); 659 mResolvedInheritedFiles.add(mResolvedBaseFile); 660 } 661 662 // Inherit splits if not overridden 663 if (!ArrayUtils.isEmpty(existing.splitNames)) { 664 for (int i = 0; i < existing.splitNames.length; i++) { 665 final String splitName = existing.splitNames[i]; 666 final File splitFile = new File(existing.splitCodePaths[i]); 667 668 if (!stagedSplits.contains(splitName)) { 669 mResolvedInheritedFiles.add(splitFile); 670 } 671 } 672 } 673 } 674 } 675 676 private void assertApkConsistent(String tag, ApkLite apk) throws PackageManagerException { 677 if (!mPackageName.equals(apk.packageName)) { 678 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag + " package " 679 + apk.packageName + " inconsistent with " + mPackageName); 680 } 681 if (mVersionCode != apk.versionCode) { 682 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag 683 + " version code " + apk.versionCode + " inconsistent with " 684 + mVersionCode); 685 } 686 if (!Signature.areExactMatch(mSignatures, apk.signatures)) { 687 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 688 tag + " signatures are inconsistent"); 689 } 690 } 691 692 /** 693 * Calculate the final install footprint size, combining both staged and 694 * existing APKs together and including unpacked native code from both. 695 */ 696 private long calculateInstalledSize() throws PackageManagerException { 697 Preconditions.checkNotNull(mResolvedBaseFile); 698 699 final ApkLite baseApk; 700 try { 701 baseApk = PackageParser.parseApkLite(mResolvedBaseFile, 0); 702 } catch (PackageParserException e) { 703 throw PackageManagerException.from(e); 704 } 705 706 final List<String> splitPaths = new ArrayList<>(); 707 for (File file : mResolvedStagedFiles) { 708 if (mResolvedBaseFile.equals(file)) continue; 709 splitPaths.add(file.getAbsolutePath()); 710 } 711 for (File file : mResolvedInheritedFiles) { 712 if (mResolvedBaseFile.equals(file)) continue; 713 splitPaths.add(file.getAbsolutePath()); 714 } 715 716 // This is kind of hacky; we're creating a half-parsed package that is 717 // straddled between the inherited and staged APKs. 718 final PackageLite pkg = new PackageLite(null, baseApk, null, 719 splitPaths.toArray(new String[splitPaths.size()])); 720 final boolean isForwardLocked = 721 (params.installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0; 722 723 try { 724 return PackageHelper.calculateInstalledSize(pkg, isForwardLocked, params.abiOverride); 725 } catch (IOException e) { 726 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 727 "Failed to calculate install size", e); 728 } 729 } 730 731 private static void linkFiles(List<File> fromFiles, File toDir) throws IOException { 732 for (File fromFile : fromFiles) { 733 final File toFile = new File(toDir, fromFile.getName()); 734 try { 735 if (LOGD) Slog.d(TAG, "Linking " + fromFile + " to " + toFile); 736 Os.link(fromFile.getAbsolutePath(), toFile.getAbsolutePath()); 737 } catch (ErrnoException e) { 738 throw new IOException("Failed to link " + fromFile + " to " + toFile, e); 739 } 740 } 741 Slog.d(TAG, "Linked " + fromFiles.size() + " files into " + toDir); 742 } 743 744 private static void copyFiles(List<File> fromFiles, File toDir) throws IOException { 745 // Remove any partial files from previous attempt 746 for (File file : toDir.listFiles()) { 747 if (file.getName().endsWith(".tmp")) { 748 file.delete(); 749 } 750 } 751 752 for (File fromFile : fromFiles) { 753 final File tmpFile = File.createTempFile("inherit", ".tmp", toDir); 754 if (LOGD) Slog.d(TAG, "Copying " + fromFile + " to " + tmpFile); 755 if (!FileUtils.copyFile(fromFile, tmpFile)) { 756 throw new IOException("Failed to copy " + fromFile + " to " + tmpFile); 757 } 758 759 final File toFile = new File(toDir, fromFile.getName()); 760 if (LOGD) Slog.d(TAG, "Renaming " + tmpFile + " to " + toFile); 761 if (!tmpFile.renameTo(toFile)) { 762 throw new IOException("Failed to rename " + tmpFile + " to " + toFile); 763 } 764 } 765 Slog.d(TAG, "Copied " + fromFiles.size() + " files into " + toDir); 766 } 767 768 private static void extractNativeLibraries(File packageDir, String abiOverride) 769 throws PackageManagerException { 770 // Always start from a clean slate 771 final File libDir = new File(packageDir, NativeLibraryHelper.LIB_DIR_NAME); 772 NativeLibraryHelper.removeNativeBinariesFromDirLI(libDir, true); 773 774 NativeLibraryHelper.Handle handle = null; 775 try { 776 handle = NativeLibraryHelper.Handle.create(packageDir); 777 final int res = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libDir, 778 abiOverride); 779 if (res != PackageManager.INSTALL_SUCCEEDED) { 780 throw new PackageManagerException(res, 781 "Failed to extract native libraries, res=" + res); 782 } 783 } catch (IOException e) { 784 throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, 785 "Failed to extract native libraries", e); 786 } finally { 787 IoUtils.closeQuietly(handle); 788 } 789 } 790 791 private static void resizeContainer(String cid, long targetSize) 792 throws PackageManagerException { 793 String path = PackageHelper.getSdDir(cid); 794 if (path == null) { 795 throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR, 796 "Failed to find mounted " + cid); 797 } 798 799 final long currentSize = new File(path).getTotalSpace(); 800 if (currentSize > targetSize) { 801 Slog.w(TAG, "Current size " + currentSize + " is larger than target size " 802 + targetSize + "; skipping resize"); 803 return; 804 } 805 806 if (!PackageHelper.unMountSdDir(cid)) { 807 throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR, 808 "Failed to unmount " + cid + " before resize"); 809 } 810 811 if (!PackageHelper.resizeSdDir(targetSize, cid, 812 PackageManagerService.getEncryptKey())) { 813 throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR, 814 "Failed to resize " + cid + " to " + targetSize + " bytes"); 815 } 816 817 path = PackageHelper.mountSdDir(cid, PackageManagerService.getEncryptKey(), 818 Process.SYSTEM_UID, false); 819 if (path == null) { 820 throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR, 821 "Failed to mount " + cid + " after resize"); 822 } 823 } 824 825 private void finalizeAndFixContainer(String cid) throws PackageManagerException { 826 if (!PackageHelper.finalizeSdDir(cid)) { 827 throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR, 828 "Failed to finalize container " + cid); 829 } 830 831 final int uid = mPm.getPackageUid(PackageManagerService.DEFAULT_CONTAINER_PACKAGE, 832 UserHandle.USER_OWNER); 833 final int gid = UserHandle.getSharedAppGid(uid); 834 if (!PackageHelper.fixSdPermissions(cid, gid, null)) { 835 throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR, 836 "Failed to fix permissions on container " + cid); 837 } 838 } 839 840 void setPermissionsResult(boolean accepted) { 841 if (!mSealed) { 842 throw new SecurityException("Must be sealed to accept permissions"); 843 } 844 845 if (accepted) { 846 // Mark and kick off another install pass 847 mPermissionsAccepted = true; 848 mHandler.obtainMessage(MSG_COMMIT).sendToTarget(); 849 } else { 850 destroyInternal(); 851 dispatchSessionFinished(INSTALL_FAILED_ABORTED, "User rejected permissions", null); 852 } 853 } 854 855 public void open() throws IOException { 856 if (mActiveCount.getAndIncrement() == 0) { 857 mCallback.onSessionActiveChanged(this, true); 858 } 859 860 synchronized (mLock) { 861 if (!mPrepared) { 862 if (stageDir != null) { 863 prepareInternalStageDir(stageDir); 864 } else if (stageCid != null) { 865 prepareExternalStageCid(stageCid, params.sizeBytes); 866 867 // TODO: deliver more granular progress for ASEC allocation 868 mInternalProgress = 0.25f; 869 computeProgressLocked(true); 870 } else { 871 throw new IllegalArgumentException( 872 "Exactly one of stageDir or stageCid stage must be set"); 873 } 874 875 mPrepared = true; 876 mCallback.onSessionPrepared(this); 877 } 878 } 879 } 880 881 @Override 882 public void close() { 883 if (mActiveCount.decrementAndGet() == 0) { 884 mCallback.onSessionActiveChanged(this, false); 885 } 886 } 887 888 @Override 889 public void abandon() { 890 destroyInternal(); 891 dispatchSessionFinished(INSTALL_FAILED_ABORTED, "Session was abandoned", null); 892 } 893 894 private void dispatchSessionFinished(int returnCode, String msg, Bundle extras) { 895 mFinalStatus = returnCode; 896 mFinalMessage = msg; 897 898 if (mRemoteObserver != null) { 899 try { 900 mRemoteObserver.onPackageInstalled(mPackageName, returnCode, msg, extras); 901 } catch (RemoteException ignored) { 902 } 903 } 904 905 final boolean success = (returnCode == PackageManager.INSTALL_SUCCEEDED); 906 mCallback.onSessionFinished(this, success); 907 } 908 909 private void destroyInternal() { 910 synchronized (mLock) { 911 mSealed = true; 912 mDestroyed = true; 913 914 // Force shut down all bridges 915 for (FileBridge bridge : mBridges) { 916 bridge.forceClose(); 917 } 918 } 919 if (stageDir != null) { 920 FileUtils.deleteContents(stageDir); 921 stageDir.delete(); 922 } 923 if (stageCid != null) { 924 PackageHelper.destroySdDir(stageCid); 925 } 926 } 927 928 void dump(IndentingPrintWriter pw) { 929 synchronized (mLock) { 930 dumpLocked(pw); 931 } 932 } 933 934 private void dumpLocked(IndentingPrintWriter pw) { 935 pw.println("Session " + sessionId + ":"); 936 pw.increaseIndent(); 937 938 pw.printPair("userId", userId); 939 pw.printPair("installerPackageName", installerPackageName); 940 pw.printPair("installerUid", installerUid); 941 pw.printPair("createdMillis", createdMillis); 942 pw.printPair("stageDir", stageDir); 943 pw.printPair("stageCid", stageCid); 944 pw.println(); 945 946 params.dump(pw); 947 948 pw.printPair("mClientProgress", mClientProgress); 949 pw.printPair("mProgress", mProgress); 950 pw.printPair("mSealed", mSealed); 951 pw.printPair("mPermissionsAccepted", mPermissionsAccepted); 952 pw.printPair("mDestroyed", mDestroyed); 953 pw.printPair("mBridges", mBridges.size()); 954 pw.printPair("mFinalStatus", mFinalStatus); 955 pw.printPair("mFinalMessage", mFinalMessage); 956 pw.println(); 957 958 pw.decreaseIndent(); 959 } 960} 961