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