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