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