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 final List<File> fromFiles = mResolvedInheritedFiles; 501 final File toDir = resolveStageDir(); 502 503 if (isLinkPossible(fromFiles, toDir)) { 504 linkFiles(fromFiles, toDir); 505 } else { 506 // TODO: this should delegate to DCS so the system process 507 // avoids holding open FDs into containers. 508 copyFiles(fromFiles, toDir); 509 } 510 } catch (IOException e) { 511 throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE, 512 "Failed to inherit existing install", e); 513 } 514 } 515 516 // TODO: surface more granular state from dexopt 517 mInternalProgress = 0.5f; 518 computeProgressLocked(true); 519 520 // Unpack native libraries 521 extractNativeLibraries(mResolvedStageDir, params.abiOverride); 522 523 // Container is ready to go, let's seal it up! 524 if (stageCid != null) { 525 finalizeAndFixContainer(stageCid); 526 } 527 528 // We've reached point of no return; call into PMS to install the stage. 529 // Regardless of success or failure we always destroy session. 530 final IPackageInstallObserver2 localObserver = new IPackageInstallObserver2.Stub() { 531 @Override 532 public void onUserActionRequired(Intent intent) { 533 throw new IllegalStateException(); 534 } 535 536 @Override 537 public void onPackageInstalled(String basePackageName, int returnCode, String msg, 538 Bundle extras) { 539 destroyInternal(); 540 dispatchSessionFinished(returnCode, msg, extras); 541 } 542 }; 543 544 final UserHandle user; 545 if ((params.installFlags & PackageManager.INSTALL_ALL_USERS) != 0) { 546 user = UserHandle.ALL; 547 } else { 548 user = new UserHandle(userId); 549 } 550 551 mPm.installStage(mPackageName, stageDir, stageCid, localObserver, params, 552 installerPackageName, installerUid, user); 553 } 554 555 /** 556 * Validate install by confirming that all application packages are have 557 * consistent package name, version code, and signing certificates. 558 * <p> 559 * Clears and populates {@link #mResolvedBaseFile}, 560 * {@link #mResolvedStagedFiles}, and {@link #mResolvedInheritedFiles}. 561 * <p> 562 * Renames package files in stage to match split names defined inside. 563 * <p> 564 * Note that upgrade compatibility is still performed by 565 * {@link PackageManagerService}. 566 */ 567 private void validateInstallLocked() throws PackageManagerException { 568 mPackageName = null; 569 mVersionCode = -1; 570 mSignatures = null; 571 572 mResolvedBaseFile = null; 573 mResolvedStagedFiles.clear(); 574 mResolvedInheritedFiles.clear(); 575 576 final File[] files = mResolvedStageDir.listFiles(); 577 if (ArrayUtils.isEmpty(files)) { 578 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "No packages staged"); 579 } 580 581 // Verify that all staged packages are internally consistent 582 final ArraySet<String> stagedSplits = new ArraySet<>(); 583 for (File file : files) { 584 585 // Installers can't stage directories, so it's fine to ignore 586 // entries like "lost+found". 587 if (file.isDirectory()) continue; 588 589 final ApkLite apk; 590 try { 591 apk = PackageParser.parseApkLite(file, PackageParser.PARSE_COLLECT_CERTIFICATES); 592 } catch (PackageParserException e) { 593 throw PackageManagerException.from(e); 594 } 595 596 if (!stagedSplits.add(apk.splitName)) { 597 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 598 "Split " + apk.splitName + " was defined multiple times"); 599 } 600 601 // Use first package to define unknown values 602 if (mPackageName == null) { 603 mPackageName = apk.packageName; 604 mVersionCode = apk.versionCode; 605 } 606 if (mSignatures == null) { 607 mSignatures = apk.signatures; 608 } 609 610 assertApkConsistent(String.valueOf(file), apk); 611 612 // Take this opportunity to enforce uniform naming 613 final String targetName; 614 if (apk.splitName == null) { 615 targetName = "base.apk"; 616 } else { 617 targetName = "split_" + apk.splitName + ".apk"; 618 } 619 if (!FileUtils.isValidExtFilename(targetName)) { 620 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 621 "Invalid filename: " + targetName); 622 } 623 624 final File targetFile = new File(mResolvedStageDir, targetName); 625 if (!file.equals(targetFile)) { 626 file.renameTo(targetFile); 627 } 628 629 // Base is coming from session 630 if (apk.splitName == null) { 631 mResolvedBaseFile = targetFile; 632 } 633 634 mResolvedStagedFiles.add(targetFile); 635 } 636 637 if (params.mode == SessionParams.MODE_FULL_INSTALL) { 638 // Full installs must include a base package 639 if (!stagedSplits.contains(null)) { 640 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 641 "Full install must include a base package"); 642 } 643 644 } else { 645 // Partial installs must be consistent with existing install 646 final ApplicationInfo app = mPm.getApplicationInfo(mPackageName, 0, userId); 647 if (app == null) { 648 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 649 "Missing existing base package for " + mPackageName); 650 } 651 652 final PackageLite existing; 653 final ApkLite existingBase; 654 try { 655 existing = PackageParser.parsePackageLite(new File(app.getCodePath()), 0); 656 existingBase = PackageParser.parseApkLite(new File(app.getBaseCodePath()), 657 PackageParser.PARSE_COLLECT_CERTIFICATES); 658 } catch (PackageParserException e) { 659 throw PackageManagerException.from(e); 660 } 661 662 assertApkConsistent("Existing base", existingBase); 663 664 // Inherit base if not overridden 665 if (mResolvedBaseFile == null) { 666 mResolvedBaseFile = new File(app.getBaseCodePath()); 667 mResolvedInheritedFiles.add(mResolvedBaseFile); 668 } 669 670 // Inherit splits if not overridden 671 if (!ArrayUtils.isEmpty(existing.splitNames)) { 672 for (int i = 0; i < existing.splitNames.length; i++) { 673 final String splitName = existing.splitNames[i]; 674 final File splitFile = new File(existing.splitCodePaths[i]); 675 676 if (!stagedSplits.contains(splitName)) { 677 mResolvedInheritedFiles.add(splitFile); 678 } 679 } 680 } 681 } 682 } 683 684 private void assertApkConsistent(String tag, ApkLite apk) throws PackageManagerException { 685 if (!mPackageName.equals(apk.packageName)) { 686 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag + " package " 687 + apk.packageName + " inconsistent with " + mPackageName); 688 } 689 if (mVersionCode != apk.versionCode) { 690 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag 691 + " version code " + apk.versionCode + " inconsistent with " 692 + mVersionCode); 693 } 694 if (!Signature.areExactMatch(mSignatures, apk.signatures)) { 695 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 696 tag + " signatures are inconsistent"); 697 } 698 } 699 700 /** 701 * Calculate the final install footprint size, combining both staged and 702 * existing APKs together and including unpacked native code from both. 703 */ 704 private long calculateInstalledSize() throws PackageManagerException { 705 Preconditions.checkNotNull(mResolvedBaseFile); 706 707 final ApkLite baseApk; 708 try { 709 baseApk = PackageParser.parseApkLite(mResolvedBaseFile, 0); 710 } catch (PackageParserException e) { 711 throw PackageManagerException.from(e); 712 } 713 714 final List<String> splitPaths = new ArrayList<>(); 715 for (File file : mResolvedStagedFiles) { 716 if (mResolvedBaseFile.equals(file)) continue; 717 splitPaths.add(file.getAbsolutePath()); 718 } 719 for (File file : mResolvedInheritedFiles) { 720 if (mResolvedBaseFile.equals(file)) continue; 721 splitPaths.add(file.getAbsolutePath()); 722 } 723 724 // This is kind of hacky; we're creating a half-parsed package that is 725 // straddled between the inherited and staged APKs. 726 final PackageLite pkg = new PackageLite(null, baseApk, null, 727 splitPaths.toArray(new String[splitPaths.size()]), null); 728 final boolean isForwardLocked = 729 (params.installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0; 730 731 try { 732 return PackageHelper.calculateInstalledSize(pkg, isForwardLocked, params.abiOverride); 733 } catch (IOException e) { 734 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 735 "Failed to calculate install size", e); 736 } 737 } 738 739 /** 740 * Determine if creating hard links between source and destination is 741 * possible. That is, do they all live on the same underlying device. 742 */ 743 private boolean isLinkPossible(List<File> fromFiles, File toDir) { 744 try { 745 final StructStat toStat = Os.stat(toDir.getAbsolutePath()); 746 for (File fromFile : fromFiles) { 747 final StructStat fromStat = Os.stat(fromFile.getAbsolutePath()); 748 if (fromStat.st_dev != toStat.st_dev) { 749 return false; 750 } 751 } 752 } catch (ErrnoException e) { 753 Slog.w(TAG, "Failed to detect if linking possible: " + e); 754 return false; 755 } 756 return true; 757 } 758 759 private static void linkFiles(List<File> fromFiles, File toDir) throws IOException { 760 for (File fromFile : fromFiles) { 761 final File toFile = new File(toDir, fromFile.getName()); 762 try { 763 if (LOGD) Slog.d(TAG, "Linking " + fromFile + " to " + toFile); 764 Os.link(fromFile.getAbsolutePath(), toFile.getAbsolutePath()); 765 } catch (ErrnoException e) { 766 throw new IOException("Failed to link " + fromFile + " to " + toFile, e); 767 } 768 } 769 Slog.d(TAG, "Linked " + fromFiles.size() + " files into " + toDir); 770 } 771 772 private static void copyFiles(List<File> fromFiles, File toDir) throws IOException { 773 // Remove any partial files from previous attempt 774 for (File file : toDir.listFiles()) { 775 if (file.getName().endsWith(".tmp")) { 776 file.delete(); 777 } 778 } 779 780 for (File fromFile : fromFiles) { 781 final File tmpFile = File.createTempFile("inherit", ".tmp", toDir); 782 if (LOGD) Slog.d(TAG, "Copying " + fromFile + " to " + tmpFile); 783 if (!FileUtils.copyFile(fromFile, tmpFile)) { 784 throw new IOException("Failed to copy " + fromFile + " to " + tmpFile); 785 } 786 try { 787 Os.chmod(tmpFile.getAbsolutePath(), 0644); 788 } catch (ErrnoException e) { 789 throw new IOException("Failed to chmod " + tmpFile); 790 } 791 final File toFile = new File(toDir, fromFile.getName()); 792 if (LOGD) Slog.d(TAG, "Renaming " + tmpFile + " to " + toFile); 793 if (!tmpFile.renameTo(toFile)) { 794 throw new IOException("Failed to rename " + tmpFile + " to " + toFile); 795 } 796 } 797 Slog.d(TAG, "Copied " + fromFiles.size() + " files into " + toDir); 798 } 799 800 private static void extractNativeLibraries(File packageDir, String abiOverride) 801 throws PackageManagerException { 802 // Always start from a clean slate 803 final File libDir = new File(packageDir, NativeLibraryHelper.LIB_DIR_NAME); 804 NativeLibraryHelper.removeNativeBinariesFromDirLI(libDir, true); 805 806 NativeLibraryHelper.Handle handle = null; 807 try { 808 handle = NativeLibraryHelper.Handle.create(packageDir); 809 final int res = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libDir, 810 abiOverride); 811 if (res != PackageManager.INSTALL_SUCCEEDED) { 812 throw new PackageManagerException(res, 813 "Failed to extract native libraries, res=" + res); 814 } 815 } catch (IOException e) { 816 throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, 817 "Failed to extract native libraries", e); 818 } finally { 819 IoUtils.closeQuietly(handle); 820 } 821 } 822 823 private static void resizeContainer(String cid, long targetSize) 824 throws PackageManagerException { 825 String path = PackageHelper.getSdDir(cid); 826 if (path == null) { 827 throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR, 828 "Failed to find mounted " + cid); 829 } 830 831 final long currentSize = new File(path).getTotalSpace(); 832 if (currentSize > targetSize) { 833 Slog.w(TAG, "Current size " + currentSize + " is larger than target size " 834 + targetSize + "; skipping resize"); 835 return; 836 } 837 838 if (!PackageHelper.unMountSdDir(cid)) { 839 throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR, 840 "Failed to unmount " + cid + " before resize"); 841 } 842 843 if (!PackageHelper.resizeSdDir(targetSize, cid, 844 PackageManagerService.getEncryptKey())) { 845 throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR, 846 "Failed to resize " + cid + " to " + targetSize + " bytes"); 847 } 848 849 path = PackageHelper.mountSdDir(cid, PackageManagerService.getEncryptKey(), 850 Process.SYSTEM_UID, false); 851 if (path == null) { 852 throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR, 853 "Failed to mount " + cid + " after resize"); 854 } 855 } 856 857 private void finalizeAndFixContainer(String cid) throws PackageManagerException { 858 if (!PackageHelper.finalizeSdDir(cid)) { 859 throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR, 860 "Failed to finalize container " + cid); 861 } 862 863 final int uid = mPm.getPackageUid(PackageManagerService.DEFAULT_CONTAINER_PACKAGE, 864 UserHandle.USER_OWNER); 865 final int gid = UserHandle.getSharedAppGid(uid); 866 if (!PackageHelper.fixSdPermissions(cid, gid, null)) { 867 throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR, 868 "Failed to fix permissions on container " + cid); 869 } 870 } 871 872 void setPermissionsResult(boolean accepted) { 873 if (!mSealed) { 874 throw new SecurityException("Must be sealed to accept permissions"); 875 } 876 877 if (accepted) { 878 // Mark and kick off another install pass 879 mPermissionsAccepted = true; 880 mHandler.obtainMessage(MSG_COMMIT).sendToTarget(); 881 } else { 882 destroyInternal(); 883 dispatchSessionFinished(INSTALL_FAILED_ABORTED, "User rejected permissions", null); 884 } 885 } 886 887 public void open() throws IOException { 888 if (mActiveCount.getAndIncrement() == 0) { 889 mCallback.onSessionActiveChanged(this, true); 890 } 891 892 synchronized (mLock) { 893 if (!mPrepared) { 894 if (stageDir != null) { 895 prepareInternalStageDir(stageDir); 896 } else if (stageCid != null) { 897 prepareExternalStageCid(stageCid, params.sizeBytes); 898 899 // TODO: deliver more granular progress for ASEC allocation 900 mInternalProgress = 0.25f; 901 computeProgressLocked(true); 902 } else { 903 throw new IllegalArgumentException( 904 "Exactly one of stageDir or stageCid stage must be set"); 905 } 906 907 mPrepared = true; 908 mCallback.onSessionPrepared(this); 909 } 910 } 911 } 912 913 @Override 914 public void close() { 915 if (mActiveCount.decrementAndGet() == 0) { 916 mCallback.onSessionActiveChanged(this, false); 917 } 918 } 919 920 @Override 921 public void abandon() { 922 destroyInternal(); 923 dispatchSessionFinished(INSTALL_FAILED_ABORTED, "Session was abandoned", null); 924 } 925 926 private void dispatchSessionFinished(int returnCode, String msg, Bundle extras) { 927 mFinalStatus = returnCode; 928 mFinalMessage = msg; 929 930 if (mRemoteObserver != null) { 931 try { 932 mRemoteObserver.onPackageInstalled(mPackageName, returnCode, msg, extras); 933 } catch (RemoteException ignored) { 934 } 935 } 936 937 final boolean success = (returnCode == PackageManager.INSTALL_SUCCEEDED); 938 mCallback.onSessionFinished(this, success); 939 } 940 941 private void destroyInternal() { 942 synchronized (mLock) { 943 mSealed = true; 944 mDestroyed = true; 945 946 // Force shut down all bridges 947 for (FileBridge bridge : mBridges) { 948 bridge.forceClose(); 949 } 950 } 951 if (stageDir != null) { 952 FileUtils.deleteContents(stageDir); 953 stageDir.delete(); 954 } 955 if (stageCid != null) { 956 PackageHelper.destroySdDir(stageCid); 957 } 958 } 959 960 void dump(IndentingPrintWriter pw) { 961 synchronized (mLock) { 962 dumpLocked(pw); 963 } 964 } 965 966 private void dumpLocked(IndentingPrintWriter pw) { 967 pw.println("Session " + sessionId + ":"); 968 pw.increaseIndent(); 969 970 pw.printPair("userId", userId); 971 pw.printPair("installerPackageName", installerPackageName); 972 pw.printPair("installerUid", installerUid); 973 pw.printPair("createdMillis", createdMillis); 974 pw.printPair("stageDir", stageDir); 975 pw.printPair("stageCid", stageCid); 976 pw.println(); 977 978 params.dump(pw); 979 980 pw.printPair("mClientProgress", mClientProgress); 981 pw.printPair("mProgress", mProgress); 982 pw.printPair("mSealed", mSealed); 983 pw.printPair("mPermissionsAccepted", mPermissionsAccepted); 984 pw.printPair("mDestroyed", mDestroyed); 985 pw.printPair("mBridges", mBridges.size()); 986 pw.printPair("mFinalStatus", mFinalStatus); 987 pw.printPair("mFinalMessage", mFinalMessage); 988 pw.println(); 989 990 pw.decreaseIndent(); 991 } 992 } 993