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