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