PackageInstallerSession.java revision 4e8628157ad0c8c52e74b720eb0328086272ffda
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.util.ArrayUtils; 76import com.android.internal.util.IndentingPrintWriter; 77import com.android.internal.util.Preconditions; 78import com.android.server.pm.Installer.InstallerException; 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 int flags = PackageParser.PARSE_COLLECT_CERTIFICATES; 707 if ((params.installFlags & PackageManager.INSTALL_EPHEMERAL) != 0) { 708 flags |= PackageParser.PARSE_IS_EPHEMERAL; 709 } 710 apk = PackageParser.parseApkLite(addedFile, flags); 711 } catch (PackageParserException e) { 712 throw PackageManagerException.from(e); 713 } 714 715 if (!stagedSplits.add(apk.splitName)) { 716 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 717 "Split " + apk.splitName + " was defined multiple times"); 718 } 719 720 // Use first package to define unknown values 721 if (mPackageName == null) { 722 mPackageName = apk.packageName; 723 mVersionCode = apk.versionCode; 724 } 725 if (mSignatures == null) { 726 mSignatures = apk.signatures; 727 mCertificates = apk.certificates; 728 } 729 730 assertApkConsistent(String.valueOf(addedFile), apk); 731 732 // Take this opportunity to enforce uniform naming 733 final String targetName; 734 if (apk.splitName == null) { 735 targetName = "base.apk"; 736 } else { 737 targetName = "split_" + apk.splitName + ".apk"; 738 } 739 if (!FileUtils.isValidExtFilename(targetName)) { 740 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 741 "Invalid filename: " + targetName); 742 } 743 744 final File targetFile = new File(mResolvedStageDir, targetName); 745 if (!addedFile.equals(targetFile)) { 746 addedFile.renameTo(targetFile); 747 } 748 749 // Base is coming from session 750 if (apk.splitName == null) { 751 mResolvedBaseFile = targetFile; 752 } 753 754 mResolvedStagedFiles.add(targetFile); 755 } 756 757 if (removeSplitList.size() > 0) { 758 // validate split names marked for removal 759 for (String splitName : removeSplitList) { 760 if (!ArrayUtils.contains(pkgInfo.splitNames, splitName)) { 761 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 762 "Split not found: " + splitName); 763 } 764 } 765 766 // ensure we've got appropriate package name, version code and signatures 767 if (mPackageName == null) { 768 mPackageName = pkgInfo.packageName; 769 mVersionCode = pkgInfo.versionCode; 770 } 771 if (mSignatures == null) { 772 mSignatures = pkgInfo.signatures; 773 } 774 } 775 776 if (params.mode == SessionParams.MODE_FULL_INSTALL) { 777 // Full installs must include a base package 778 if (!stagedSplits.contains(null)) { 779 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 780 "Full install must include a base package"); 781 } 782 783 } else { 784 // Partial installs must be consistent with existing install 785 if (appInfo == null) { 786 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 787 "Missing existing base package for " + mPackageName); 788 } 789 790 final PackageLite existing; 791 final ApkLite existingBase; 792 try { 793 existing = PackageParser.parsePackageLite(new File(appInfo.getCodePath()), 0); 794 existingBase = PackageParser.parseApkLite(new File(appInfo.getBaseCodePath()), 795 PackageParser.PARSE_COLLECT_CERTIFICATES); 796 } catch (PackageParserException e) { 797 throw PackageManagerException.from(e); 798 } 799 800 assertApkConsistent("Existing base", existingBase); 801 802 // Inherit base if not overridden 803 if (mResolvedBaseFile == null) { 804 mResolvedBaseFile = new File(appInfo.getBaseCodePath()); 805 mResolvedInheritedFiles.add(mResolvedBaseFile); 806 } 807 808 // Inherit splits if not overridden 809 if (!ArrayUtils.isEmpty(existing.splitNames)) { 810 for (int i = 0; i < existing.splitNames.length; i++) { 811 final String splitName = existing.splitNames[i]; 812 final File splitFile = new File(existing.splitCodePaths[i]); 813 final boolean splitRemoved = removeSplitList.contains(splitName); 814 if (!stagedSplits.contains(splitName) && !splitRemoved) { 815 mResolvedInheritedFiles.add(splitFile); 816 } 817 } 818 } 819 820 // Inherit compiled oat directory. 821 final File packageInstallDir = (new File(appInfo.getBaseCodePath())).getParentFile(); 822 mInheritedFilesBase = packageInstallDir; 823 final File oatDir = new File(packageInstallDir, "oat"); 824 if (oatDir.exists()) { 825 final File[] archSubdirs = oatDir.listFiles(); 826 827 // Keep track of all instruction sets we've seen compiled output for. 828 // If we're linking (and not copying) inherited files, we can recreate the 829 // instruction set hierarchy and link compiled output. 830 if (archSubdirs != null && archSubdirs.length > 0) { 831 final String[] instructionSets = InstructionSets.getAllDexCodeInstructionSets(); 832 for (File archSubDir : archSubdirs) { 833 // Skip any directory that isn't an ISA subdir. 834 if (!ArrayUtils.contains(instructionSets, archSubDir.getName())) { 835 continue; 836 } 837 838 mResolvedInstructionSets.add(archSubDir.getName()); 839 List<File> oatFiles = Arrays.asList(archSubDir.listFiles()); 840 if (!oatFiles.isEmpty()) { 841 mResolvedInheritedFiles.addAll(oatFiles); 842 } 843 } 844 } 845 } 846 } 847 } 848 849 private void assertApkConsistent(String tag, ApkLite apk) 850 throws PackageManagerException { 851 if (!mPackageName.equals(apk.packageName)) { 852 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag + " package " 853 + apk.packageName + " inconsistent with " + mPackageName); 854 } 855 if (params.appPackageName != null && !params.appPackageName.equals(apk.packageName)) { 856 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag 857 + " specified package " + params.appPackageName 858 + " inconsistent with " + apk.packageName); 859 } 860 if (mVersionCode != apk.versionCode) { 861 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag 862 + " version code " + apk.versionCode + " inconsistent with " 863 + mVersionCode); 864 } 865 if (!Signature.areExactMatch(mSignatures, apk.signatures)) { 866 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 867 tag + " signatures are inconsistent"); 868 } 869 } 870 871 /** 872 * Calculate the final install footprint size, combining both staged and 873 * existing APKs together and including unpacked native code from both. 874 */ 875 private long calculateInstalledSize() throws PackageManagerException { 876 Preconditions.checkNotNull(mResolvedBaseFile); 877 878 final ApkLite baseApk; 879 try { 880 baseApk = PackageParser.parseApkLite(mResolvedBaseFile, 0); 881 } catch (PackageParserException e) { 882 throw PackageManagerException.from(e); 883 } 884 885 final List<String> splitPaths = new ArrayList<>(); 886 for (File file : mResolvedStagedFiles) { 887 if (mResolvedBaseFile.equals(file)) continue; 888 splitPaths.add(file.getAbsolutePath()); 889 } 890 for (File file : mResolvedInheritedFiles) { 891 if (mResolvedBaseFile.equals(file)) continue; 892 splitPaths.add(file.getAbsolutePath()); 893 } 894 895 // This is kind of hacky; we're creating a half-parsed package that is 896 // straddled between the inherited and staged APKs. 897 final PackageLite pkg = new PackageLite(null, baseApk, null, null, 898 splitPaths.toArray(new String[splitPaths.size()]), null); 899 final boolean isForwardLocked = 900 (params.installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0; 901 902 try { 903 return PackageHelper.calculateInstalledSize(pkg, isForwardLocked, params.abiOverride); 904 } catch (IOException e) { 905 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 906 "Failed to calculate install size", e); 907 } 908 } 909 910 /** 911 * Determine if creating hard links between source and destination is 912 * possible. That is, do they all live on the same underlying device. 913 */ 914 private boolean isLinkPossible(List<File> fromFiles, File toDir) { 915 try { 916 final StructStat toStat = Os.stat(toDir.getAbsolutePath()); 917 for (File fromFile : fromFiles) { 918 final StructStat fromStat = Os.stat(fromFile.getAbsolutePath()); 919 if (fromStat.st_dev != toStat.st_dev) { 920 return false; 921 } 922 } 923 } catch (ErrnoException e) { 924 Slog.w(TAG, "Failed to detect if linking possible: " + e); 925 return false; 926 } 927 return true; 928 } 929 930 private static String getRelativePath(File file, File base) throws IOException { 931 final String pathStr = file.getAbsolutePath(); 932 final String baseStr = base.getAbsolutePath(); 933 // Don't allow relative paths. 934 if (pathStr.contains("/.") ) { 935 throw new IOException("Invalid path (was relative) : " + pathStr); 936 } 937 938 if (pathStr.startsWith(baseStr)) { 939 return pathStr.substring(baseStr.length()); 940 } 941 942 throw new IOException("File: " + pathStr + " outside base: " + baseStr); 943 } 944 945 private void createOatDirs(List<String> instructionSets, File fromDir) 946 throws PackageManagerException { 947 for (String instructionSet : instructionSets) { 948 try { 949 mPm.mInstaller.createOatDir(fromDir.getAbsolutePath(), instructionSet); 950 } catch (InstallerException e) { 951 throw PackageManagerException.from(e); 952 } 953 } 954 } 955 956 private void linkFiles(List<File> fromFiles, File toDir, File fromDir) 957 throws IOException { 958 for (File fromFile : fromFiles) { 959 final String relativePath = getRelativePath(fromFile, fromDir); 960 try { 961 mPm.mInstaller.linkFile(relativePath, fromDir.getAbsolutePath(), 962 toDir.getAbsolutePath()); 963 } catch (InstallerException e) { 964 throw new IOException("failed linkOrCreateDir(" + relativePath + ", " 965 + fromDir + ", " + toDir + ")", e); 966 } 967 } 968 969 Slog.d(TAG, "Linked " + fromFiles.size() + " files into " + toDir); 970 } 971 972 private static void copyFiles(List<File> fromFiles, File toDir) throws IOException { 973 // Remove any partial files from previous attempt 974 for (File file : toDir.listFiles()) { 975 if (file.getName().endsWith(".tmp")) { 976 file.delete(); 977 } 978 } 979 980 for (File fromFile : fromFiles) { 981 final File tmpFile = File.createTempFile("inherit", ".tmp", toDir); 982 if (LOGD) Slog.d(TAG, "Copying " + fromFile + " to " + tmpFile); 983 if (!FileUtils.copyFile(fromFile, tmpFile)) { 984 throw new IOException("Failed to copy " + fromFile + " to " + tmpFile); 985 } 986 try { 987 Os.chmod(tmpFile.getAbsolutePath(), 0644); 988 } catch (ErrnoException e) { 989 throw new IOException("Failed to chmod " + tmpFile); 990 } 991 final File toFile = new File(toDir, fromFile.getName()); 992 if (LOGD) Slog.d(TAG, "Renaming " + tmpFile + " to " + toFile); 993 if (!tmpFile.renameTo(toFile)) { 994 throw new IOException("Failed to rename " + tmpFile + " to " + toFile); 995 } 996 } 997 Slog.d(TAG, "Copied " + fromFiles.size() + " files into " + toDir); 998 } 999 1000 private static void extractNativeLibraries(File packageDir, String abiOverride) 1001 throws PackageManagerException { 1002 // Always start from a clean slate 1003 final File libDir = new File(packageDir, NativeLibraryHelper.LIB_DIR_NAME); 1004 NativeLibraryHelper.removeNativeBinariesFromDirLI(libDir, true); 1005 1006 NativeLibraryHelper.Handle handle = null; 1007 try { 1008 handle = NativeLibraryHelper.Handle.create(packageDir); 1009 final int res = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libDir, 1010 abiOverride); 1011 if (res != PackageManager.INSTALL_SUCCEEDED) { 1012 throw new PackageManagerException(res, 1013 "Failed to extract native libraries, res=" + res); 1014 } 1015 } catch (IOException e) { 1016 throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, 1017 "Failed to extract native libraries", e); 1018 } finally { 1019 IoUtils.closeQuietly(handle); 1020 } 1021 } 1022 1023 private static void resizeContainer(String cid, long targetSize) 1024 throws PackageManagerException { 1025 String path = PackageHelper.getSdDir(cid); 1026 if (path == null) { 1027 throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR, 1028 "Failed to find mounted " + cid); 1029 } 1030 1031 final long currentSize = new File(path).getTotalSpace(); 1032 if (currentSize > targetSize) { 1033 Slog.w(TAG, "Current size " + currentSize + " is larger than target size " 1034 + targetSize + "; skipping resize"); 1035 return; 1036 } 1037 1038 if (!PackageHelper.unMountSdDir(cid)) { 1039 throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR, 1040 "Failed to unmount " + cid + " before resize"); 1041 } 1042 1043 if (!PackageHelper.resizeSdDir(targetSize, cid, 1044 PackageManagerService.getEncryptKey())) { 1045 throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR, 1046 "Failed to resize " + cid + " to " + targetSize + " bytes"); 1047 } 1048 1049 path = PackageHelper.mountSdDir(cid, PackageManagerService.getEncryptKey(), 1050 Process.SYSTEM_UID, false); 1051 if (path == null) { 1052 throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR, 1053 "Failed to mount " + cid + " after resize"); 1054 } 1055 } 1056 1057 private void finalizeAndFixContainer(String cid) throws PackageManagerException { 1058 if (!PackageHelper.finalizeSdDir(cid)) { 1059 throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR, 1060 "Failed to finalize container " + cid); 1061 } 1062 1063 if (!PackageHelper.fixSdPermissions(cid, defaultContainerGid, null)) { 1064 throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR, 1065 "Failed to fix permissions on container " + cid); 1066 } 1067 } 1068 1069 void setPermissionsResult(boolean accepted) { 1070 if (!mSealed) { 1071 throw new SecurityException("Must be sealed to accept permissions"); 1072 } 1073 1074 if (accepted) { 1075 // Mark and kick off another install pass 1076 synchronized (mLock) { 1077 mPermissionsAccepted = true; 1078 } 1079 mHandler.obtainMessage(MSG_COMMIT).sendToTarget(); 1080 } else { 1081 destroyInternal(); 1082 dispatchSessionFinished(INSTALL_FAILED_ABORTED, "User rejected permissions", null); 1083 } 1084 } 1085 1086 public void open() throws IOException { 1087 if (mActiveCount.getAndIncrement() == 0) { 1088 mCallback.onSessionActiveChanged(this, true); 1089 } 1090 1091 synchronized (mLock) { 1092 if (!mPrepared) { 1093 if (stageDir != null) { 1094 prepareStageDir(stageDir); 1095 } else if (stageCid != null) { 1096 final long identity = Binder.clearCallingIdentity(); 1097 try { 1098 prepareExternalStageCid(stageCid, params.sizeBytes); 1099 } finally { 1100 Binder.restoreCallingIdentity(identity); 1101 } 1102 1103 // TODO: deliver more granular progress for ASEC allocation 1104 mInternalProgress = 0.25f; 1105 computeProgressLocked(true); 1106 } else { 1107 throw new IllegalArgumentException( 1108 "Exactly one of stageDir or stageCid stage must be set"); 1109 } 1110 1111 mPrepared = true; 1112 mCallback.onSessionPrepared(this); 1113 } 1114 } 1115 } 1116 1117 @Override 1118 public void close() { 1119 if (mActiveCount.decrementAndGet() == 0) { 1120 mCallback.onSessionActiveChanged(this, false); 1121 } 1122 } 1123 1124 @Override 1125 public void abandon() { 1126 if (mRelinquished) { 1127 Slog.d(TAG, "Ignoring abandon after commit relinquished control"); 1128 return; 1129 } 1130 destroyInternal(); 1131 dispatchSessionFinished(INSTALL_FAILED_ABORTED, "Session was abandoned", null); 1132 } 1133 1134 private void dispatchSessionFinished(int returnCode, String msg, Bundle extras) { 1135 mFinalStatus = returnCode; 1136 mFinalMessage = msg; 1137 1138 if (mRemoteObserver != null) { 1139 try { 1140 mRemoteObserver.onPackageInstalled(mPackageName, returnCode, msg, extras); 1141 } catch (RemoteException ignored) { 1142 } 1143 } 1144 1145 final boolean success = (returnCode == PackageManager.INSTALL_SUCCEEDED); 1146 mCallback.onSessionFinished(this, success); 1147 } 1148 1149 private void destroyInternal() { 1150 synchronized (mLock) { 1151 mSealed = true; 1152 mDestroyed = true; 1153 1154 // Force shut down all bridges 1155 for (FileBridge bridge : mBridges) { 1156 bridge.forceClose(); 1157 } 1158 } 1159 if (stageDir != null) { 1160 try { 1161 mPm.mInstaller.rmPackageDir(stageDir.getAbsolutePath()); 1162 } catch (InstallerException ignored) { 1163 } 1164 } 1165 if (stageCid != null) { 1166 PackageHelper.destroySdDir(stageCid); 1167 } 1168 } 1169 1170 void dump(IndentingPrintWriter pw) { 1171 synchronized (mLock) { 1172 dumpLocked(pw); 1173 } 1174 } 1175 1176 private void dumpLocked(IndentingPrintWriter pw) { 1177 pw.println("Session " + sessionId + ":"); 1178 pw.increaseIndent(); 1179 1180 pw.printPair("userId", userId); 1181 pw.printPair("installerPackageName", installerPackageName); 1182 pw.printPair("installerUid", installerUid); 1183 pw.printPair("createdMillis", createdMillis); 1184 pw.printPair("stageDir", stageDir); 1185 pw.printPair("stageCid", stageCid); 1186 pw.println(); 1187 1188 params.dump(pw); 1189 1190 pw.printPair("mClientProgress", mClientProgress); 1191 pw.printPair("mProgress", mProgress); 1192 pw.printPair("mSealed", mSealed); 1193 pw.printPair("mPermissionsAccepted", mPermissionsAccepted); 1194 pw.printPair("mRelinquished", mRelinquished); 1195 pw.printPair("mDestroyed", mDestroyed); 1196 pw.printPair("mBridges", mBridges.size()); 1197 pw.printPair("mFinalStatus", mFinalStatus); 1198 pw.printPair("mFinalMessage", mFinalMessage); 1199 pw.println(); 1200 1201 pw.decreaseIndent(); 1202 } 1203} 1204