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