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