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.system.OsConstants.O_CREAT; 25 import static android.system.OsConstants.O_RDONLY; 26 import static android.system.OsConstants.O_WRONLY; 27 import static com.android.server.pm.PackageInstallerService.prepareExternalStageCid; 28 import static com.android.server.pm.PackageInstallerService.prepareInternalStageDir; 29 30 import android.content.Context; 31 import android.content.Intent; 32 import android.content.IntentSender; 33 import android.content.pm.ApplicationInfo; 34 import android.content.pm.IPackageInstallObserver2; 35 import android.content.pm.IPackageInstallerSession; 36 import android.content.pm.PackageInstaller; 37 import android.content.pm.PackageInstaller.SessionInfo; 38 import android.content.pm.PackageInstaller.SessionParams; 39 import android.content.pm.PackageManager; 40 import android.content.pm.PackageParser; 41 import android.content.pm.PackageParser.ApkLite; 42 import android.content.pm.PackageParser.PackageLite; 43 import android.content.pm.PackageParser.PackageParserException; 44 import android.content.pm.Signature; 45 import android.os.Bundle; 46 import android.os.FileBridge; 47 import android.os.FileUtils; 48 import android.os.Handler; 49 import android.os.Looper; 50 import android.os.Message; 51 import android.os.ParcelFileDescriptor; 52 import android.os.Process; 53 import android.os.RemoteException; 54 import android.os.UserHandle; 55 import android.system.ErrnoException; 56 import android.system.Os; 57 import android.system.OsConstants; 58 import android.system.StructStat; 59 import android.util.ArraySet; 60 import android.util.ExceptionUtils; 61 import android.util.MathUtils; 62 import android.util.Slog; 63 64 import com.android.internal.annotations.GuardedBy; 65 import com.android.internal.content.NativeLibraryHelper; 66 import com.android.internal.content.PackageHelper; 67 import com.android.internal.util.ArrayUtils; 68 import com.android.internal.util.IndentingPrintWriter; 69 import com.android.internal.util.Preconditions; 70 import com.android.server.pm.PackageInstallerService.PackageInstallObserverAdapter; 71 72 import libcore.io.IoUtils; 73 import libcore.io.Libcore; 74 75 import java.io.File; 76 import java.io.FileDescriptor; 77 import java.io.IOException; 78 import java.util.ArrayList; 79 import java.util.List; 80 import java.util.concurrent.atomic.AtomicInteger; 81 82 public 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 final boolean wasSealed; 414 synchronized (mLock) { 415 wasSealed = mSealed; 416 if (!mSealed) { 417 // Verify that all writers are hands-off 418 for (FileBridge bridge : mBridges) { 419 if (!bridge.isClosed()) { 420 throw new SecurityException("Files still open"); 421 } 422 } 423 mSealed = true; 424 } 425 426 // Client staging is fully done at this point 427 mClientProgress = 1f; 428 computeProgressLocked(true); 429 } 430 431 if (!wasSealed) { 432 // Persist the fact that we've sealed ourselves to prevent 433 // mutations of any hard links we create. We do this without holding 434 // the session lock, since otherwise it's a lock inversion. 435 mCallback.onSessionSealedBlocking(this); 436 } 437 438 // This ongoing commit should keep session active, even though client 439 // will probably close their end. 440 mActiveCount.incrementAndGet(); 441 442 final PackageInstallObserverAdapter adapter = new PackageInstallObserverAdapter(mContext, 443 statusReceiver, sessionId); 444 mHandler.obtainMessage(MSG_COMMIT, adapter.getBinder()).sendToTarget(); 445 } 446 447 private void commitLocked() throws PackageManagerException { 448 if (mDestroyed) { 449 throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session destroyed"); 450 } 451 if (!mSealed) { 452 throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session not sealed"); 453 } 454 455 try { 456 resolveStageDir(); 457 } catch (IOException e) { 458 throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR, 459 "Failed to resolve stage location", e); 460 } 461 462 // Verify that stage looks sane with respect to existing application. 463 // This currently only ensures packageName, versionCode, and certificate 464 // consistency. 465 validateInstallLocked(); 466 467 Preconditions.checkNotNull(mPackageName); 468 Preconditions.checkNotNull(mSignatures); 469 Preconditions.checkNotNull(mResolvedBaseFile); 470 471 if (!mPermissionsAccepted) { 472 // User needs to accept permissions; give installer an intent they 473 // can use to involve user. 474 final Intent intent = new Intent(PackageInstaller.ACTION_CONFIRM_PERMISSIONS); 475 intent.setPackage("com.android.packageinstaller"); 476 intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId); 477 try { 478 mRemoteObserver.onUserActionRequired(intent); 479 } catch (RemoteException ignored) { 480 } 481 482 // Commit was keeping session marked as active until now; release 483 // that extra refcount so session appears idle. 484 close(); 485 return; 486 } 487 488 if (stageCid != null) { 489 // Figure out the final installed size and resize the container once 490 // and for all. Internally the parser handles straddling between two 491 // locations when inheriting. 492 final long finalSize = calculateInstalledSize(); 493 resizeContainer(stageCid, finalSize); 494 } 495 496 // Inherit any packages and native libraries from existing install that 497 // haven't been overridden. 498 if (params.mode == SessionParams.MODE_INHERIT_EXISTING) { 499 try { 500 if (stageCid != null) { 501 // TODO: this should delegate to DCS so the system process 502 // avoids holding open FDs into containers. 503 copyFiles(mResolvedInheritedFiles, resolveStageDir()); 504 } else { 505 linkFiles(mResolvedInheritedFiles, resolveStageDir()); 506 } 507 } catch (IOException e) { 508 throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE, 509 "Failed to inherit existing install", e); 510 } 511 } 512 513 // TODO: surface more granular state from dexopt 514 mInternalProgress = 0.5f; 515 computeProgressLocked(true); 516 517 // Unpack native libraries 518 extractNativeLibraries(mResolvedStageDir, params.abiOverride); 519 520 // Container is ready to go, let's seal it up! 521 if (stageCid != null) { 522 finalizeAndFixContainer(stageCid); 523 } 524 525 // We've reached point of no return; call into PMS to install the stage. 526 // Regardless of success or failure we always destroy session. 527 final IPackageInstallObserver2 localObserver = new IPackageInstallObserver2.Stub() { 528 @Override 529 public void onUserActionRequired(Intent intent) { 530 throw new IllegalStateException(); 531 } 532 533 @Override 534 public void onPackageInstalled(String basePackageName, int returnCode, String msg, 535 Bundle extras) { 536 destroyInternal(); 537 dispatchSessionFinished(returnCode, msg, extras); 538 } 539 }; 540 541 final UserHandle user; 542 if ((params.installFlags & PackageManager.INSTALL_ALL_USERS) != 0) { 543 user = UserHandle.ALL; 544 } else { 545 user = new UserHandle(userId); 546 } 547 548 mPm.installStage(mPackageName, stageDir, stageCid, localObserver, params, 549 installerPackageName, installerUid, user); 550 } 551 552 /** 553 * Validate install by confirming that all application packages are have 554 * consistent package name, version code, and signing certificates. 555 * <p> 556 * Clears and populates {@link #mResolvedBaseFile}, 557 * {@link #mResolvedStagedFiles}, and {@link #mResolvedInheritedFiles}. 558 * <p> 559 * Renames package files in stage to match split names defined inside. 560 * <p> 561 * Note that upgrade compatibility is still performed by 562 * {@link PackageManagerService}. 563 */ 564 private void validateInstallLocked() throws PackageManagerException { 565 mPackageName = null; 566 mVersionCode = -1; 567 mSignatures = null; 568 569 mResolvedBaseFile = null; 570 mResolvedStagedFiles.clear(); 571 mResolvedInheritedFiles.clear(); 572 573 final File[] files = mResolvedStageDir.listFiles(); 574 if (ArrayUtils.isEmpty(files)) { 575 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "No packages staged"); 576 } 577 578 // Verify that all staged packages are internally consistent 579 final ArraySet<String> stagedSplits = new ArraySet<>(); 580 for (File file : files) { 581 582 // Installers can't stage directories, so it's fine to ignore 583 // entries like "lost+found". 584 if (file.isDirectory()) continue; 585 586 final ApkLite apk; 587 try { 588 apk = PackageParser.parseApkLite(file, PackageParser.PARSE_COLLECT_CERTIFICATES); 589 } catch (PackageParserException e) { 590 throw PackageManagerException.from(e); 591 } 592 593 if (!stagedSplits.add(apk.splitName)) { 594 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 595 "Split " + apk.splitName + " was defined multiple times"); 596 } 597 598 // Use first package to define unknown values 599 if (mPackageName == null) { 600 mPackageName = apk.packageName; 601 mVersionCode = apk.versionCode; 602 } 603 if (mSignatures == null) { 604 mSignatures = apk.signatures; 605 } 606 607 assertApkConsistent(String.valueOf(file), apk); 608 609 // Take this opportunity to enforce uniform naming 610 final String targetName; 611 if (apk.splitName == null) { 612 targetName = "base.apk"; 613 } else { 614 targetName = "split_" + apk.splitName + ".apk"; 615 } 616 if (!FileUtils.isValidExtFilename(targetName)) { 617 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 618 "Invalid filename: " + targetName); 619 } 620 621 final File targetFile = new File(mResolvedStageDir, targetName); 622 if (!file.equals(targetFile)) { 623 file.renameTo(targetFile); 624 } 625 626 // Base is coming from session 627 if (apk.splitName == null) { 628 mResolvedBaseFile = targetFile; 629 } 630 631 mResolvedStagedFiles.add(targetFile); 632 } 633 634 if (params.mode == SessionParams.MODE_FULL_INSTALL) { 635 // Full installs must include a base package 636 if (!stagedSplits.contains(null)) { 637 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 638 "Full install must include a base package"); 639 } 640 641 } else { 642 // Partial installs must be consistent with existing install 643 final ApplicationInfo app = mPm.getApplicationInfo(mPackageName, 0, userId); 644 if (app == null) { 645 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 646 "Missing existing base package for " + mPackageName); 647 } 648 649 final PackageLite existing; 650 final ApkLite existingBase; 651 try { 652 existing = PackageParser.parsePackageLite(new File(app.getCodePath()), 0); 653 existingBase = PackageParser.parseApkLite(new File(app.getBaseCodePath()), 654 PackageParser.PARSE_COLLECT_CERTIFICATES); 655 } catch (PackageParserException e) { 656 throw PackageManagerException.from(e); 657 } 658 659 assertApkConsistent("Existing base", existingBase); 660 661 // Inherit base if not overridden 662 if (mResolvedBaseFile == null) { 663 mResolvedBaseFile = new File(app.getBaseCodePath()); 664 mResolvedInheritedFiles.add(mResolvedBaseFile); 665 } 666 667 // Inherit splits if not overridden 668 if (!ArrayUtils.isEmpty(existing.splitNames)) { 669 for (int i = 0; i < existing.splitNames.length; i++) { 670 final String splitName = existing.splitNames[i]; 671 final File splitFile = new File(existing.splitCodePaths[i]); 672 673 if (!stagedSplits.contains(splitName)) { 674 mResolvedInheritedFiles.add(splitFile); 675 } 676 } 677 } 678 } 679 } 680 681 private void assertApkConsistent(String tag, ApkLite apk) throws PackageManagerException { 682 if (!mPackageName.equals(apk.packageName)) { 683 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag + " package " 684 + apk.packageName + " inconsistent with " + mPackageName); 685 } 686 if (mVersionCode != apk.versionCode) { 687 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag 688 + " version code " + apk.versionCode + " inconsistent with " 689 + mVersionCode); 690 } 691 if (!Signature.areExactMatch(mSignatures, apk.signatures)) { 692 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 693 tag + " signatures are inconsistent"); 694 } 695 } 696 697 /** 698 * Calculate the final install footprint size, combining both staged and 699 * existing APKs together and including unpacked native code from both. 700 */ 701 private long calculateInstalledSize() throws PackageManagerException { 702 Preconditions.checkNotNull(mResolvedBaseFile); 703 704 final ApkLite baseApk; 705 try { 706 baseApk = PackageParser.parseApkLite(mResolvedBaseFile, 0); 707 } catch (PackageParserException e) { 708 throw PackageManagerException.from(e); 709 } 710 711 final List<String> splitPaths = new ArrayList<>(); 712 for (File file : mResolvedStagedFiles) { 713 if (mResolvedBaseFile.equals(file)) continue; 714 splitPaths.add(file.getAbsolutePath()); 715 } 716 for (File file : mResolvedInheritedFiles) { 717 if (mResolvedBaseFile.equals(file)) continue; 718 splitPaths.add(file.getAbsolutePath()); 719 } 720 721 // This is kind of hacky; we're creating a half-parsed package that is 722 // straddled between the inherited and staged APKs. 723 final PackageLite pkg = new PackageLite(null, baseApk, null, 724 splitPaths.toArray(new String[splitPaths.size()])); 725 final boolean isForwardLocked = 726 (params.installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0; 727 728 try { 729 return PackageHelper.calculateInstalledSize(pkg, isForwardLocked, params.abiOverride); 730 } catch (IOException e) { 731 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 732 "Failed to calculate install size", e); 733 } 734 } 735 736 private static void linkFiles(List<File> fromFiles, File toDir) throws IOException { 737 for (File fromFile : fromFiles) { 738 final File toFile = new File(toDir, fromFile.getName()); 739 try { 740 if (LOGD) Slog.d(TAG, "Linking " + fromFile + " to " + toFile); 741 Os.link(fromFile.getAbsolutePath(), toFile.getAbsolutePath()); 742 } catch (ErrnoException e) { 743 throw new IOException("Failed to link " + fromFile + " to " + toFile, e); 744 } 745 } 746 Slog.d(TAG, "Linked " + fromFiles.size() + " files into " + toDir); 747 } 748 749 private static void copyFiles(List<File> fromFiles, File toDir) throws IOException { 750 // Remove any partial files from previous attempt 751 for (File file : toDir.listFiles()) { 752 if (file.getName().endsWith(".tmp")) { 753 file.delete(); 754 } 755 } 756 757 for (File fromFile : fromFiles) { 758 final File tmpFile = File.createTempFile("inherit", ".tmp", toDir); 759 if (LOGD) Slog.d(TAG, "Copying " + fromFile + " to " + tmpFile); 760 if (!FileUtils.copyFile(fromFile, tmpFile)) { 761 throw new IOException("Failed to copy " + fromFile + " to " + tmpFile); 762 } 763 764 final File toFile = new File(toDir, fromFile.getName()); 765 if (LOGD) Slog.d(TAG, "Renaming " + tmpFile + " to " + toFile); 766 if (!tmpFile.renameTo(toFile)) { 767 throw new IOException("Failed to rename " + tmpFile + " to " + toFile); 768 } 769 } 770 Slog.d(TAG, "Copied " + fromFiles.size() + " files into " + toDir); 771 } 772 773 private static void extractNativeLibraries(File packageDir, String abiOverride) 774 throws PackageManagerException { 775 // Always start from a clean slate 776 final File libDir = new File(packageDir, NativeLibraryHelper.LIB_DIR_NAME); 777 NativeLibraryHelper.removeNativeBinariesFromDirLI(libDir, true); 778 779 NativeLibraryHelper.Handle handle = null; 780 try { 781 handle = NativeLibraryHelper.Handle.create(packageDir); 782 final int res = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libDir, 783 abiOverride); 784 if (res != PackageManager.INSTALL_SUCCEEDED) { 785 throw new PackageManagerException(res, 786 "Failed to extract native libraries, res=" + res); 787 } 788 } catch (IOException e) { 789 throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, 790 "Failed to extract native libraries", e); 791 } finally { 792 IoUtils.closeQuietly(handle); 793 } 794 } 795 796 private static void resizeContainer(String cid, long targetSize) 797 throws PackageManagerException { 798 String path = PackageHelper.getSdDir(cid); 799 if (path == null) { 800 throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR, 801 "Failed to find mounted " + cid); 802 } 803 804 final long currentSize = new File(path).getTotalSpace(); 805 if (currentSize > targetSize) { 806 Slog.w(TAG, "Current size " + currentSize + " is larger than target size " 807 + targetSize + "; skipping resize"); 808 return; 809 } 810 811 if (!PackageHelper.unMountSdDir(cid)) { 812 throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR, 813 "Failed to unmount " + cid + " before resize"); 814 } 815 816 if (!PackageHelper.resizeSdDir(targetSize, cid, 817 PackageManagerService.getEncryptKey())) { 818 throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR, 819 "Failed to resize " + cid + " to " + targetSize + " bytes"); 820 } 821 822 path = PackageHelper.mountSdDir(cid, PackageManagerService.getEncryptKey(), 823 Process.SYSTEM_UID, false); 824 if (path == null) { 825 throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR, 826 "Failed to mount " + cid + " after resize"); 827 } 828 } 829 830 private void finalizeAndFixContainer(String cid) throws PackageManagerException { 831 if (!PackageHelper.finalizeSdDir(cid)) { 832 throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR, 833 "Failed to finalize container " + cid); 834 } 835 836 final int uid = mPm.getPackageUid(PackageManagerService.DEFAULT_CONTAINER_PACKAGE, 837 UserHandle.USER_OWNER); 838 final int gid = UserHandle.getSharedAppGid(uid); 839 if (!PackageHelper.fixSdPermissions(cid, gid, null)) { 840 throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR, 841 "Failed to fix permissions on container " + cid); 842 } 843 } 844 845 void setPermissionsResult(boolean accepted) { 846 if (!mSealed) { 847 throw new SecurityException("Must be sealed to accept permissions"); 848 } 849 850 if (accepted) { 851 // Mark and kick off another install pass 852 mPermissionsAccepted = true; 853 mHandler.obtainMessage(MSG_COMMIT).sendToTarget(); 854 } else { 855 destroyInternal(); 856 dispatchSessionFinished(INSTALL_FAILED_ABORTED, "User rejected permissions", null); 857 } 858 } 859 860 public void open() throws IOException { 861 if (mActiveCount.getAndIncrement() == 0) { 862 mCallback.onSessionActiveChanged(this, true); 863 } 864 865 synchronized (mLock) { 866 if (!mPrepared) { 867 if (stageDir != null) { 868 prepareInternalStageDir(stageDir); 869 } else if (stageCid != null) { 870 prepareExternalStageCid(stageCid, params.sizeBytes); 871 872 // TODO: deliver more granular progress for ASEC allocation 873 mInternalProgress = 0.25f; 874 computeProgressLocked(true); 875 } else { 876 throw new IllegalArgumentException( 877 "Exactly one of stageDir or stageCid stage must be set"); 878 } 879 880 mPrepared = true; 881 mCallback.onSessionPrepared(this); 882 } 883 } 884 } 885 886 @Override 887 public void close() { 888 if (mActiveCount.decrementAndGet() == 0) { 889 mCallback.onSessionActiveChanged(this, false); 890 } 891 } 892 893 @Override 894 public void abandon() { 895 destroyInternal(); 896 dispatchSessionFinished(INSTALL_FAILED_ABORTED, "Session was abandoned", null); 897 } 898 899 private void dispatchSessionFinished(int returnCode, String msg, Bundle extras) { 900 mFinalStatus = returnCode; 901 mFinalMessage = msg; 902 903 if (mRemoteObserver != null) { 904 try { 905 mRemoteObserver.onPackageInstalled(mPackageName, returnCode, msg, extras); 906 } catch (RemoteException ignored) { 907 } 908 } 909 910 final boolean success = (returnCode == PackageManager.INSTALL_SUCCEEDED); 911 mCallback.onSessionFinished(this, success); 912 } 913 914 private void destroyInternal() { 915 synchronized (mLock) { 916 mSealed = true; 917 mDestroyed = true; 918 919 // Force shut down all bridges 920 for (FileBridge bridge : mBridges) { 921 bridge.forceClose(); 922 } 923 } 924 if (stageDir != null) { 925 FileUtils.deleteContents(stageDir); 926 stageDir.delete(); 927 } 928 if (stageCid != null) { 929 PackageHelper.destroySdDir(stageCid); 930 } 931 } 932 933 void dump(IndentingPrintWriter pw) { 934 synchronized (mLock) { 935 dumpLocked(pw); 936 } 937 } 938 939 private void dumpLocked(IndentingPrintWriter pw) { 940 pw.println("Session " + sessionId + ":"); 941 pw.increaseIndent(); 942 943 pw.printPair("userId", userId); 944 pw.printPair("installerPackageName", installerPackageName); 945 pw.printPair("installerUid", installerUid); 946 pw.printPair("createdMillis", createdMillis); 947 pw.printPair("stageDir", stageDir); 948 pw.printPair("stageCid", stageCid); 949 pw.println(); 950 951 params.dump(pw); 952 953 pw.printPair("mClientProgress", mClientProgress); 954 pw.printPair("mProgress", mProgress); 955 pw.printPair("mSealed", mSealed); 956 pw.printPair("mPermissionsAccepted", mPermissionsAccepted); 957 pw.printPair("mDestroyed", mDestroyed); 958 pw.printPair("mBridges", mBridges.size()); 959 pw.printPair("mFinalStatus", mFinalStatus); 960 pw.printPair("mFinalMessage", mFinalMessage); 961 pw.println(); 962 963 pw.decreaseIndent(); 964 } 965 } 966