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