1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.pm; 18 19 import static android.content.pm.PackageManager.INSTALL_FAILED_ABORTED; 20 import static android.content.pm.PackageManager.INSTALL_FAILED_CONTAINER_ERROR; 21 import static android.content.pm.PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; 22 import static android.content.pm.PackageManager.INSTALL_FAILED_INTERNAL_ERROR; 23 import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK; 24 import static android.system.OsConstants.O_CREAT; 25 import static android.system.OsConstants.O_RDONLY; 26 import static android.system.OsConstants.O_WRONLY; 27 28 import static com.android.internal.util.XmlUtils.readBitmapAttribute; 29 import static com.android.internal.util.XmlUtils.readBooleanAttribute; 30 import static com.android.internal.util.XmlUtils.readIntAttribute; 31 import static com.android.internal.util.XmlUtils.readLongAttribute; 32 import static com.android.internal.util.XmlUtils.readStringAttribute; 33 import static com.android.internal.util.XmlUtils.readUriAttribute; 34 import static com.android.internal.util.XmlUtils.writeBooleanAttribute; 35 import static com.android.internal.util.XmlUtils.writeIntAttribute; 36 import static com.android.internal.util.XmlUtils.writeLongAttribute; 37 import static com.android.internal.util.XmlUtils.writeStringAttribute; 38 import static com.android.internal.util.XmlUtils.writeUriAttribute; 39 import static com.android.server.pm.PackageInstallerService.prepareExternalStageCid; 40 import static com.android.server.pm.PackageInstallerService.prepareStageDir; 41 42 import android.Manifest; 43 import android.annotation.NonNull; 44 import android.annotation.Nullable; 45 import android.app.admin.DevicePolicyManager; 46 import android.content.Context; 47 import android.content.Intent; 48 import android.content.IntentSender; 49 import android.content.pm.ApplicationInfo; 50 import android.content.pm.IPackageInstallObserver2; 51 import android.content.pm.IPackageInstallerSession; 52 import android.content.pm.PackageInfo; 53 import android.content.pm.PackageInstaller; 54 import android.content.pm.PackageInstaller.SessionInfo; 55 import android.content.pm.PackageInstaller.SessionParams; 56 import android.content.pm.PackageManager; 57 import android.content.pm.PackageParser; 58 import android.content.pm.PackageParser.ApkLite; 59 import android.content.pm.PackageParser.PackageLite; 60 import android.content.pm.PackageParser.PackageParserException; 61 import android.content.pm.Signature; 62 import android.graphics.Bitmap; 63 import android.graphics.BitmapFactory; 64 import android.os.Binder; 65 import android.os.Bundle; 66 import android.os.FileBridge; 67 import android.os.FileUtils; 68 import android.os.Handler; 69 import android.os.Looper; 70 import android.os.Message; 71 import android.os.ParcelFileDescriptor; 72 import android.os.ParcelableException; 73 import android.os.Process; 74 import android.os.RemoteException; 75 import android.os.RevocableFileDescriptor; 76 import android.os.UserHandle; 77 import android.os.storage.StorageManager; 78 import android.system.ErrnoException; 79 import android.system.Os; 80 import android.system.OsConstants; 81 import android.system.StructStat; 82 import android.text.TextUtils; 83 import android.util.ArraySet; 84 import android.util.ExceptionUtils; 85 import android.util.MathUtils; 86 import android.util.Slog; 87 88 import com.android.internal.annotations.GuardedBy; 89 import com.android.internal.content.NativeLibraryHelper; 90 import com.android.internal.content.PackageHelper; 91 import com.android.internal.util.ArrayUtils; 92 import com.android.internal.util.IndentingPrintWriter; 93 import com.android.internal.util.Preconditions; 94 import com.android.server.pm.Installer.InstallerException; 95 import com.android.server.pm.PackageInstallerService.PackageInstallObserverAdapter; 96 97 import libcore.io.IoUtils; 98 import libcore.io.Libcore; 99 100 import org.xmlpull.v1.XmlPullParser; 101 import org.xmlpull.v1.XmlPullParserException; 102 import org.xmlpull.v1.XmlSerializer; 103 104 import java.io.File; 105 import java.io.FileDescriptor; 106 import java.io.FileFilter; 107 import java.io.FileOutputStream; 108 import java.io.IOException; 109 import java.security.cert.Certificate; 110 import java.util.ArrayList; 111 import java.util.Arrays; 112 import java.util.List; 113 import java.util.concurrent.atomic.AtomicInteger; 114 115 public class PackageInstallerSession extends IPackageInstallerSession.Stub { 116 private static final String TAG = "PackageInstaller"; 117 private static final boolean LOGD = true; 118 private static final String REMOVE_SPLIT_MARKER_EXTENSION = ".removed"; 119 120 private static final int MSG_COMMIT = 0; 121 private static final int MSG_SESSION_FINISHED_WITH_EXCEPTION = 1; 122 123 /** XML constants used for persisting a session */ 124 static final String TAG_SESSION = "session"; 125 private static final String TAG_GRANTED_RUNTIME_PERMISSION = "granted-runtime-permission"; 126 private static final String ATTR_SESSION_ID = "sessionId"; 127 private static final String ATTR_USER_ID = "userId"; 128 private static final String ATTR_INSTALLER_PACKAGE_NAME = "installerPackageName"; 129 private static final String ATTR_INSTALLER_UID = "installerUid"; 130 private static final String ATTR_CREATED_MILLIS = "createdMillis"; 131 private static final String ATTR_SESSION_STAGE_DIR = "sessionStageDir"; 132 private static final String ATTR_SESSION_STAGE_CID = "sessionStageCid"; 133 private static final String ATTR_PREPARED = "prepared"; 134 private static final String ATTR_SEALED = "sealed"; 135 private static final String ATTR_MODE = "mode"; 136 private static final String ATTR_INSTALL_FLAGS = "installFlags"; 137 private static final String ATTR_INSTALL_LOCATION = "installLocation"; 138 private static final String ATTR_SIZE_BYTES = "sizeBytes"; 139 private static final String ATTR_APP_PACKAGE_NAME = "appPackageName"; 140 @Deprecated 141 private static final String ATTR_APP_ICON = "appIcon"; 142 private static final String ATTR_APP_LABEL = "appLabel"; 143 private static final String ATTR_ORIGINATING_URI = "originatingUri"; 144 private static final String ATTR_ORIGINATING_UID = "originatingUid"; 145 private static final String ATTR_REFERRER_URI = "referrerUri"; 146 private static final String ATTR_ABI_OVERRIDE = "abiOverride"; 147 private static final String ATTR_VOLUME_UUID = "volumeUuid"; 148 private static final String ATTR_NAME = "name"; 149 private static final String ATTR_INSTALL_REASON = "installRason"; 150 151 // TODO: enforce INSTALL_ALLOW_TEST 152 // TODO: enforce INSTALL_ALLOW_DOWNGRADE 153 154 private final PackageInstallerService.InternalCallback mCallback; 155 private final Context mContext; 156 private final PackageManagerService mPm; 157 private final Handler mHandler; 158 159 final int sessionId; 160 final int userId; 161 final SessionParams params; 162 final long createdMillis; 163 final int defaultContainerGid; 164 165 /** Staging location where client data is written. */ 166 final File stageDir; 167 final String stageCid; 168 169 private final AtomicInteger mActiveCount = new AtomicInteger(); 170 171 private final Object mLock = new Object(); 172 173 /** Uid of the creator of this session. */ 174 private final int mOriginalInstallerUid; 175 176 /** Package of the owner of the installer session */ 177 @GuardedBy("mLock") 178 private String mInstallerPackageName; 179 180 /** Uid of the owner of the installer session */ 181 @GuardedBy("mLock") 182 private int mInstallerUid; 183 184 @GuardedBy("mLock") 185 private float mClientProgress = 0; 186 @GuardedBy("mLock") 187 private float mInternalProgress = 0; 188 189 @GuardedBy("mLock") 190 private float mProgress = 0; 191 @GuardedBy("mLock") 192 private float mReportedProgress = -1; 193 194 /** State of the session. */ 195 @GuardedBy("mLock") 196 private boolean mPrepared = false; 197 @GuardedBy("mLock") 198 private boolean mSealed = false; 199 @GuardedBy("mLock") 200 private boolean mCommitted = false; 201 @GuardedBy("mLock") 202 private boolean mRelinquished = false; 203 @GuardedBy("mLock") 204 private boolean mDestroyed = false; 205 206 /** Permissions have been accepted by the user (see {@link #setPermissionsResult}) */ 207 @GuardedBy("mLock") 208 private boolean mPermissionsManuallyAccepted = false; 209 210 @GuardedBy("mLock") 211 private int mFinalStatus; 212 @GuardedBy("mLock") 213 private String mFinalMessage; 214 215 @GuardedBy("mLock") 216 private final ArrayList<RevocableFileDescriptor> mFds = new ArrayList<>(); 217 @GuardedBy("mLock") 218 private final ArrayList<FileBridge> mBridges = new ArrayList<>(); 219 220 @GuardedBy("mLock") 221 private IPackageInstallObserver2 mRemoteObserver; 222 223 /** Fields derived from commit parsing */ 224 @GuardedBy("mLock") 225 private String mPackageName; 226 @GuardedBy("mLock") 227 private int mVersionCode; 228 @GuardedBy("mLock") 229 private Signature[] mSignatures; 230 @GuardedBy("mLock") 231 private Certificate[][] mCertificates; 232 233 /** 234 * Path to the validated base APK for this session, which may point at an 235 * APK inside the session (when the session defines the base), or it may 236 * point at the existing base APK (when adding splits to an existing app). 237 * <p> 238 * This is used when confirming permissions, since we can't fully stage the 239 * session inside an ASEC before confirming with user. 240 */ 241 @GuardedBy("mLock") 242 private File mResolvedBaseFile; 243 244 @GuardedBy("mLock") 245 private File mResolvedStageDir; 246 247 @GuardedBy("mLock") 248 private final List<File> mResolvedStagedFiles = new ArrayList<>(); 249 @GuardedBy("mLock") 250 private final List<File> mResolvedInheritedFiles = new ArrayList<>(); 251 @GuardedBy("mLock") 252 private final List<String> mResolvedInstructionSets = new ArrayList<>(); 253 @GuardedBy("mLock") 254 private File mInheritedFilesBase; 255 256 private static final FileFilter sAddedFilter = new FileFilter() { 257 @Override 258 public boolean accept(File file) { 259 // Installers can't stage directories, so it's fine to ignore 260 // entries like "lost+found". 261 if (file.isDirectory()) return false; 262 if (file.getName().endsWith(REMOVE_SPLIT_MARKER_EXTENSION)) return false; 263 return true; 264 } 265 }; 266 private static final FileFilter sRemovedFilter = new FileFilter() { 267 @Override 268 public boolean accept(File file) { 269 if (file.isDirectory()) return false; 270 if (!file.getName().endsWith(REMOVE_SPLIT_MARKER_EXTENSION)) return false; 271 return true; 272 } 273 }; 274 275 private final Handler.Callback mHandlerCallback = new Handler.Callback() { 276 @Override 277 public boolean handleMessage(Message msg) { 278 switch (msg.what) { 279 case MSG_COMMIT: 280 synchronized (mLock) { 281 try { 282 commitLocked(); 283 } catch (PackageManagerException e) { 284 final String completeMsg = ExceptionUtils.getCompleteMessage(e); 285 Slog.e(TAG, 286 "Commit of session " + sessionId + " failed: " + completeMsg); 287 destroyInternal(); 288 dispatchSessionFinished(e.error, completeMsg, null); 289 } 290 } 291 292 break; 293 case MSG_SESSION_FINISHED_WITH_EXCEPTION: 294 PackageManagerException e = (PackageManagerException) msg.obj; 295 296 dispatchSessionFinished(e.error, ExceptionUtils.getCompleteMessage(e), 297 null); 298 break; 299 } 300 301 return true; 302 } 303 }; 304 305 /** 306 * @return {@code true} iff the installing is app an device owner? 307 */ 308 private boolean isInstallerDeviceOwnerLocked() { 309 DevicePolicyManager dpm = (DevicePolicyManager) mContext.getSystemService( 310 Context.DEVICE_POLICY_SERVICE); 311 312 return (dpm != null) && dpm.isDeviceOwnerAppOnCallingUser( 313 mInstallerPackageName); 314 } 315 316 /** 317 * Checks if the permissions still need to be confirmed. 318 * 319 * <p>This is dependant on the identity of the installer, hence this cannot be cached if the 320 * installer might still {@link #transfer(String) change}. 321 * 322 * @return {@code true} iff we need to ask to confirm the permissions? 323 */ 324 private boolean needToAskForPermissionsLocked() { 325 if (mPermissionsManuallyAccepted) { 326 return false; 327 } 328 329 final boolean isPermissionGranted = 330 (mPm.checkUidPermission(android.Manifest.permission.INSTALL_PACKAGES, 331 mInstallerUid) == PackageManager.PERMISSION_GRANTED); 332 final boolean isInstallerRoot = (mInstallerUid == Process.ROOT_UID); 333 final boolean forcePermissionPrompt = 334 (params.installFlags & PackageManager.INSTALL_FORCE_PERMISSION_PROMPT) != 0; 335 336 // Device owners are allowed to silently install packages, so the permission check is 337 // waived if the installer is the device owner. 338 return forcePermissionPrompt || !(isPermissionGranted || isInstallerRoot 339 || isInstallerDeviceOwnerLocked()); 340 } 341 342 public PackageInstallerSession(PackageInstallerService.InternalCallback callback, 343 Context context, PackageManagerService pm, Looper looper, int sessionId, int userId, 344 String installerPackageName, int installerUid, SessionParams params, long createdMillis, 345 File stageDir, String stageCid, boolean prepared, boolean sealed) { 346 mCallback = callback; 347 mContext = context; 348 mPm = pm; 349 mHandler = new Handler(looper, mHandlerCallback); 350 351 this.sessionId = sessionId; 352 this.userId = userId; 353 mOriginalInstallerUid = installerUid; 354 mInstallerPackageName = installerPackageName; 355 mInstallerUid = installerUid; 356 this.params = params; 357 this.createdMillis = createdMillis; 358 this.stageDir = stageDir; 359 this.stageCid = stageCid; 360 361 if ((stageDir == null) == (stageCid == null)) { 362 throw new IllegalArgumentException( 363 "Exactly one of stageDir or stageCid stage must be set"); 364 } 365 366 mPrepared = prepared; 367 368 if (sealed) { 369 synchronized (mLock) { 370 try { 371 sealAndValidateLocked(); 372 } catch (PackageManagerException | IOException e) { 373 destroyInternal(); 374 throw new IllegalArgumentException(e); 375 } 376 } 377 } 378 379 final long identity = Binder.clearCallingIdentity(); 380 try { 381 final int uid = mPm.getPackageUid(PackageManagerService.DEFAULT_CONTAINER_PACKAGE, 382 PackageManager.MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM); 383 defaultContainerGid = UserHandle.getSharedAppGid(uid); 384 } finally { 385 Binder.restoreCallingIdentity(identity); 386 } 387 } 388 389 public SessionInfo generateInfo() { 390 return generateInfo(true); 391 } 392 393 public SessionInfo generateInfo(boolean includeIcon) { 394 final SessionInfo info = new SessionInfo(); 395 synchronized (mLock) { 396 info.sessionId = sessionId; 397 info.installerPackageName = mInstallerPackageName; 398 info.resolvedBaseCodePath = (mResolvedBaseFile != null) ? 399 mResolvedBaseFile.getAbsolutePath() : null; 400 info.progress = mProgress; 401 info.sealed = mSealed; 402 info.active = mActiveCount.get() > 0; 403 404 info.mode = params.mode; 405 info.installReason = params.installReason; 406 info.sizeBytes = params.sizeBytes; 407 info.appPackageName = params.appPackageName; 408 if (includeIcon) { 409 info.appIcon = params.appIcon; 410 } 411 info.appLabel = params.appLabel; 412 413 info.installLocation = params.installLocation; 414 info.originatingUri = params.originatingUri; 415 info.originatingUid = params.originatingUid; 416 info.referrerUri = params.referrerUri; 417 info.grantedRuntimePermissions = params.grantedRuntimePermissions; 418 info.installFlags = params.installFlags; 419 } 420 return info; 421 } 422 423 public boolean isPrepared() { 424 synchronized (mLock) { 425 return mPrepared; 426 } 427 } 428 429 public boolean isSealed() { 430 synchronized (mLock) { 431 return mSealed; 432 } 433 } 434 435 private void assertPreparedAndNotSealedLocked(String cookie) { 436 assertPreparedAndNotCommittedOrDestroyedLocked(cookie); 437 if (mSealed) { 438 throw new SecurityException(cookie + " not allowed after sealing"); 439 } 440 } 441 442 private void assertPreparedAndNotCommittedOrDestroyedLocked(String cookie) { 443 assertPreparedAndNotDestroyedLocked(cookie); 444 if (mCommitted) { 445 throw new SecurityException(cookie + " not allowed after commit"); 446 } 447 } 448 449 private void assertPreparedAndNotDestroyedLocked(String cookie) { 450 if (!mPrepared) { 451 throw new IllegalStateException(cookie + " before prepared"); 452 } 453 if (mDestroyed) { 454 throw new SecurityException(cookie + " not allowed after destruction"); 455 } 456 } 457 458 /** 459 * Resolve the actual location where staged data should be written. This 460 * might point at an ASEC mount point, which is why we delay path resolution 461 * until someone actively works with the session. 462 */ 463 private File resolveStageDirLocked() throws IOException { 464 if (mResolvedStageDir == null) { 465 if (stageDir != null) { 466 mResolvedStageDir = stageDir; 467 } else { 468 final String path = PackageHelper.getSdDir(stageCid); 469 if (path != null) { 470 mResolvedStageDir = new File(path); 471 } else { 472 throw new IOException("Failed to resolve path to container " + stageCid); 473 } 474 } 475 } 476 return mResolvedStageDir; 477 } 478 479 @Override 480 public void setClientProgress(float progress) { 481 synchronized (mLock) { 482 assertCallerIsOwnerOrRootLocked(); 483 484 // Always publish first staging movement 485 final boolean forcePublish = (mClientProgress == 0); 486 mClientProgress = progress; 487 computeProgressLocked(forcePublish); 488 } 489 } 490 491 @Override 492 public void addClientProgress(float progress) { 493 synchronized (mLock) { 494 assertCallerIsOwnerOrRootLocked(); 495 496 setClientProgress(mClientProgress + progress); 497 } 498 } 499 500 private void computeProgressLocked(boolean forcePublish) { 501 mProgress = MathUtils.constrain(mClientProgress * 0.8f, 0f, 0.8f) 502 + MathUtils.constrain(mInternalProgress * 0.2f, 0f, 0.2f); 503 504 // Only publish when meaningful change 505 if (forcePublish || Math.abs(mProgress - mReportedProgress) >= 0.01) { 506 mReportedProgress = mProgress; 507 mCallback.onSessionProgressChanged(this, mProgress); 508 } 509 } 510 511 @Override 512 public String[] getNames() { 513 synchronized (mLock) { 514 assertCallerIsOwnerOrRootLocked(); 515 assertPreparedAndNotCommittedOrDestroyedLocked("getNames"); 516 517 try { 518 return resolveStageDirLocked().list(); 519 } catch (IOException e) { 520 throw ExceptionUtils.wrap(e); 521 } 522 } 523 } 524 525 @Override 526 public void removeSplit(String splitName) { 527 if (TextUtils.isEmpty(params.appPackageName)) { 528 throw new IllegalStateException("Must specify package name to remove a split"); 529 } 530 531 synchronized (mLock) { 532 assertCallerIsOwnerOrRootLocked(); 533 assertPreparedAndNotCommittedOrDestroyedLocked("removeSplit"); 534 535 try { 536 createRemoveSplitMarkerLocked(splitName); 537 } catch (IOException e) { 538 throw ExceptionUtils.wrap(e); 539 } 540 } 541 } 542 543 private void createRemoveSplitMarkerLocked(String splitName) throws IOException { 544 try { 545 final String markerName = splitName + REMOVE_SPLIT_MARKER_EXTENSION; 546 if (!FileUtils.isValidExtFilename(markerName)) { 547 throw new IllegalArgumentException("Invalid marker: " + markerName); 548 } 549 final File target = new File(resolveStageDirLocked(), markerName); 550 target.createNewFile(); 551 Os.chmod(target.getAbsolutePath(), 0 /*mode*/); 552 } catch (ErrnoException e) { 553 throw e.rethrowAsIOException(); 554 } 555 } 556 557 @Override 558 public ParcelFileDescriptor openWrite(String name, long offsetBytes, long lengthBytes) { 559 try { 560 return openWriteInternal(name, offsetBytes, lengthBytes); 561 } catch (IOException e) { 562 throw ExceptionUtils.wrap(e); 563 } 564 } 565 566 private ParcelFileDescriptor openWriteInternal(String name, long offsetBytes, long lengthBytes) 567 throws IOException { 568 // Quick sanity check of state, and allocate a pipe for ourselves. We 569 // then do heavy disk allocation outside the lock, but this open pipe 570 // will block any attempted install transitions. 571 final RevocableFileDescriptor fd; 572 final FileBridge bridge; 573 final File stageDir; 574 synchronized (mLock) { 575 assertCallerIsOwnerOrRootLocked(); 576 assertPreparedAndNotSealedLocked("openWrite"); 577 578 if (PackageInstaller.ENABLE_REVOCABLE_FD) { 579 fd = new RevocableFileDescriptor(); 580 bridge = null; 581 mFds.add(fd); 582 } else { 583 fd = null; 584 bridge = new FileBridge(); 585 mBridges.add(bridge); 586 } 587 588 stageDir = resolveStageDirLocked(); 589 } 590 591 try { 592 // Use installer provided name for now; we always rename later 593 if (!FileUtils.isValidExtFilename(name)) { 594 throw new IllegalArgumentException("Invalid name: " + name); 595 } 596 final File target; 597 final long identity = Binder.clearCallingIdentity(); 598 try { 599 target = new File(stageDir, name); 600 } finally { 601 Binder.restoreCallingIdentity(identity); 602 } 603 604 // TODO: this should delegate to DCS so the system process avoids 605 // holding open FDs into containers. 606 final FileDescriptor targetFd = Libcore.os.open(target.getAbsolutePath(), 607 O_CREAT | O_WRONLY, 0644); 608 Os.chmod(target.getAbsolutePath(), 0644); 609 610 // If caller specified a total length, allocate it for them. Free up 611 // cache space to grow, if needed. 612 if (stageDir != null && lengthBytes > 0) { 613 mContext.getSystemService(StorageManager.class).allocateBytes(targetFd, lengthBytes, 614 PackageHelper.translateAllocateFlags(params.installFlags)); 615 } 616 617 if (offsetBytes > 0) { 618 Libcore.os.lseek(targetFd, offsetBytes, OsConstants.SEEK_SET); 619 } 620 621 if (PackageInstaller.ENABLE_REVOCABLE_FD) { 622 fd.init(mContext, targetFd); 623 return fd.getRevocableFileDescriptor(); 624 } else { 625 bridge.setTargetFile(targetFd); 626 bridge.start(); 627 return new ParcelFileDescriptor(bridge.getClientSocket()); 628 } 629 630 } catch (ErrnoException e) { 631 throw e.rethrowAsIOException(); 632 } 633 } 634 635 @Override 636 public ParcelFileDescriptor openRead(String name) { 637 synchronized (mLock) { 638 assertCallerIsOwnerOrRootLocked(); 639 assertPreparedAndNotCommittedOrDestroyedLocked("openRead"); 640 try { 641 return openReadInternalLocked(name); 642 } catch (IOException e) { 643 throw ExceptionUtils.wrap(e); 644 } 645 } 646 } 647 648 private ParcelFileDescriptor openReadInternalLocked(String name) throws IOException { 649 try { 650 if (!FileUtils.isValidExtFilename(name)) { 651 throw new IllegalArgumentException("Invalid name: " + name); 652 } 653 final File target = new File(resolveStageDirLocked(), name); 654 final FileDescriptor targetFd = Libcore.os.open(target.getAbsolutePath(), O_RDONLY, 0); 655 return new ParcelFileDescriptor(targetFd); 656 } catch (ErrnoException e) { 657 throw e.rethrowAsIOException(); 658 } 659 } 660 661 /** 662 * Check if the caller is the owner of this session. Otherwise throw a 663 * {@link SecurityException}. 664 */ 665 private void assertCallerIsOwnerOrRootLocked() { 666 final int callingUid = Binder.getCallingUid(); 667 if (callingUid != Process.ROOT_UID && callingUid != mInstallerUid) { 668 throw new SecurityException("Session does not belong to uid " + callingUid); 669 } 670 } 671 672 /** 673 * If anybody is reading or writing data of the session, throw an {@link SecurityException}. 674 */ 675 private void assertNoWriteFileTransfersOpenLocked() { 676 // Verify that all writers are hands-off 677 for (RevocableFileDescriptor fd : mFds) { 678 if (!fd.isRevoked()) { 679 throw new SecurityException("Files still open"); 680 } 681 } 682 for (FileBridge bridge : mBridges) { 683 if (!bridge.isClosed()) { 684 throw new SecurityException("Files still open"); 685 } 686 } 687 } 688 689 @Override 690 public void commit(@NonNull IntentSender statusReceiver, boolean forTransfer) { 691 Preconditions.checkNotNull(statusReceiver); 692 693 final boolean wasSealed; 694 synchronized (mLock) { 695 assertCallerIsOwnerOrRootLocked(); 696 assertPreparedAndNotDestroyedLocked("commit"); 697 698 final PackageInstallObserverAdapter adapter = new PackageInstallObserverAdapter( 699 mContext, statusReceiver, sessionId, isInstallerDeviceOwnerLocked(), userId); 700 mRemoteObserver = adapter.getBinder(); 701 702 if (forTransfer) { 703 mContext.enforceCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES, null); 704 705 if (mInstallerUid == mOriginalInstallerUid) { 706 throw new IllegalArgumentException("Session has not been transferred"); 707 } 708 } else { 709 if (mInstallerUid != mOriginalInstallerUid) { 710 throw new IllegalArgumentException("Session has been transferred"); 711 } 712 } 713 714 wasSealed = mSealed; 715 if (!mSealed) { 716 try { 717 sealAndValidateLocked(); 718 } catch (IOException e) { 719 throw new IllegalArgumentException(e); 720 } catch (PackageManagerException e) { 721 destroyInternal(); 722 723 // Cannot call dispatchFinal synchronous as this might be called from inside the 724 // system server on the main thread. Hence the call back scheduled in 725 // dispachFinal has to be scheduled on a different thread. 726 mHandler.obtainMessage(MSG_SESSION_FINISHED_WITH_EXCEPTION, e).sendToTarget(); 727 728 return; 729 } 730 } 731 732 // Client staging is fully done at this point 733 mClientProgress = 1f; 734 computeProgressLocked(true); 735 736 // This ongoing commit should keep session active, even though client 737 // will probably close their end. 738 mActiveCount.incrementAndGet(); 739 740 mCommitted = true; 741 mHandler.obtainMessage(MSG_COMMIT).sendToTarget(); 742 } 743 744 if (!wasSealed) { 745 // Persist the fact that we've sealed ourselves to prevent 746 // mutations of any hard links we create. We do this without holding 747 // the session lock, since otherwise it's a lock inversion. 748 mCallback.onSessionSealedBlocking(this); 749 } 750 } 751 752 /** 753 * Seal the session to prevent further modification and validate the contents of it. 754 * 755 * <p>The session will be sealed after calling this method even if it failed. 756 * 757 * @throws PackageManagerException if the session was sealed but something went wrong. If the 758 * session was sealed this is the only possible exception. 759 */ 760 private void sealAndValidateLocked() throws PackageManagerException, IOException { 761 assertNoWriteFileTransfersOpenLocked(); 762 assertPreparedAndNotDestroyedLocked("sealing of session"); 763 764 final PackageInfo pkgInfo = mPm.getPackageInfo( 765 params.appPackageName, PackageManager.GET_SIGNATURES 766 | PackageManager.MATCH_STATIC_SHARED_LIBRARIES /*flags*/, userId); 767 768 resolveStageDirLocked(); 769 770 mSealed = true; 771 772 // Verify that stage looks sane with respect to existing application. 773 // This currently only ensures packageName, versionCode, and certificate 774 // consistency. 775 try { 776 validateInstallLocked(pkgInfo); 777 } catch (PackageManagerException e) { 778 throw e; 779 } catch (Throwable e) { 780 // Convert all exceptions into package manager exceptions as only those are handled 781 // in the code above 782 throw new PackageManagerException(e); 783 } 784 785 // Read transfers from the original owner stay open, but as the session's data 786 // cannot be modified anymore, there is no leak of information. 787 } 788 789 @Override 790 public void transfer(String packageName) { 791 Preconditions.checkNotNull(packageName); 792 793 ApplicationInfo newOwnerAppInfo = mPm.getApplicationInfo(packageName, 0, userId); 794 if (newOwnerAppInfo == null) { 795 throw new ParcelableException(new PackageManager.NameNotFoundException(packageName)); 796 } 797 798 if (PackageManager.PERMISSION_GRANTED != mPm.checkUidPermission( 799 Manifest.permission.INSTALL_PACKAGES, newOwnerAppInfo.uid)) { 800 throw new SecurityException("Destination package " + packageName + " does not have " 801 + "the " + Manifest.permission.INSTALL_PACKAGES + " permission"); 802 } 803 804 // Only install flags that can be verified by the app the session is transferred to are 805 // allowed. The parameters can be read via PackageInstaller.SessionInfo. 806 if (!params.areHiddenOptionsSet()) { 807 throw new SecurityException("Can only transfer sessions that use public options"); 808 } 809 810 synchronized (mLock) { 811 assertCallerIsOwnerOrRootLocked(); 812 assertPreparedAndNotSealedLocked("transfer"); 813 814 try { 815 sealAndValidateLocked(); 816 } catch (IOException e) { 817 throw new IllegalStateException(e); 818 } catch (PackageManagerException e) { 819 // Session is sealed but could not be verified, we need to destroy it 820 destroyInternal(); 821 dispatchSessionFinished(e.error, ExceptionUtils.getCompleteMessage(e), null); 822 823 throw new IllegalArgumentException("Package is not valid", e); 824 } 825 826 if (!mPackageName.equals(mInstallerPackageName)) { 827 throw new SecurityException("Can only transfer sessions that update the original " 828 + "installer"); 829 } 830 831 mInstallerPackageName = packageName; 832 mInstallerUid = newOwnerAppInfo.uid; 833 } 834 835 // Persist the fact that we've sealed ourselves to prevent 836 // mutations of any hard links we create. We do this without holding 837 // the session lock, since otherwise it's a lock inversion. 838 mCallback.onSessionSealedBlocking(this); 839 } 840 841 private void commitLocked() 842 throws PackageManagerException { 843 if (mDestroyed) { 844 throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session destroyed"); 845 } 846 if (!mSealed) { 847 throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session not sealed"); 848 } 849 850 Preconditions.checkNotNull(mPackageName); 851 Preconditions.checkNotNull(mSignatures); 852 Preconditions.checkNotNull(mResolvedBaseFile); 853 854 if (needToAskForPermissionsLocked()) { 855 // User needs to accept permissions; give installer an intent they 856 // can use to involve user. 857 final Intent intent = new Intent(PackageInstaller.ACTION_CONFIRM_PERMISSIONS); 858 intent.setPackage(mContext.getPackageManager().getPermissionControllerPackageName()); 859 intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId); 860 try { 861 mRemoteObserver.onUserActionRequired(intent); 862 } catch (RemoteException ignored) { 863 } 864 865 // Commit was keeping session marked as active until now; release 866 // that extra refcount so session appears idle. 867 closeInternal(false); 868 return; 869 } 870 871 if (stageCid != null) { 872 // Figure out the final installed size and resize the container once 873 // and for all. Internally the parser handles straddling between two 874 // locations when inheriting. 875 final long finalSize = calculateInstalledSize(); 876 resizeContainer(stageCid, finalSize); 877 } 878 879 // Inherit any packages and native libraries from existing install that 880 // haven't been overridden. 881 if (params.mode == SessionParams.MODE_INHERIT_EXISTING) { 882 try { 883 final List<File> fromFiles = mResolvedInheritedFiles; 884 final File toDir = resolveStageDirLocked(); 885 886 if (LOGD) Slog.d(TAG, "Inherited files: " + mResolvedInheritedFiles); 887 if (!mResolvedInheritedFiles.isEmpty() && mInheritedFilesBase == null) { 888 throw new IllegalStateException("mInheritedFilesBase == null"); 889 } 890 891 if (isLinkPossible(fromFiles, toDir)) { 892 if (!mResolvedInstructionSets.isEmpty()) { 893 final File oatDir = new File(toDir, "oat"); 894 createOatDirs(mResolvedInstructionSets, oatDir); 895 } 896 linkFiles(fromFiles, toDir, mInheritedFilesBase); 897 } else { 898 // TODO: this should delegate to DCS so the system process 899 // avoids holding open FDs into containers. 900 copyFiles(fromFiles, toDir); 901 } 902 } catch (IOException e) { 903 throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE, 904 "Failed to inherit existing install", e); 905 } 906 } 907 908 // TODO: surface more granular state from dexopt 909 mInternalProgress = 0.5f; 910 computeProgressLocked(true); 911 912 // Unpack native libraries 913 extractNativeLibraries(mResolvedStageDir, params.abiOverride); 914 915 // Container is ready to go, let's seal it up! 916 if (stageCid != null) { 917 finalizeAndFixContainer(stageCid); 918 } 919 920 // We've reached point of no return; call into PMS to install the stage. 921 // Regardless of success or failure we always destroy session. 922 final IPackageInstallObserver2 localObserver = new IPackageInstallObserver2.Stub() { 923 @Override 924 public void onUserActionRequired(Intent intent) { 925 throw new IllegalStateException(); 926 } 927 928 @Override 929 public void onPackageInstalled(String basePackageName, int returnCode, String msg, 930 Bundle extras) { 931 destroyInternal(); 932 dispatchSessionFinished(returnCode, msg, extras); 933 } 934 }; 935 936 final UserHandle user; 937 if ((params.installFlags & PackageManager.INSTALL_ALL_USERS) != 0) { 938 user = UserHandle.ALL; 939 } else { 940 user = new UserHandle(userId); 941 } 942 943 mRelinquished = true; 944 mPm.installStage(mPackageName, stageDir, stageCid, localObserver, params, 945 mInstallerPackageName, mInstallerUid, user, mCertificates); 946 } 947 948 /** 949 * Validate install by confirming that all application packages are have 950 * consistent package name, version code, and signing certificates. 951 * <p> 952 * Clears and populates {@link #mResolvedBaseFile}, 953 * {@link #mResolvedStagedFiles}, and {@link #mResolvedInheritedFiles}. 954 * <p> 955 * Renames package files in stage to match split names defined inside. 956 * <p> 957 * Note that upgrade compatibility is still performed by 958 * {@link PackageManagerService}. 959 */ 960 private void validateInstallLocked(@Nullable PackageInfo pkgInfo) 961 throws PackageManagerException { 962 mPackageName = null; 963 mVersionCode = -1; 964 mSignatures = null; 965 966 mResolvedBaseFile = null; 967 mResolvedStagedFiles.clear(); 968 mResolvedInheritedFiles.clear(); 969 970 try { 971 resolveStageDirLocked(); 972 } catch (IOException e) { 973 throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR, 974 "Failed to resolve stage location", e); 975 } 976 977 final File[] removedFiles = mResolvedStageDir.listFiles(sRemovedFilter); 978 final List<String> removeSplitList = new ArrayList<>(); 979 if (!ArrayUtils.isEmpty(removedFiles)) { 980 for (File removedFile : removedFiles) { 981 final String fileName = removedFile.getName(); 982 final String splitName = fileName.substring( 983 0, fileName.length() - REMOVE_SPLIT_MARKER_EXTENSION.length()); 984 removeSplitList.add(splitName); 985 } 986 } 987 988 final File[] addedFiles = mResolvedStageDir.listFiles(sAddedFilter); 989 if (ArrayUtils.isEmpty(addedFiles) && removeSplitList.size() == 0) { 990 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "No packages staged"); 991 } 992 // Verify that all staged packages are internally consistent 993 final ArraySet<String> stagedSplits = new ArraySet<>(); 994 for (File addedFile : addedFiles) { 995 final ApkLite apk; 996 try { 997 int flags = PackageParser.PARSE_COLLECT_CERTIFICATES; 998 if ((params.installFlags & PackageManager.INSTALL_INSTANT_APP) != 0) { 999 flags |= PackageParser.PARSE_IS_EPHEMERAL; 1000 } 1001 apk = PackageParser.parseApkLite(addedFile, flags); 1002 } catch (PackageParserException e) { 1003 throw PackageManagerException.from(e); 1004 } 1005 1006 if (!stagedSplits.add(apk.splitName)) { 1007 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 1008 "Split " + apk.splitName + " was defined multiple times"); 1009 } 1010 1011 // Use first package to define unknown values 1012 if (mPackageName == null) { 1013 mPackageName = apk.packageName; 1014 mVersionCode = apk.versionCode; 1015 } 1016 if (mSignatures == null) { 1017 mSignatures = apk.signatures; 1018 mCertificates = apk.certificates; 1019 } 1020 1021 assertApkConsistentLocked(String.valueOf(addedFile), apk); 1022 1023 // Take this opportunity to enforce uniform naming 1024 final String targetName; 1025 if (apk.splitName == null) { 1026 targetName = "base.apk"; 1027 } else { 1028 targetName = "split_" + apk.splitName + ".apk"; 1029 } 1030 if (!FileUtils.isValidExtFilename(targetName)) { 1031 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 1032 "Invalid filename: " + targetName); 1033 } 1034 1035 final File targetFile = new File(mResolvedStageDir, targetName); 1036 if (!addedFile.equals(targetFile)) { 1037 addedFile.renameTo(targetFile); 1038 } 1039 1040 // Base is coming from session 1041 if (apk.splitName == null) { 1042 mResolvedBaseFile = targetFile; 1043 } 1044 1045 mResolvedStagedFiles.add(targetFile); 1046 } 1047 1048 if (removeSplitList.size() > 0) { 1049 if (pkgInfo == null) { 1050 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 1051 "Missing existing base package for " + mPackageName); 1052 } 1053 1054 // validate split names marked for removal 1055 for (String splitName : removeSplitList) { 1056 if (!ArrayUtils.contains(pkgInfo.splitNames, splitName)) { 1057 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 1058 "Split not found: " + splitName); 1059 } 1060 } 1061 1062 // ensure we've got appropriate package name, version code and signatures 1063 if (mPackageName == null) { 1064 mPackageName = pkgInfo.packageName; 1065 mVersionCode = pkgInfo.versionCode; 1066 } 1067 if (mSignatures == null) { 1068 mSignatures = pkgInfo.signatures; 1069 } 1070 } 1071 1072 if (params.mode == SessionParams.MODE_FULL_INSTALL) { 1073 // Full installs must include a base package 1074 if (!stagedSplits.contains(null)) { 1075 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 1076 "Full install must include a base package"); 1077 } 1078 1079 } else { 1080 // Partial installs must be consistent with existing install 1081 if (pkgInfo == null || pkgInfo.applicationInfo == null) { 1082 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 1083 "Missing existing base package for " + mPackageName); 1084 } 1085 1086 final PackageLite existing; 1087 final ApkLite existingBase; 1088 ApplicationInfo appInfo = pkgInfo.applicationInfo; 1089 try { 1090 existing = PackageParser.parsePackageLite(new File(appInfo.getCodePath()), 0); 1091 existingBase = PackageParser.parseApkLite(new File(appInfo.getBaseCodePath()), 1092 PackageParser.PARSE_COLLECT_CERTIFICATES); 1093 } catch (PackageParserException e) { 1094 throw PackageManagerException.from(e); 1095 } 1096 1097 assertApkConsistentLocked("Existing base", existingBase); 1098 1099 // Inherit base if not overridden 1100 if (mResolvedBaseFile == null) { 1101 mResolvedBaseFile = new File(appInfo.getBaseCodePath()); 1102 mResolvedInheritedFiles.add(mResolvedBaseFile); 1103 } 1104 1105 // Inherit splits if not overridden 1106 if (!ArrayUtils.isEmpty(existing.splitNames)) { 1107 for (int i = 0; i < existing.splitNames.length; i++) { 1108 final String splitName = existing.splitNames[i]; 1109 final File splitFile = new File(existing.splitCodePaths[i]); 1110 final boolean splitRemoved = removeSplitList.contains(splitName); 1111 if (!stagedSplits.contains(splitName) && !splitRemoved) { 1112 mResolvedInheritedFiles.add(splitFile); 1113 } 1114 } 1115 } 1116 1117 // Inherit compiled oat directory. 1118 final File packageInstallDir = (new File(appInfo.getBaseCodePath())).getParentFile(); 1119 mInheritedFilesBase = packageInstallDir; 1120 final File oatDir = new File(packageInstallDir, "oat"); 1121 if (oatDir.exists()) { 1122 final File[] archSubdirs = oatDir.listFiles(); 1123 1124 // Keep track of all instruction sets we've seen compiled output for. 1125 // If we're linking (and not copying) inherited files, we can recreate the 1126 // instruction set hierarchy and link compiled output. 1127 if (archSubdirs != null && archSubdirs.length > 0) { 1128 final String[] instructionSets = InstructionSets.getAllDexCodeInstructionSets(); 1129 for (File archSubDir : archSubdirs) { 1130 // Skip any directory that isn't an ISA subdir. 1131 if (!ArrayUtils.contains(instructionSets, archSubDir.getName())) { 1132 continue; 1133 } 1134 1135 mResolvedInstructionSets.add(archSubDir.getName()); 1136 List<File> oatFiles = Arrays.asList(archSubDir.listFiles()); 1137 1138 // Only add compiled files associated with the base. 1139 // Once b/62269291 is resolved, we can add all compiled files again. 1140 for (File oatFile : oatFiles) { 1141 if (oatFile.getName().equals("base.art") 1142 || oatFile.getName().equals("base.odex") 1143 || oatFile.getName().equals("base.vdex")) { 1144 mResolvedInheritedFiles.add(oatFile); 1145 } 1146 } 1147 } 1148 } 1149 } 1150 } 1151 } 1152 1153 private void assertApkConsistentLocked(String tag, ApkLite apk) 1154 throws PackageManagerException { 1155 if (!mPackageName.equals(apk.packageName)) { 1156 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag + " package " 1157 + apk.packageName + " inconsistent with " + mPackageName); 1158 } 1159 if (params.appPackageName != null && !params.appPackageName.equals(apk.packageName)) { 1160 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag 1161 + " specified package " + params.appPackageName 1162 + " inconsistent with " + apk.packageName); 1163 } 1164 if (mVersionCode != apk.versionCode) { 1165 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag 1166 + " version code " + apk.versionCode + " inconsistent with " 1167 + mVersionCode); 1168 } 1169 if (!Signature.areExactMatch(mSignatures, apk.signatures)) { 1170 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 1171 tag + " signatures are inconsistent"); 1172 } 1173 } 1174 1175 /** 1176 * Calculate the final install footprint size, combining both staged and 1177 * existing APKs together and including unpacked native code from both. 1178 */ 1179 private long calculateInstalledSize() throws PackageManagerException { 1180 Preconditions.checkNotNull(mResolvedBaseFile); 1181 1182 final ApkLite baseApk; 1183 try { 1184 baseApk = PackageParser.parseApkLite(mResolvedBaseFile, 0); 1185 } catch (PackageParserException e) { 1186 throw PackageManagerException.from(e); 1187 } 1188 1189 final List<String> splitPaths = new ArrayList<>(); 1190 for (File file : mResolvedStagedFiles) { 1191 if (mResolvedBaseFile.equals(file)) continue; 1192 splitPaths.add(file.getAbsolutePath()); 1193 } 1194 for (File file : mResolvedInheritedFiles) { 1195 if (mResolvedBaseFile.equals(file)) continue; 1196 splitPaths.add(file.getAbsolutePath()); 1197 } 1198 1199 // This is kind of hacky; we're creating a half-parsed package that is 1200 // straddled between the inherited and staged APKs. 1201 final PackageLite pkg = new PackageLite(null, baseApk, null, null, null, null, 1202 splitPaths.toArray(new String[splitPaths.size()]), null); 1203 final boolean isForwardLocked = 1204 (params.installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0; 1205 1206 try { 1207 return PackageHelper.calculateInstalledSize(pkg, isForwardLocked, params.abiOverride); 1208 } catch (IOException e) { 1209 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, 1210 "Failed to calculate install size", e); 1211 } 1212 } 1213 1214 /** 1215 * Determine if creating hard links between source and destination is 1216 * possible. That is, do they all live on the same underlying device. 1217 */ 1218 private boolean isLinkPossible(List<File> fromFiles, File toDir) { 1219 try { 1220 final StructStat toStat = Os.stat(toDir.getAbsolutePath()); 1221 for (File fromFile : fromFiles) { 1222 final StructStat fromStat = Os.stat(fromFile.getAbsolutePath()); 1223 if (fromStat.st_dev != toStat.st_dev) { 1224 return false; 1225 } 1226 } 1227 } catch (ErrnoException e) { 1228 Slog.w(TAG, "Failed to detect if linking possible: " + e); 1229 return false; 1230 } 1231 return true; 1232 } 1233 1234 /** 1235 * @return the uid of the owner this session 1236 */ 1237 public int getInstallerUid() { 1238 synchronized (mLock) { 1239 return mInstallerUid; 1240 } 1241 } 1242 1243 private static String getRelativePath(File file, File base) throws IOException { 1244 final String pathStr = file.getAbsolutePath(); 1245 final String baseStr = base.getAbsolutePath(); 1246 // Don't allow relative paths. 1247 if (pathStr.contains("/.") ) { 1248 throw new IOException("Invalid path (was relative) : " + pathStr); 1249 } 1250 1251 if (pathStr.startsWith(baseStr)) { 1252 return pathStr.substring(baseStr.length()); 1253 } 1254 1255 throw new IOException("File: " + pathStr + " outside base: " + baseStr); 1256 } 1257 1258 private void createOatDirs(List<String> instructionSets, File fromDir) 1259 throws PackageManagerException { 1260 for (String instructionSet : instructionSets) { 1261 try { 1262 mPm.mInstaller.createOatDir(fromDir.getAbsolutePath(), instructionSet); 1263 } catch (InstallerException e) { 1264 throw PackageManagerException.from(e); 1265 } 1266 } 1267 } 1268 1269 private void linkFiles(List<File> fromFiles, File toDir, File fromDir) 1270 throws IOException { 1271 for (File fromFile : fromFiles) { 1272 final String relativePath = getRelativePath(fromFile, fromDir); 1273 try { 1274 mPm.mInstaller.linkFile(relativePath, fromDir.getAbsolutePath(), 1275 toDir.getAbsolutePath()); 1276 } catch (InstallerException e) { 1277 throw new IOException("failed linkOrCreateDir(" + relativePath + ", " 1278 + fromDir + ", " + toDir + ")", e); 1279 } 1280 } 1281 1282 Slog.d(TAG, "Linked " + fromFiles.size() + " files into " + toDir); 1283 } 1284 1285 private static void copyFiles(List<File> fromFiles, File toDir) throws IOException { 1286 // Remove any partial files from previous attempt 1287 for (File file : toDir.listFiles()) { 1288 if (file.getName().endsWith(".tmp")) { 1289 file.delete(); 1290 } 1291 } 1292 1293 for (File fromFile : fromFiles) { 1294 final File tmpFile = File.createTempFile("inherit", ".tmp", toDir); 1295 if (LOGD) Slog.d(TAG, "Copying " + fromFile + " to " + tmpFile); 1296 if (!FileUtils.copyFile(fromFile, tmpFile)) { 1297 throw new IOException("Failed to copy " + fromFile + " to " + tmpFile); 1298 } 1299 try { 1300 Os.chmod(tmpFile.getAbsolutePath(), 0644); 1301 } catch (ErrnoException e) { 1302 throw new IOException("Failed to chmod " + tmpFile); 1303 } 1304 final File toFile = new File(toDir, fromFile.getName()); 1305 if (LOGD) Slog.d(TAG, "Renaming " + tmpFile + " to " + toFile); 1306 if (!tmpFile.renameTo(toFile)) { 1307 throw new IOException("Failed to rename " + tmpFile + " to " + toFile); 1308 } 1309 } 1310 Slog.d(TAG, "Copied " + fromFiles.size() + " files into " + toDir); 1311 } 1312 1313 private static void extractNativeLibraries(File packageDir, String abiOverride) 1314 throws PackageManagerException { 1315 // Always start from a clean slate 1316 final File libDir = new File(packageDir, NativeLibraryHelper.LIB_DIR_NAME); 1317 NativeLibraryHelper.removeNativeBinariesFromDirLI(libDir, true); 1318 1319 NativeLibraryHelper.Handle handle = null; 1320 try { 1321 handle = NativeLibraryHelper.Handle.create(packageDir); 1322 final int res = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libDir, 1323 abiOverride); 1324 if (res != PackageManager.INSTALL_SUCCEEDED) { 1325 throw new PackageManagerException(res, 1326 "Failed to extract native libraries, res=" + res); 1327 } 1328 } catch (IOException e) { 1329 throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, 1330 "Failed to extract native libraries", e); 1331 } finally { 1332 IoUtils.closeQuietly(handle); 1333 } 1334 } 1335 1336 private static void resizeContainer(String cid, long targetSize) 1337 throws PackageManagerException { 1338 String path = PackageHelper.getSdDir(cid); 1339 if (path == null) { 1340 throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR, 1341 "Failed to find mounted " + cid); 1342 } 1343 1344 final long currentSize = new File(path).getTotalSpace(); 1345 if (currentSize > targetSize) { 1346 Slog.w(TAG, "Current size " + currentSize + " is larger than target size " 1347 + targetSize + "; skipping resize"); 1348 return; 1349 } 1350 1351 if (!PackageHelper.unMountSdDir(cid)) { 1352 throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR, 1353 "Failed to unmount " + cid + " before resize"); 1354 } 1355 1356 if (!PackageHelper.resizeSdDir(targetSize, cid, 1357 PackageManagerService.getEncryptKey())) { 1358 throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR, 1359 "Failed to resize " + cid + " to " + targetSize + " bytes"); 1360 } 1361 1362 path = PackageHelper.mountSdDir(cid, PackageManagerService.getEncryptKey(), 1363 Process.SYSTEM_UID, false); 1364 if (path == null) { 1365 throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR, 1366 "Failed to mount " + cid + " after resize"); 1367 } 1368 } 1369 1370 private void finalizeAndFixContainer(String cid) throws PackageManagerException { 1371 if (!PackageHelper.finalizeSdDir(cid)) { 1372 throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR, 1373 "Failed to finalize container " + cid); 1374 } 1375 1376 if (!PackageHelper.fixSdPermissions(cid, defaultContainerGid, null)) { 1377 throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR, 1378 "Failed to fix permissions on container " + cid); 1379 } 1380 } 1381 1382 void setPermissionsResult(boolean accepted) { 1383 if (!mSealed) { 1384 throw new SecurityException("Must be sealed to accept permissions"); 1385 } 1386 1387 if (accepted) { 1388 // Mark and kick off another install pass 1389 synchronized (mLock) { 1390 mPermissionsManuallyAccepted = true; 1391 mHandler.obtainMessage(MSG_COMMIT).sendToTarget(); 1392 } 1393 } else { 1394 destroyInternal(); 1395 dispatchSessionFinished(INSTALL_FAILED_ABORTED, "User rejected permissions", null); 1396 } 1397 } 1398 1399 public void open() throws IOException { 1400 if (mActiveCount.getAndIncrement() == 0) { 1401 mCallback.onSessionActiveChanged(this, true); 1402 } 1403 1404 boolean wasPrepared; 1405 synchronized (mLock) { 1406 wasPrepared = mPrepared; 1407 if (!mPrepared) { 1408 if (stageDir != null) { 1409 prepareStageDir(stageDir); 1410 } else if (stageCid != null) { 1411 final long identity = Binder.clearCallingIdentity(); 1412 try { 1413 prepareExternalStageCid(stageCid, params.sizeBytes); 1414 } finally { 1415 Binder.restoreCallingIdentity(identity); 1416 } 1417 1418 // TODO: deliver more granular progress for ASEC allocation 1419 mInternalProgress = 0.25f; 1420 computeProgressLocked(true); 1421 } else { 1422 throw new IllegalArgumentException( 1423 "Exactly one of stageDir or stageCid stage must be set"); 1424 } 1425 1426 mPrepared = true; 1427 } 1428 } 1429 1430 if (!wasPrepared) { 1431 mCallback.onSessionPrepared(this); 1432 } 1433 } 1434 1435 @Override 1436 public void close() { 1437 closeInternal(true); 1438 } 1439 1440 private void closeInternal(boolean checkCaller) { 1441 int activeCount; 1442 synchronized (mLock) { 1443 if (checkCaller) { 1444 assertCallerIsOwnerOrRootLocked(); 1445 } 1446 1447 activeCount = mActiveCount.decrementAndGet(); 1448 } 1449 1450 if (activeCount == 0) { 1451 mCallback.onSessionActiveChanged(this, false); 1452 } 1453 } 1454 1455 @Override 1456 public void abandon() { 1457 synchronized (mLock) { 1458 assertCallerIsOwnerOrRootLocked(); 1459 1460 if (mRelinquished) { 1461 Slog.d(TAG, "Ignoring abandon after commit relinquished control"); 1462 return; 1463 } 1464 destroyInternal(); 1465 } 1466 1467 dispatchSessionFinished(INSTALL_FAILED_ABORTED, "Session was abandoned", null); 1468 } 1469 1470 private void dispatchSessionFinished(int returnCode, String msg, Bundle extras) { 1471 final IPackageInstallObserver2 observer; 1472 final String packageName; 1473 synchronized (mLock) { 1474 mFinalStatus = returnCode; 1475 mFinalMessage = msg; 1476 1477 observer = mRemoteObserver; 1478 packageName = mPackageName; 1479 } 1480 1481 if (observer != null) { 1482 try { 1483 observer.onPackageInstalled(packageName, returnCode, msg, extras); 1484 } catch (RemoteException ignored) { 1485 } 1486 } 1487 1488 final boolean success = (returnCode == PackageManager.INSTALL_SUCCEEDED); 1489 1490 // Send broadcast to default launcher only if it's a new install 1491 final boolean isNewInstall = extras == null || !extras.getBoolean(Intent.EXTRA_REPLACING); 1492 if (success && isNewInstall) { 1493 mPm.sendSessionCommitBroadcast(generateInfo(), userId); 1494 } 1495 1496 mCallback.onSessionFinished(this, success); 1497 } 1498 1499 private void destroyInternal() { 1500 synchronized (mLock) { 1501 mSealed = true; 1502 mDestroyed = true; 1503 1504 // Force shut down all bridges 1505 for (RevocableFileDescriptor fd : mFds) { 1506 fd.revoke(); 1507 } 1508 for (FileBridge bridge : mBridges) { 1509 bridge.forceClose(); 1510 } 1511 } 1512 if (stageDir != null) { 1513 try { 1514 mPm.mInstaller.rmPackageDir(stageDir.getAbsolutePath()); 1515 } catch (InstallerException ignored) { 1516 } 1517 } 1518 if (stageCid != null) { 1519 PackageHelper.destroySdDir(stageCid); 1520 } 1521 } 1522 1523 void dump(IndentingPrintWriter pw) { 1524 synchronized (mLock) { 1525 dumpLocked(pw); 1526 } 1527 } 1528 1529 private void dumpLocked(IndentingPrintWriter pw) { 1530 pw.println("Session " + sessionId + ":"); 1531 pw.increaseIndent(); 1532 1533 pw.printPair("userId", userId); 1534 pw.printPair("mOriginalInstallerUid", mOriginalInstallerUid); 1535 pw.printPair("mInstallerPackageName", mInstallerPackageName); 1536 pw.printPair("mInstallerUid", mInstallerUid); 1537 pw.printPair("createdMillis", createdMillis); 1538 pw.printPair("stageDir", stageDir); 1539 pw.printPair("stageCid", stageCid); 1540 pw.println(); 1541 1542 params.dump(pw); 1543 1544 pw.printPair("mClientProgress", mClientProgress); 1545 pw.printPair("mProgress", mProgress); 1546 pw.printPair("mSealed", mSealed); 1547 pw.printPair("mPermissionsManuallyAccepted", mPermissionsManuallyAccepted); 1548 pw.printPair("mRelinquished", mRelinquished); 1549 pw.printPair("mDestroyed", mDestroyed); 1550 pw.printPair("mFds", mFds.size()); 1551 pw.printPair("mBridges", mBridges.size()); 1552 pw.printPair("mFinalStatus", mFinalStatus); 1553 pw.printPair("mFinalMessage", mFinalMessage); 1554 pw.println(); 1555 1556 pw.decreaseIndent(); 1557 } 1558 1559 private static void writeGrantedRuntimePermissionsLocked(XmlSerializer out, 1560 String[] grantedRuntimePermissions) throws IOException { 1561 if (grantedRuntimePermissions != null) { 1562 for (String permission : grantedRuntimePermissions) { 1563 out.startTag(null, TAG_GRANTED_RUNTIME_PERMISSION); 1564 writeStringAttribute(out, ATTR_NAME, permission); 1565 out.endTag(null, TAG_GRANTED_RUNTIME_PERMISSION); 1566 } 1567 } 1568 } 1569 1570 private static File buildAppIconFile(int sessionId, @NonNull File sessionsDir) { 1571 return new File(sessionsDir, "app_icon." + sessionId + ".png"); 1572 } 1573 1574 /** 1575 * Write this session to a {@link XmlSerializer}. 1576 * 1577 * @param out Where to write the session to 1578 * @param sessionsDir The directory containing the sessions 1579 */ 1580 void write(@NonNull XmlSerializer out, @NonNull File sessionsDir) throws IOException { 1581 synchronized (mLock) { 1582 if (mDestroyed) { 1583 return; 1584 } 1585 1586 out.startTag(null, TAG_SESSION); 1587 1588 writeIntAttribute(out, ATTR_SESSION_ID, sessionId); 1589 writeIntAttribute(out, ATTR_USER_ID, userId); 1590 writeStringAttribute(out, ATTR_INSTALLER_PACKAGE_NAME, 1591 mInstallerPackageName); 1592 writeIntAttribute(out, ATTR_INSTALLER_UID, mInstallerUid); 1593 writeLongAttribute(out, ATTR_CREATED_MILLIS, createdMillis); 1594 if (stageDir != null) { 1595 writeStringAttribute(out, ATTR_SESSION_STAGE_DIR, 1596 stageDir.getAbsolutePath()); 1597 } 1598 if (stageCid != null) { 1599 writeStringAttribute(out, ATTR_SESSION_STAGE_CID, stageCid); 1600 } 1601 writeBooleanAttribute(out, ATTR_PREPARED, isPrepared()); 1602 writeBooleanAttribute(out, ATTR_SEALED, isSealed()); 1603 1604 writeIntAttribute(out, ATTR_MODE, params.mode); 1605 writeIntAttribute(out, ATTR_INSTALL_FLAGS, params.installFlags); 1606 writeIntAttribute(out, ATTR_INSTALL_LOCATION, params.installLocation); 1607 writeLongAttribute(out, ATTR_SIZE_BYTES, params.sizeBytes); 1608 writeStringAttribute(out, ATTR_APP_PACKAGE_NAME, params.appPackageName); 1609 writeStringAttribute(out, ATTR_APP_LABEL, params.appLabel); 1610 writeUriAttribute(out, ATTR_ORIGINATING_URI, params.originatingUri); 1611 writeIntAttribute(out, ATTR_ORIGINATING_UID, params.originatingUid); 1612 writeUriAttribute(out, ATTR_REFERRER_URI, params.referrerUri); 1613 writeStringAttribute(out, ATTR_ABI_OVERRIDE, params.abiOverride); 1614 writeStringAttribute(out, ATTR_VOLUME_UUID, params.volumeUuid); 1615 writeIntAttribute(out, ATTR_INSTALL_REASON, params.installReason); 1616 1617 // Persist app icon if changed since last written 1618 File appIconFile = buildAppIconFile(sessionId, sessionsDir); 1619 if (params.appIcon == null && appIconFile.exists()) { 1620 appIconFile.delete(); 1621 } else if (params.appIcon != null 1622 && appIconFile.lastModified() != params.appIconLastModified) { 1623 if (LOGD) Slog.w(TAG, "Writing changed icon " + appIconFile); 1624 FileOutputStream os = null; 1625 try { 1626 os = new FileOutputStream(appIconFile); 1627 params.appIcon.compress(Bitmap.CompressFormat.PNG, 90, os); 1628 } catch (IOException e) { 1629 Slog.w(TAG, "Failed to write icon " + appIconFile + ": " + e.getMessage()); 1630 } finally { 1631 IoUtils.closeQuietly(os); 1632 } 1633 1634 params.appIconLastModified = appIconFile.lastModified(); 1635 } 1636 1637 writeGrantedRuntimePermissionsLocked(out, params.grantedRuntimePermissions); 1638 } 1639 1640 out.endTag(null, TAG_SESSION); 1641 } 1642 1643 private static String[] readGrantedRuntimePermissions(XmlPullParser in) 1644 throws IOException, XmlPullParserException { 1645 List<String> permissions = null; 1646 1647 final int outerDepth = in.getDepth(); 1648 int type; 1649 while ((type = in.next()) != XmlPullParser.END_DOCUMENT 1650 && (type != XmlPullParser.END_TAG || in.getDepth() > outerDepth)) { 1651 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { 1652 continue; 1653 } 1654 if (TAG_GRANTED_RUNTIME_PERMISSION.equals(in.getName())) { 1655 String permission = readStringAttribute(in, ATTR_NAME); 1656 if (permissions == null) { 1657 permissions = new ArrayList<>(); 1658 } 1659 permissions.add(permission); 1660 } 1661 } 1662 1663 if (permissions == null) { 1664 return null; 1665 } 1666 1667 String[] permissionsArray = new String[permissions.size()]; 1668 permissions.toArray(permissionsArray); 1669 return permissionsArray; 1670 } 1671 1672 /** 1673 * Read new session from a {@link XmlPullParser xml description} and create it. 1674 * 1675 * @param in The source of the description 1676 * @param callback Callback the session uses to notify about changes of it's state 1677 * @param context Context to be used by the session 1678 * @param pm PackageManager to use by the session 1679 * @param installerThread Thread to be used for callbacks of this session 1680 * @param sessionsDir The directory the sessions are stored in 1681 * 1682 * @return The newly created session 1683 */ 1684 public static PackageInstallerSession readFromXml(@NonNull XmlPullParser in, 1685 @NonNull PackageInstallerService.InternalCallback callback, @NonNull Context context, 1686 @NonNull PackageManagerService pm, Looper installerThread, @NonNull File sessionsDir) 1687 throws IOException, XmlPullParserException { 1688 final int sessionId = readIntAttribute(in, ATTR_SESSION_ID); 1689 final int userId = readIntAttribute(in, ATTR_USER_ID); 1690 final String installerPackageName = readStringAttribute(in, ATTR_INSTALLER_PACKAGE_NAME); 1691 final int installerUid = readIntAttribute(in, ATTR_INSTALLER_UID, pm.getPackageUid( 1692 installerPackageName, PackageManager.MATCH_UNINSTALLED_PACKAGES, userId)); 1693 final long createdMillis = readLongAttribute(in, ATTR_CREATED_MILLIS); 1694 final String stageDirRaw = readStringAttribute(in, ATTR_SESSION_STAGE_DIR); 1695 final File stageDir = (stageDirRaw != null) ? new File(stageDirRaw) : null; 1696 final String stageCid = readStringAttribute(in, ATTR_SESSION_STAGE_CID); 1697 final boolean prepared = readBooleanAttribute(in, ATTR_PREPARED, true); 1698 final boolean sealed = readBooleanAttribute(in, ATTR_SEALED); 1699 1700 final SessionParams params = new SessionParams( 1701 SessionParams.MODE_INVALID); 1702 params.mode = readIntAttribute(in, ATTR_MODE); 1703 params.installFlags = readIntAttribute(in, ATTR_INSTALL_FLAGS); 1704 params.installLocation = readIntAttribute(in, ATTR_INSTALL_LOCATION); 1705 params.sizeBytes = readLongAttribute(in, ATTR_SIZE_BYTES); 1706 params.appPackageName = readStringAttribute(in, ATTR_APP_PACKAGE_NAME); 1707 params.appIcon = readBitmapAttribute(in, ATTR_APP_ICON); 1708 params.appLabel = readStringAttribute(in, ATTR_APP_LABEL); 1709 params.originatingUri = readUriAttribute(in, ATTR_ORIGINATING_URI); 1710 params.originatingUid = 1711 readIntAttribute(in, ATTR_ORIGINATING_UID, SessionParams.UID_UNKNOWN); 1712 params.referrerUri = readUriAttribute(in, ATTR_REFERRER_URI); 1713 params.abiOverride = readStringAttribute(in, ATTR_ABI_OVERRIDE); 1714 params.volumeUuid = readStringAttribute(in, ATTR_VOLUME_UUID); 1715 params.grantedRuntimePermissions = readGrantedRuntimePermissions(in); 1716 params.installReason = readIntAttribute(in, ATTR_INSTALL_REASON); 1717 1718 final File appIconFile = buildAppIconFile(sessionId, sessionsDir); 1719 if (appIconFile.exists()) { 1720 params.appIcon = BitmapFactory.decodeFile(appIconFile.getAbsolutePath()); 1721 params.appIconLastModified = appIconFile.lastModified(); 1722 } 1723 1724 return new PackageInstallerSession(callback, context, pm, 1725 installerThread, sessionId, userId, installerPackageName, installerUid, 1726 params, createdMillis, stageDir, stageCid, prepared, sealed); 1727 } 1728 } 1729