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