blob: e4a0a3ab8dfa797dc9ca3392e5d0ff5de79fd700 [file] [log] [blame]
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.pm;
import static android.app.AppOpsManager.MODE_DEFAULT;
import static android.content.pm.PackageInstaller.SessionParams.MODE_INHERIT_EXISTING;
import static android.content.pm.PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
import static android.content.pm.PackageManager.INSTALL_STAGED;
import static android.content.pm.PackageManager.INSTALL_SUCCEEDED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
import static android.os.incremental.IncrementalManager.isIncrementalPath;
import static com.android.internal.content.NativeLibraryHelper.LIB_DIR_NAME;
import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL;
import static com.android.server.pm.PackageManagerService.DEBUG_INSTANT;
import static com.android.server.pm.PackageManagerService.TAG;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.apex.ApexInfo;
import android.content.pm.DataLoaderType;
import android.content.pm.IPackageInstallObserver2;
import android.content.pm.PackageInfoLite;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.SigningDetails;
import android.content.pm.parsing.PackageLite;
import android.os.Environment;
import android.os.Trace;
import android.os.UserHandle;
import android.util.ArraySet;
import android.util.Pair;
import android.util.Slog;
import com.android.internal.content.F2fsUtils;
import com.android.internal.content.InstallLocationUtils;
import com.android.internal.content.NativeLibraryHelper;
import com.android.internal.util.Preconditions;
import com.android.server.pm.parsing.PackageParser2;
import com.android.server.pm.pkg.AndroidPackage;
import libcore.io.IoUtils;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
class InstallingSession {
final OriginInfo mOriginInfo;
final MoveInfo mMoveInfo;
final IPackageInstallObserver2 mObserver;
int mInstallFlags;
@NonNull
final InstallSource mInstallSource;
final String mVolumeUuid;
int mRet;
final String mPackageAbiOverride;
final String[] mGrantedRuntimePermissions;
final List<String> mAllowlistedRestrictedPermissions;
final int mAutoRevokePermissionsMode;
final SigningDetails mSigningDetails;
final int mInstallReason;
final int mInstallScenario;
@Nullable
MultiPackageInstallingSession mParentInstallingSession;
final boolean mForceQueryableOverride;
final int mDataLoaderType;
final long mRequiredInstalledVersionCode;
final int mPackageSource;
final PackageLite mPackageLite;
String mTraceMethod;
int mTraceCookie;
/** User handle for the user requesting the information or installation. */
private final UserHandle mUser;
@NonNull
final PackageManagerService mPm;
final InstallPackageHelper mInstallPackageHelper;
final RemovePackageHelper mRemovePackageHelper;
final boolean mIsInherit;
InstallingSession(OriginInfo originInfo, MoveInfo moveInfo, IPackageInstallObserver2 observer,
int installFlags, InstallSource installSource, String volumeUuid,
UserHandle user, String packageAbiOverride, int packageSource,
PackageLite packageLite, PackageManagerService pm) {
mPm = pm;
mUser = user;
mInstallPackageHelper = new InstallPackageHelper(mPm);
mRemovePackageHelper = new RemovePackageHelper(mPm);
mOriginInfo = originInfo;
mMoveInfo = moveInfo;
mObserver = observer;
mInstallFlags = installFlags;
mInstallSource = Preconditions.checkNotNull(installSource);
mVolumeUuid = volumeUuid;
mPackageAbiOverride = packageAbiOverride;
mGrantedRuntimePermissions = null;
mAllowlistedRestrictedPermissions = null;
mAutoRevokePermissionsMode = MODE_DEFAULT;
mSigningDetails = SigningDetails.UNKNOWN;
mInstallReason = PackageManager.INSTALL_REASON_UNKNOWN;
mInstallScenario = PackageManager.INSTALL_SCENARIO_DEFAULT;
mForceQueryableOverride = false;
mDataLoaderType = DataLoaderType.NONE;
mRequiredInstalledVersionCode = PackageManager.VERSION_CODE_HIGHEST;
mPackageSource = packageSource;
mPackageLite = packageLite;
mIsInherit = false;
}
InstallingSession(File stagedDir, IPackageInstallObserver2 observer,
PackageInstaller.SessionParams sessionParams, InstallSource installSource,
UserHandle user, SigningDetails signingDetails, int installerUid,
PackageLite packageLite, PackageManagerService pm) {
mPm = pm;
mUser = user;
mInstallPackageHelper = new InstallPackageHelper(mPm);
mRemovePackageHelper = new RemovePackageHelper(mPm);
mOriginInfo = OriginInfo.fromStagedFile(stagedDir);
mMoveInfo = null;
mInstallReason = fixUpInstallReason(
installSource.installerPackageName, installerUid, sessionParams.installReason);
mInstallScenario = sessionParams.installScenario;
mObserver = observer;
mInstallFlags = sessionParams.installFlags;
mInstallSource = installSource;
mVolumeUuid = sessionParams.volumeUuid;
mPackageAbiOverride = sessionParams.abiOverride;
mGrantedRuntimePermissions = sessionParams.grantedRuntimePermissions;
mAllowlistedRestrictedPermissions = sessionParams.whitelistedRestrictedPermissions;
mAutoRevokePermissionsMode = sessionParams.autoRevokePermissionsMode;
mSigningDetails = signingDetails;
mForceQueryableOverride = sessionParams.forceQueryableOverride;
mDataLoaderType = (sessionParams.dataLoaderParams != null)
? sessionParams.dataLoaderParams.getType() : DataLoaderType.NONE;
mRequiredInstalledVersionCode = sessionParams.requiredInstalledVersionCode;
mPackageSource = sessionParams.packageSource;
mPackageLite = packageLite;
mIsInherit = sessionParams.mode == MODE_INHERIT_EXISTING;
}
@Override
public String toString() {
return "InstallingSession{" + Integer.toHexString(System.identityHashCode(this))
+ " file=" + mOriginInfo.mFile + "}";
}
/**
* Override install location based on default policy if needed.
*
* Only {@link #mInstallFlags} may mutate in this method.
*
* Only {@link PackageManager#INSTALL_INTERNAL} flag may mutate in
* {@link #mInstallFlags}
*/
private int overrideInstallLocation(String packageName, int recommendedInstallLocation,
int installLocation) {
if (mOriginInfo.mStaged) {
// If we're already staged, we've firmly committed to an install location
if (mOriginInfo.mFile != null) {
mInstallFlags |= PackageManager.INSTALL_INTERNAL;
} else {
throw new IllegalStateException("Invalid stage location");
}
}
if (recommendedInstallLocation < 0) {
return InstallLocationUtils.getInstallationErrorCode(recommendedInstallLocation);
}
// Override with defaults if needed.
Computer snapshot = mPm.snapshotComputer();
AndroidPackage installedPkg = snapshot.getPackage(packageName);
if (installedPkg != null) {
// Currently installed package which the new package is attempting to replace
recommendedInstallLocation = InstallLocationUtils.installLocationPolicy(
installLocation, recommendedInstallLocation, mInstallFlags,
installedPkg.isSystem(), installedPkg.isExternalStorage());
}
final boolean onInt = (mInstallFlags & PackageManager.INSTALL_INTERNAL) != 0;
if (!onInt) {
// Override install location with flags
if (recommendedInstallLocation == InstallLocationUtils.RECOMMEND_INSTALL_EXTERNAL) {
// Set the flag to install on external media.
mInstallFlags &= ~PackageManager.INSTALL_INTERNAL;
} else {
// Make sure the flag for installing on external media is unset
mInstallFlags |= PackageManager.INSTALL_INTERNAL;
}
}
return INSTALL_SUCCEEDED;
}
/*
* Invoke remote method to get package information and install
* location values. Override install location based on default
* policy if needed and then create install arguments based
* on the install location.
*/
private void handleStartCopy() {
if ((mInstallFlags & PackageManager.INSTALL_APEX) != 0) {
mRet = INSTALL_SUCCEEDED;
return;
}
PackageInfoLite pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mPm.mContext,
mPackageLite, mOriginInfo.mResolvedPath, mInstallFlags, mPackageAbiOverride);
// For staged session, there is a delay between its verification and install. Device
// state can change within this delay and hence we need to re-verify certain conditions.
boolean isStaged = (mInstallFlags & INSTALL_STAGED) != 0;
if (isStaged) {
Pair<Integer, String> ret = mInstallPackageHelper.verifyReplacingVersionCode(
pkgLite, mRequiredInstalledVersionCode, mInstallFlags);
mRet = ret.first;
if (mRet != INSTALL_SUCCEEDED) {
return;
}
}
final boolean ephemeral = (mInstallFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
if (DEBUG_INSTANT && ephemeral) {
Slog.v(TAG, "pkgLite for install: " + pkgLite);
}
if (!mOriginInfo.mStaged && pkgLite.recommendedInstallLocation
== InstallLocationUtils.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
// If we are not staged and have too little free space, try to free cache
// before giving up.
pkgLite.recommendedInstallLocation = mPm.freeCacheForInstallation(
pkgLite.recommendedInstallLocation, mPackageLite,
mOriginInfo.mResolvedPath, mPackageAbiOverride, mInstallFlags);
}
mRet = overrideInstallLocation(pkgLite.packageName, pkgLite.recommendedInstallLocation,
pkgLite.installLocation);
}
private void handleReturnCode() {
processPendingInstall();
}
private void processPendingInstall() {
InstallRequest installRequest = new InstallRequest(this);
if (mRet == PackageManager.INSTALL_SUCCEEDED) {
mRet = copyApk(installRequest);
}
if (mRet == PackageManager.INSTALL_SUCCEEDED) {
F2fsUtils.releaseCompressedBlocks(
mPm.mContext.getContentResolver(), new File(installRequest.getCodePath()));
}
installRequest.setReturnCode(mRet);
if (mParentInstallingSession != null) {
mParentInstallingSession.tryProcessInstallRequest(installRequest);
} else {
// Queue up an async operation since the package installation may take a little while.
mPm.mHandler.post(() -> processInstallRequests(
mRet == PackageManager.INSTALL_SUCCEEDED /* success */,
Collections.singletonList(installRequest)));
}
}
private int copyApk(InstallRequest request) {
if (mMoveInfo == null) {
return copyApkForFileInstall(request);
} else {
return copyApkForMoveInstall(request);
}
}
private int copyApkForFileInstall(InstallRequest request) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyApk");
try {
if (mOriginInfo.mStaged) {
if (DEBUG_INSTALL) {
Slog.d(TAG, mOriginInfo.mFile + " already staged; skipping copy");
}
request.setCodeFile(mOriginInfo.mFile);
return PackageManager.INSTALL_SUCCEEDED;
}
try {
final boolean isEphemeral =
(mInstallFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
request.setCodeFile(
mPm.mInstallerService.allocateStageDirLegacy(mVolumeUuid, isEphemeral));
} catch (IOException e) {
Slog.w(TAG, "Failed to create copy file: " + e);
return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
}
int ret = PackageManagerServiceUtils.copyPackage(
mOriginInfo.mFile.getAbsolutePath(), request.getCodeFile());
if (ret != PackageManager.INSTALL_SUCCEEDED) {
Slog.e(TAG, "Failed to copy package");
return ret;
}
final boolean isIncremental = isIncrementalPath(
request.getCodeFile().getAbsolutePath());
final File libraryRoot = new File(request.getCodeFile(), LIB_DIR_NAME);
NativeLibraryHelper.Handle handle = null;
try {
handle = NativeLibraryHelper.Handle.create(request.getCodeFile());
ret = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot,
request.getAbiOverride(), isIncremental);
} catch (IOException e) {
Slog.e(TAG, "Copying native libraries failed", e);
ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
} finally {
IoUtils.closeQuietly(handle);
}
return ret;
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
private int copyApkForMoveInstall(InstallRequest request) {
if (DEBUG_INSTALL) {
Slog.d(TAG, "Moving " + mMoveInfo.mPackageName + " from "
+ mMoveInfo.mFromUuid + " to " + mMoveInfo.mToUuid);
}
synchronized (mPm.mInstallLock) {
try {
mPm.mInstaller.moveCompleteApp(mMoveInfo.mFromUuid, mMoveInfo.mToUuid,
mMoveInfo.mPackageName, mMoveInfo.mAppId, mMoveInfo.mSeInfo,
mMoveInfo.mTargetSdkVersion, mMoveInfo.mFromCodePath);
} catch (Installer.InstallerException e) {
Slog.w(TAG, "Failed to move app", e);
return PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
}
}
final String toPathName = new File(mMoveInfo.mFromCodePath).getName();
request.setCodeFile(
new File(Environment.getDataAppDirectory(mMoveInfo.mToUuid), toPathName));
if (DEBUG_INSTALL) Slog.d(TAG, "codeFile after move is " + request.getCodeFile());
return PackageManager.INSTALL_SUCCEEDED;
}
/**
* Ensure that the install reason matches what we know about the package installer (e.g. whether
* it is acting on behalf on an enterprise or the user).
*
* Note that the ordering of the conditionals in this method is important. The checks we perform
* are as follows, in this order:
*
* 1) If the install is being performed by a system app, we can trust the app to have set the
* install reason correctly. Thus, we pass through the install reason unchanged, no matter
* what it is.
* 2) If the install is being performed by a device or profile owner app, the install reason
* should be enterprise policy. However, we cannot be sure that the device or profile owner
* set the install reason correctly. If the app targets an older SDK version where install
* reasons did not exist yet, or if the app author simply forgot, the install reason may be
* unset or wrong. Thus, we force the install reason to be enterprise policy.
* 3) In all other cases, the install is being performed by a regular app that is neither part
* of the system nor a device or profile owner. We have no reason to believe that this app is
* acting on behalf of the enterprise admin. Thus, we check whether the install reason was
* set to enterprise policy and if so, change it to unknown instead.
*/
private int fixUpInstallReason(String installerPackageName, int installerUid,
int installReason) {
if (mPm.snapshotComputer().checkUidPermission(android.Manifest.permission.INSTALL_PACKAGES,
installerUid) == PERMISSION_GRANTED) {
// If the install is being performed by a system app, we trust that app to have set the
// install reason correctly.
return installReason;
}
final String ownerPackage = mPm.mProtectedPackages.getDeviceOwnerOrProfileOwnerPackage(
UserHandle.getUserId(installerUid));
if (ownerPackage != null && ownerPackage.equals(installerPackageName)) {
// If the install is being performed by a device or profile owner, the install
// reason should be enterprise policy.
return PackageManager.INSTALL_REASON_POLICY;
}
if (installReason == PackageManager.INSTALL_REASON_POLICY) {
// If the install is being performed by a regular app (i.e. neither system app nor
// device or profile owner), we have no reason to believe that the app is acting on
// behalf of an enterprise. If the app set the install reason to enterprise policy,
// change it to unknown instead.
return PackageManager.INSTALL_REASON_UNKNOWN;
}
// If the install is being performed by a regular app and the install reason was set to any
// value but enterprise policy, leave the install reason unchanged.
return installReason;
}
public void installStage() {
setTraceMethod("installStage").setTraceCookie(System.identityHashCode(this));
Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "installStage",
System.identityHashCode(this));
Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
System.identityHashCode(this));
mPm.mHandler.post(this::start);
}
public void installStage(List<InstallingSession> children)
throws PackageManagerException {
final MultiPackageInstallingSession installingSession =
new MultiPackageInstallingSession(getUser(), children, mPm);
setTraceMethod("installStageMultiPackage").setTraceCookie(System.identityHashCode(
installingSession));
Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "installStageMultiPackage",
System.identityHashCode(installingSession));
Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
System.identityHashCode(installingSession));
mPm.mHandler.post(installingSession::start);
}
public void movePackage() {
setTraceMethod("movePackage").setTraceCookie(System.identityHashCode(this));
Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "movePackage",
System.identityHashCode(this));
Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
System.identityHashCode(this));
mPm.mHandler.post(this::start);
}
public UserHandle getUser() {
return mUser;
}
private void start() {
if (DEBUG_INSTALL) Slog.i(TAG, "start " + mUser + ": " + this);
Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
System.identityHashCode(this));
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "startInstall");
handleStartCopy();
handleReturnCode();
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
private InstallingSession setTraceMethod(String traceMethod) {
mTraceMethod = traceMethod;
return this;
}
private void setTraceCookie(int traceCookie) {
mTraceCookie = traceCookie;
}
private void processInstallRequests(boolean success, List<InstallRequest> installRequests) {
List<InstallRequest> apexInstallRequests = new ArrayList<>();
List<InstallRequest> apkInstallRequests = new ArrayList<>();
for (InstallRequest request : installRequests) {
if ((request.getInstallFlags() & PackageManager.INSTALL_APEX) != 0) {
apexInstallRequests.add(request);
} else {
apkInstallRequests.add(request);
}
}
// Note: supporting multi package install of both APEXes and APKs might requir some
// thinking to ensure atomicity of the install.
if (!apexInstallRequests.isEmpty() && !apkInstallRequests.isEmpty()) {
// This should've been caught at the validation step, but for some reason wasn't.
throw new IllegalStateException(
"Attempted to do a multi package install of both APEXes and APKs");
}
if (!apexInstallRequests.isEmpty()) {
if (success) {
// Since installApexPackages requires talking to external service (apexd), we
// schedule to run it async. Once it finishes, it will resume the install.
Thread t = new Thread(() -> installApexPackagesTraced(apexInstallRequests),
"installApexPackages");
t.start();
} else {
// Non-staged APEX installation failed somewhere before
// processInstallRequestAsync. In that case just notify the observer about the
// failure.
InstallRequest request = apexInstallRequests.get(0);
mPm.notifyInstallObserver(request);
}
return;
}
processApkInstallRequests(success, installRequests);
}
private void processApkInstallRequests(boolean success, List<InstallRequest> installRequests) {
if (success) {
for (InstallRequest request : installRequests) {
if (request.getReturnCode() != PackageManager.INSTALL_SUCCEEDED) {
cleanUpForFailedInstall(request);
}
}
mInstallPackageHelper.installPackagesTraced(installRequests);
for (InstallRequest request : installRequests) {
request.onInstallCompleted(mPm.snapshotComputer());
doPostInstall(request);
}
}
for (InstallRequest request : installRequests) {
mInstallPackageHelper.restoreAndPostInstall(request);
}
}
private void doPostInstall(InstallRequest request) {
if (mMoveInfo != null) {
if (request.getReturnCode() == PackageManager.INSTALL_SUCCEEDED) {
mRemovePackageHelper.cleanUpForMoveInstall(mMoveInfo.mFromUuid,
mMoveInfo.mPackageName, mMoveInfo.mFromCodePath);
} else {
mRemovePackageHelper.cleanUpForMoveInstall(mMoveInfo.mToUuid,
mMoveInfo.mPackageName, mMoveInfo.mFromCodePath);
}
} else {
if (request.getReturnCode() != PackageManager.INSTALL_SUCCEEDED) {
mRemovePackageHelper.removeCodePath(request.getCodeFile());
}
}
}
private void cleanUpForFailedInstall(InstallRequest request) {
if (request.isInstallMove()) {
mRemovePackageHelper.cleanUpForMoveInstall(request.getMoveToUuid(),
request.getMovePackageName(), request.getMoveFromCodePath());
} else {
mRemovePackageHelper.removeCodePath(request.getCodeFile());
}
}
private void installApexPackagesTraced(List<InstallRequest> requests) {
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installApexPackages");
installApexPackages(requests);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
private void installApexPackages(List<InstallRequest> requests) {
if (requests.isEmpty()) {
return;
}
if (requests.size() != 1) {
throw new IllegalStateException(
"Only a non-staged install of a single APEX is supported");
}
InstallRequest request = requests.get(0);
try {
// Should directory scanning logic be moved to ApexManager for better test coverage?
final File dir = request.getOriginInfo().mResolvedFile;
final File[] apexes = dir.listFiles();
if (apexes == null) {
throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
dir.getAbsolutePath() + " is not a directory");
}
if (apexes.length != 1) {
throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
"Expected exactly one .apex file under " + dir.getAbsolutePath()
+ " got: " + apexes.length);
}
try (PackageParser2 packageParser = mPm.mInjector.getScanningPackageParser()) {
ApexInfo apexInfo = mPm.mApexManager.installPackage(apexes[0]);
// APEX has been handled successfully by apexd. Let's continue the install flow
// so it will be scanned and registered with the system.
// TODO(b/225756739): Improve atomicity of rebootless APEX install.
// The newly installed APEX will not be reverted even if
// processApkInstallRequests() fails. Need a way to keep info stored in apexd
// and PMS in sync in the face of install failures.
request.setApexInfo(apexInfo);
mPm.mHandler.post(() -> processApkInstallRequests(true, requests));
return;
}
} catch (PackageManagerException e) {
request.setError("APEX installation failed", e);
}
PackageManagerService.invalidatePackageInfoCache();
mPm.notifyInstallObserver(request);
}
/**
* Container for a multi-package install which refers to all install sessions and args being
* committed together.
*/
private class MultiPackageInstallingSession {
private final List<InstallingSession> mChildInstallingSessions;
private final Set<InstallRequest> mCurrentInstallRequests;
@NonNull
final PackageManagerService mPm;
final UserHandle mUser;
MultiPackageInstallingSession(UserHandle user,
List<InstallingSession> childInstallingSessions,
PackageManagerService pm)
throws PackageManagerException {
if (childInstallingSessions.size() == 0) {
throw new PackageManagerException("No child sessions found!");
}
mPm = pm;
mUser = user;
mChildInstallingSessions = childInstallingSessions;
for (int i = 0; i < childInstallingSessions.size(); i++) {
final InstallingSession childInstallingSession = childInstallingSessions.get(i);
childInstallingSession.mParentInstallingSession = this;
}
mCurrentInstallRequests = new ArraySet<>(mChildInstallingSessions.size());
}
public void start() {
if (DEBUG_INSTALL) Slog.i(TAG, "start " + mUser + ": " + this);
Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
System.identityHashCode(this));
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "start");
for (InstallingSession childInstallingSession : mChildInstallingSessions) {
childInstallingSession.handleStartCopy();
}
for (InstallingSession childInstallingSession : mChildInstallingSessions) {
childInstallingSession.handleReturnCode();
}
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
public void tryProcessInstallRequest(InstallRequest request) {
mCurrentInstallRequests.add(request);
if (mCurrentInstallRequests.size() != mChildInstallingSessions.size()) {
return;
}
int completeStatus = PackageManager.INSTALL_SUCCEEDED;
for (InstallRequest installRequest : mCurrentInstallRequests) {
if (installRequest.getReturnCode() == PackageManager.INSTALL_UNKNOWN) {
return;
} else if (installRequest.getReturnCode() != PackageManager.INSTALL_SUCCEEDED) {
completeStatus = installRequest.getReturnCode();
break;
}
}
final List<InstallRequest> installRequests = new ArrayList<>(
mCurrentInstallRequests.size());
for (InstallRequest installRequest : mCurrentInstallRequests) {
installRequest.setReturnCode(completeStatus);
installRequests.add(installRequest);
}
int finalCompleteStatus = completeStatus;
mPm.mHandler.post(() -> processInstallRequests(
finalCompleteStatus == PackageManager.INSTALL_SUCCEEDED /* success */,
installRequests));
}
@Override
public String toString() {
return "MultiPackageInstallingSession{" + Integer.toHexString(
System.identityHashCode(this))
+ "}";
}
}
}