diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java index 7ea0c04562e26..84b8264caf6a7 100644 --- a/services/core/java/com/android/server/pm/InstallPackageHelper.java +++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java @@ -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()); } diff --git a/services/core/java/com/android/server/pm/InstallRequest.java b/services/core/java/com/android/server/pm/InstallRequest.java index 573082a58255f..4443710dd08bc 100644 --- a/services/core/java/com/android/server/pm/InstallRequest.java +++ b/services/core/java/com/android/server/pm/InstallRequest.java @@ -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(); + } + } + } } diff --git a/services/core/java/com/android/server/pm/InstallingSession.java b/services/core/java/com/android/server/pm/InstallingSession.java index 88469f539f940..d13822a4d98c1 100644 --- a/services/core/java/com/android/server/pm/InstallingSession.java +++ b/services/core/java/com/android/server/pm/InstallingSession.java @@ -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 { diff --git a/services/core/java/com/android/server/pm/PackageHandler.java b/services/core/java/com/android/server/pm/PackageHandler.java index 4e8b0a14ae1d3..66ef93d121911 100644 --- a/services/core/java/com/android/server/pm/PackageHandler.java +++ b/services/core/java/com/android/server/pm/PackageHandler.java @@ -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 diff --git a/services/core/java/com/android/server/pm/PackageMetrics.java b/services/core/java/com/android/server/pm/PackageMetrics.java new file mode 100644 index 0000000000000..b725325eaaa98 --- /dev/null +++ b/services/core/java/com/android/server/pm/PackageMetrics.java @@ -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 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 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 getInstallStepDurations() { + ArrayList steps = new ArrayList<>(); + ArrayList 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; + } + } +} diff --git a/services/core/java/com/android/server/pm/ReconcilePackageUtils.java b/services/core/java/com/android/server/pm/ReconcilePackageUtils.java index ffce69e00d24f..01d17f6444f51 100644 --- a/services/core/java/com/android/server/pm/ReconcilePackageUtils.java +++ b/services/core/java/com/android/server/pm/ReconcilePackageUtils.java @@ -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; }