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