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