Merge "[pm/metrics][1/n] generating atoms for successful apk installs"
This commit is contained in:
committed by
Android (Google) Code Review
commit
95f9a35ea5
@@ -427,7 +427,7 @@ final class InstallPackageHelper {
|
||||
mPm.snapshotComputer().checkPackageFrozen(pkgName);
|
||||
}
|
||||
|
||||
final boolean isReplace = request.isReplace();
|
||||
final boolean isReplace = request.isInstallReplace();
|
||||
// Also need to kill any apps that are dependent on the library, except the case of
|
||||
// installation of new version static shared library.
|
||||
if (clientLibPkgs != null) {
|
||||
@@ -814,6 +814,7 @@ final class InstallPackageHelper {
|
||||
for (InstallRequest request : requests) {
|
||||
try {
|
||||
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "preparePackage");
|
||||
request.onPrepareStarted();
|
||||
preparePackageLI(request);
|
||||
} catch (PrepareFailure prepareFailure) {
|
||||
request.setError(prepareFailure.error,
|
||||
@@ -822,6 +823,7 @@ final class InstallPackageHelper {
|
||||
request.setOriginPermission(prepareFailure.mConflictingPermission);
|
||||
return;
|
||||
} finally {
|
||||
request.onPrepareFinished();
|
||||
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
|
||||
}
|
||||
|
||||
@@ -834,11 +836,13 @@ final class InstallPackageHelper {
|
||||
request.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
|
||||
final String packageName = packageToScan.getPackageName();
|
||||
try {
|
||||
request.onScanStarted();
|
||||
final ScanResult scanResult = scanPackageTracedLI(request.getParsedPackage(),
|
||||
request.getParseFlags(), request.getScanFlags(),
|
||||
System.currentTimeMillis(), request.getUser(),
|
||||
request.getAbiOverride());
|
||||
request.setScanResult(scanResult);
|
||||
request.onScanFinished();
|
||||
if (!scannedPackages.add(packageName)) {
|
||||
request.setError(
|
||||
PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE,
|
||||
@@ -966,7 +970,7 @@ final class InstallPackageHelper {
|
||||
final boolean isRollback =
|
||||
request.getInstallReason() == PackageManager.INSTALL_REASON_ROLLBACK;
|
||||
@PackageManagerService.ScanFlags int scanFlags = SCAN_NEW_INSTALL | SCAN_UPDATE_SIGNATURE;
|
||||
if (request.isMoveInstall()) {
|
||||
if (request.isInstallMove()) {
|
||||
// moving a complete application; perform an initial scan on the new install location
|
||||
scanFlags |= SCAN_INITIAL;
|
||||
}
|
||||
@@ -1349,7 +1353,7 @@ final class InstallPackageHelper {
|
||||
}
|
||||
}
|
||||
|
||||
if (request.isMoveInstall()) {
|
||||
if (request.isInstallMove()) {
|
||||
// We did an in-place move, so dex is ready to roll
|
||||
scanFlags |= SCAN_NO_DEX;
|
||||
scanFlags |= SCAN_MOVE;
|
||||
@@ -1668,7 +1672,7 @@ final class InstallPackageHelper {
|
||||
ParsedPackage parsedPackage) throws PrepareFailure {
|
||||
final int status = request.getReturnCode();
|
||||
final String statusMsg = request.getReturnMsg();
|
||||
if (request.isMoveInstall()) {
|
||||
if (request.isInstallMove()) {
|
||||
if (status != PackageManager.INSTALL_SUCCEEDED) {
|
||||
mRemovePackageHelper.cleanUpForMoveInstall(request.getMoveToUuid(),
|
||||
request.getMovePackageName(), request.getMoveFromCodePath());
|
||||
@@ -1888,7 +1892,8 @@ final class InstallPackageHelper {
|
||||
final RemovePackageHelper removePackageHelper = new RemovePackageHelper(mPm);
|
||||
final DeletePackageHelper deletePackageHelper = new DeletePackageHelper(mPm);
|
||||
|
||||
if (installRequest.isReplace()) {
|
||||
installRequest.onCommitStarted();
|
||||
if (installRequest.isInstallReplace()) {
|
||||
AndroidPackage oldPackage = mPm.mPackages.get(packageName);
|
||||
|
||||
// Set the update and install times
|
||||
@@ -1905,7 +1910,7 @@ final class InstallPackageHelper {
|
||||
mPm.mAppsFilter.getVisibilityAllowList(mPm.snapshotComputer(),
|
||||
installRequest.getScannedPackageSetting(),
|
||||
allUsers, mPm.mSettings.getPackagesLocked());
|
||||
if (installRequest.isSystem()) {
|
||||
if (installRequest.isInstallSystem()) {
|
||||
// Remove existing system package
|
||||
removePackageHelper.removePackage(oldPackage, true);
|
||||
if (!disableSystemPackageLPw(oldPackage)) {
|
||||
@@ -1973,6 +1978,7 @@ final class InstallPackageHelper {
|
||||
mPm.updateSequenceNumberLP(ps, installRequest.getNewUsers());
|
||||
mPm.updateInstantAppInstallerLocked(packageName);
|
||||
}
|
||||
installRequest.onCommitFinished();
|
||||
}
|
||||
ApplicationPackageManager.invalidateGetPackagesForUidCache();
|
||||
}
|
||||
@@ -2223,7 +2229,7 @@ final class InstallPackageHelper {
|
||||
FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL
|
||||
| Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
|
||||
}
|
||||
if (installRequest.isReplace()) {
|
||||
if (installRequest.isInstallReplace()) {
|
||||
mDexManager.notifyPackageUpdated(pkg.getPackageName(),
|
||||
pkg.getBaseApkPath(), pkg.getSplitCodePaths());
|
||||
}
|
||||
|
||||
@@ -18,8 +18,10 @@ package com.android.server.pm;
|
||||
|
||||
import static android.content.pm.PackageManager.INSTALL_REASON_UNKNOWN;
|
||||
import static android.content.pm.PackageManager.INSTALL_SCENARIO_DEFAULT;
|
||||
import static android.content.pm.PackageManager.INSTALL_SUCCEEDED;
|
||||
import static android.os.Process.INVALID_UID;
|
||||
|
||||
import static com.android.server.pm.PackageManagerService.SCAN_AS_INSTANT_APP;
|
||||
import static com.android.server.pm.PackageManagerService.TAG;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
@@ -105,6 +107,12 @@ final class InstallRequest {
|
||||
@Nullable
|
||||
private ScanResult mScanResult;
|
||||
|
||||
private boolean mIsInstallInherit;
|
||||
private boolean mIsInstallForUsers;
|
||||
|
||||
@Nullable
|
||||
private final PackageMetrics mPackageMetrics;
|
||||
|
||||
// New install
|
||||
InstallRequest(InstallingSession params) {
|
||||
mUserId = params.getUser().getIdentifier();
|
||||
@@ -116,6 +124,8 @@ final class InstallRequest {
|
||||
params.mTraceMethod, params.mTraceCookie, params.mSigningDetails,
|
||||
params.mInstallReason, params.mInstallScenario, params.mForceQueryableOverride,
|
||||
params.mDataLoaderType, params.mPackageSource);
|
||||
mPackageMetrics = new PackageMetrics(this);
|
||||
mIsInstallInherit = params.mIsInherit;
|
||||
}
|
||||
|
||||
// Install existing package as user
|
||||
@@ -127,6 +137,8 @@ final class InstallRequest {
|
||||
mPkg = pkg;
|
||||
mNewUsers = newUsers;
|
||||
mPostInstallRunnable = runnable;
|
||||
mPackageMetrics = new PackageMetrics(this);
|
||||
mIsInstallForUsers = true;
|
||||
}
|
||||
|
||||
// addForInit
|
||||
@@ -143,6 +155,7 @@ final class InstallRequest {
|
||||
mParseFlags = parseFlags;
|
||||
mScanFlags = scanFlags;
|
||||
mScanResult = scanResult;
|
||||
mPackageMetrics = null; // No real logging from this code path
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -200,7 +213,7 @@ final class InstallRequest {
|
||||
return mInstallArgs == null ? null : mInstallArgs.mObserver;
|
||||
}
|
||||
|
||||
public boolean isMoveInstall() {
|
||||
public boolean isInstallMove() {
|
||||
return mInstallArgs != null && mInstallArgs.mMoveInfo != null;
|
||||
}
|
||||
|
||||
@@ -278,7 +291,7 @@ final class InstallRequest {
|
||||
return mRemovedInfo != null ? mRemovedInfo.mRemovedPackage : null;
|
||||
}
|
||||
|
||||
public boolean isInstallForExistingUser() {
|
||||
public boolean isInstallExistingForUser() {
|
||||
return mInstallArgs == null;
|
||||
}
|
||||
|
||||
@@ -406,14 +419,22 @@ final class InstallRequest {
|
||||
return mClearCodeCache;
|
||||
}
|
||||
|
||||
public boolean isReplace() {
|
||||
public boolean isInstallReplace() {
|
||||
return mReplace;
|
||||
}
|
||||
|
||||
public boolean isSystem() {
|
||||
public boolean isInstallSystem() {
|
||||
return mSystem;
|
||||
}
|
||||
|
||||
public boolean isInstallInherit() {
|
||||
return mIsInstallInherit;
|
||||
}
|
||||
|
||||
public boolean isInstallForUsers() {
|
||||
return mIsInstallForUsers;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public PackageSetting getOriginalPackageSetting() {
|
||||
return mOriginalPs;
|
||||
@@ -520,6 +541,10 @@ final class InstallRequest {
|
||||
return mScanResult.mRequest.mIsPlatformPackage;
|
||||
}
|
||||
|
||||
public boolean isInstantInstall() {
|
||||
return (mScanFlags & SCAN_AS_INSTANT_APP) != 0;
|
||||
}
|
||||
|
||||
public void assertScanResultExists() {
|
||||
if (mScanResult == null) {
|
||||
// Should not happen. This indicates a bug in the installation code flow
|
||||
@@ -529,7 +554,6 @@ final class InstallRequest {
|
||||
Slog.e(TAG, "ScanResult is null and it should not happen");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void setScanFlags(int scanFlags) {
|
||||
@@ -658,4 +682,60 @@ final class InstallRequest {
|
||||
mRemovedInfo.mRemovedAppId = appId;
|
||||
}
|
||||
}
|
||||
|
||||
public void onPrepareStarted() {
|
||||
if (mPackageMetrics != null) {
|
||||
mPackageMetrics.onStepStarted(PackageMetrics.STEP_PREPARE);
|
||||
}
|
||||
}
|
||||
|
||||
public void onPrepareFinished() {
|
||||
if (mPackageMetrics != null) {
|
||||
mPackageMetrics.onStepFinished(PackageMetrics.STEP_PREPARE);
|
||||
}
|
||||
}
|
||||
|
||||
public void onScanStarted() {
|
||||
if (mPackageMetrics != null) {
|
||||
mPackageMetrics.onStepStarted(PackageMetrics.STEP_SCAN);
|
||||
}
|
||||
}
|
||||
|
||||
public void onScanFinished() {
|
||||
if (mPackageMetrics != null) {
|
||||
mPackageMetrics.onStepFinished(PackageMetrics.STEP_SCAN);
|
||||
}
|
||||
}
|
||||
|
||||
public void onReconcileStarted() {
|
||||
if (mPackageMetrics != null) {
|
||||
mPackageMetrics.onStepStarted(PackageMetrics.STEP_RECONCILE);
|
||||
}
|
||||
}
|
||||
|
||||
public void onReconcileFinished() {
|
||||
if (mPackageMetrics != null) {
|
||||
mPackageMetrics.onStepFinished(PackageMetrics.STEP_RECONCILE);
|
||||
}
|
||||
}
|
||||
|
||||
public void onCommitStarted() {
|
||||
if (mPackageMetrics != null) {
|
||||
mPackageMetrics.onStepStarted(PackageMetrics.STEP_COMMIT);
|
||||
}
|
||||
}
|
||||
|
||||
public void onCommitFinished() {
|
||||
if (mPackageMetrics != null) {
|
||||
mPackageMetrics.onStepFinished(PackageMetrics.STEP_COMMIT);
|
||||
}
|
||||
}
|
||||
|
||||
public void onInstallCompleted() {
|
||||
if (getReturnCode() == INSTALL_SUCCEEDED) {
|
||||
if (mPackageMetrics != null) {
|
||||
mPackageMetrics.onInstallSucceed();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
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;
|
||||
@@ -93,6 +94,7 @@ class InstallingSession {
|
||||
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,
|
||||
@@ -121,6 +123,7 @@ class InstallingSession {
|
||||
mRequiredInstalledVersionCode = PackageManager.VERSION_CODE_HIGHEST;
|
||||
mPackageSource = packageSource;
|
||||
mPackageLite = packageLite;
|
||||
mIsInherit = false;
|
||||
}
|
||||
|
||||
InstallingSession(File stagedDir, IPackageInstallObserver2 observer,
|
||||
@@ -151,6 +154,7 @@ class InstallingSession {
|
||||
mRequiredInstalledVersionCode = sessionParams.requiredInstalledVersionCode;
|
||||
mPackageSource = sessionParams.packageSource;
|
||||
mPackageLite = packageLite;
|
||||
mIsInherit = sessionParams.mode == MODE_INHERIT_EXISTING;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -506,6 +510,7 @@ class InstallingSession {
|
||||
mInstallPackageHelper.installPackagesTraced(installRequests);
|
||||
|
||||
for (InstallRequest request : installRequests) {
|
||||
request.onInstallCompleted();
|
||||
doPostInstall(request);
|
||||
}
|
||||
}
|
||||
@@ -531,7 +536,7 @@ class InstallingSession {
|
||||
}
|
||||
|
||||
private void cleanUpForFailedInstall(InstallRequest request) {
|
||||
if (request.isMoveInstall()) {
|
||||
if (request.isInstallMove()) {
|
||||
mRemovePackageHelper.cleanUpForMoveInstall(request.getMoveToUuid(),
|
||||
request.getMovePackageName(), request.getMoveFromCodePath());
|
||||
} else {
|
||||
|
||||
@@ -95,7 +95,7 @@ final class PackageHandler extends Handler {
|
||||
|
||||
request.closeFreezer();
|
||||
request.runPostInstallRunnable();
|
||||
if (!request.isInstallForExistingUser()) {
|
||||
if (!request.isInstallExistingForUser()) {
|
||||
mInstallPackageHelper.handlePackagePostInstall(request, didRestore);
|
||||
} else if (DEBUG_INSTALL) {
|
||||
// No post-install when we run restore from installExistingPackageForUser
|
||||
|
||||
153
services/core/java/com/android/server/pm/PackageMetrics.java
Normal file
153
services/core/java/com/android/server/pm/PackageMetrics.java
Normal file
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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 android.annotation.IntDef;
|
||||
import android.os.UserManager;
|
||||
import android.util.Pair;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import com.android.internal.util.FrameworkStatsLog;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Metrics class for reporting stats to logging infrastructures like Westworld
|
||||
*/
|
||||
final class PackageMetrics {
|
||||
public static final int STEP_PREPARE = 1;
|
||||
public static final int STEP_SCAN = 2;
|
||||
public static final int STEP_RECONCILE = 3;
|
||||
public static final int STEP_COMMIT = 4;
|
||||
|
||||
@IntDef(prefix = {"STEP_"}, value = {
|
||||
STEP_PREPARE,
|
||||
STEP_SCAN,
|
||||
STEP_RECONCILE,
|
||||
STEP_COMMIT,
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface StepInt {}
|
||||
|
||||
private final long mInstallStartTimestampMillis;
|
||||
private final SparseArray<InstallStep> mInstallSteps = new SparseArray<>();
|
||||
private final InstallRequest mInstallRequest;
|
||||
|
||||
PackageMetrics(InstallRequest installRequest) {
|
||||
// New instance is used for tracking installation metrics only.
|
||||
// Other metrics should use static methods of this class.
|
||||
mInstallStartTimestampMillis = System.currentTimeMillis();
|
||||
mInstallRequest = installRequest;
|
||||
}
|
||||
|
||||
public void onInstallSucceed() {
|
||||
final long installDurationMillis =
|
||||
System.currentTimeMillis() - mInstallStartTimestampMillis;
|
||||
// write to stats
|
||||
final Pair<int[], long[]> stepDurations = getInstallStepDurations();
|
||||
final int[] newUsers = mInstallRequest.getNewUsers();
|
||||
final int[] originalUsers = mInstallRequest.getOriginUsers();
|
||||
FrameworkStatsLog.write(FrameworkStatsLog.PACKAGE_INSTALLATION_SESSION_REPORTED,
|
||||
0 /* session_id */,
|
||||
null /* package_name */,
|
||||
mInstallRequest.getUid() /* uid */,
|
||||
newUsers /* user_ids */,
|
||||
getUserTypes(newUsers) /* user_types */,
|
||||
originalUsers /* original_user_ids */,
|
||||
getUserTypes(originalUsers) /* original_user_types */,
|
||||
mInstallRequest.getReturnCode() /* public_return_code */,
|
||||
0 /* internal_error_code */,
|
||||
0 /* apks_size_bytes */,
|
||||
0 /* version_code */,
|
||||
stepDurations.first /* install_steps */,
|
||||
stepDurations.second /* step_duration_millis */,
|
||||
installDurationMillis /* total_duration_millis */,
|
||||
mInstallRequest.getInstallFlags() /* install_flags */,
|
||||
-1 /* installer_package_uid */,
|
||||
-1 /* original_installer_package_uid */,
|
||||
mInstallRequest.getDataLoaderType() /* data_loader_type */,
|
||||
0 /* user_action_required_type */,
|
||||
mInstallRequest.isInstantInstall() /* is_instant */,
|
||||
mInstallRequest.isInstallReplace() /* is_replace */,
|
||||
mInstallRequest.isInstallSystem() /* is_system */,
|
||||
mInstallRequest.isInstallInherit() /* is_inherit */,
|
||||
mInstallRequest.isInstallForUsers() /* is_installing_existing_as_user */,
|
||||
mInstallRequest.isInstallMove() /* is_move_install */,
|
||||
false /* is_staged */
|
||||
);
|
||||
}
|
||||
|
||||
public void onStepStarted(@StepInt int step) {
|
||||
mInstallSteps.put(step, new InstallStep());
|
||||
}
|
||||
|
||||
public void onStepFinished(@StepInt int step) {
|
||||
final InstallStep installStep = mInstallSteps.get(step);
|
||||
if (installStep != null) {
|
||||
// Only valid if the start timestamp is set; otherwise no-op
|
||||
installStep.finish();
|
||||
}
|
||||
}
|
||||
|
||||
// List of steps (e.g., 1, 2, 3) and corresponding list of durations (e.g., 200ms, 100ms, 150ms)
|
||||
private Pair<int[], long[]> getInstallStepDurations() {
|
||||
ArrayList<Integer> steps = new ArrayList<>();
|
||||
ArrayList<Long> durations = new ArrayList<>();
|
||||
for (int i = 0; i < mInstallSteps.size(); i++) {
|
||||
final long duration = mInstallSteps.valueAt(i).getDurationMillis();
|
||||
if (duration >= 0) {
|
||||
steps.add(mInstallSteps.keyAt(i));
|
||||
durations.add(mInstallSteps.valueAt(i).getDurationMillis());
|
||||
}
|
||||
}
|
||||
int[] stepsArray = new int[steps.size()];
|
||||
long[] durationsArray = new long[durations.size()];
|
||||
for (int i = 0; i < stepsArray.length; i++) {
|
||||
stepsArray[i] = steps.get(i);
|
||||
durationsArray[i] = durations.get(i);
|
||||
}
|
||||
return new Pair<>(stepsArray, durationsArray);
|
||||
}
|
||||
|
||||
private static int[] getUserTypes(int[] userIds) {
|
||||
if (userIds == null) {
|
||||
return null;
|
||||
}
|
||||
final int[] userTypes = new int[userIds.length];
|
||||
for (int i = 0; i < userTypes.length; i++) {
|
||||
String userType = UserManagerService.getInstance().getUserInfo(userIds[i]).userType;
|
||||
userTypes[i] = UserManager.getUserTypeForStatsd(userType);
|
||||
}
|
||||
return userTypes;
|
||||
}
|
||||
|
||||
private static class InstallStep {
|
||||
private final long mStartTimestampMillis;
|
||||
private long mDurationMillis = -1;
|
||||
InstallStep() {
|
||||
mStartTimestampMillis = System.currentTimeMillis();
|
||||
}
|
||||
void finish() {
|
||||
mDurationMillis = System.currentTimeMillis() - mStartTimestampMillis;
|
||||
}
|
||||
long getDurationMillis() {
|
||||
return mDurationMillis;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -67,6 +67,7 @@ final class ReconcilePackageUtils {
|
||||
new ArrayMap<>();
|
||||
|
||||
for (InstallRequest installRequest : installRequests) {
|
||||
installRequest.onReconcileStarted();
|
||||
final String installPackageName = installRequest.getParsedPackage().getPackageName();
|
||||
|
||||
// add / replace existing with incoming packages
|
||||
@@ -90,7 +91,7 @@ final class ReconcilePackageUtils {
|
||||
|
||||
final DeletePackageAction deletePackageAction;
|
||||
// we only want to try to delete for non system apps
|
||||
if (installRequest.isReplace() && !installRequest.isSystem()) {
|
||||
if (installRequest.isInstallReplace() && !installRequest.isInstallSystem()) {
|
||||
final boolean killApp = (installRequest.getScanFlags() & SCAN_DONT_KILL_APP) == 0;
|
||||
final int deleteFlags = PackageManager.DELETE_KEEP_DATA
|
||||
| (killApp ? 0 : PackageManager.DELETE_DONT_KILL_APP);
|
||||
@@ -286,6 +287,10 @@ final class ReconcilePackageUtils {
|
||||
}
|
||||
}
|
||||
|
||||
for (InstallRequest installRequest : installRequests) {
|
||||
installRequest.onReconcileFinished();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user