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