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