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