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