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