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