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