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