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