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