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