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