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