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