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