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