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