Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 43df27f2 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "adds logging code for InitAppScanReported" into main

parents f482fa05 b7c2ccd8
Loading
Loading
Loading
Loading
+173 −0
Original line number Diff line number Diff line
/*
    * Copyright (C) 2025 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.content.pm.PackageManager;
import android.content.pm.SigningDetails.SignatureSchemeVersion;
import android.os.SystemClock;

import com.android.internal.util.FrameworkStatsLog;

/**
 * A helper class to collect and log metrics for the initial scan of a single package during system
 * boot. This class uses a builder pattern to gather metric data before logging.
 */
public final class InitAppScanMetrics {

    private boolean mIsFsiEnabled;
    private int mNumApkSplits;
    private int mSignatureSchemeVersion =
            FrameworkStatsLog.INIT_APP_SCAN_REPORTED__SIGNATURE_SCHEME_VERSION__UNKNOWN;

    private final long mTotalScanStartTimeMillis;
    private long mTotalScanDurationMillis;

    private int mInitAppScanOutcome =
            FrameworkStatsLog.INIT_APP_SCAN_REPORTED__INIT_APP_SCAN_OUTCOME__UNSPECIFIED;

    /** Starts the timer for the total scan duration. */
    public InitAppScanMetrics() {
        this.mTotalScanStartTimeMillis = SystemClock.uptimeMillis();
    }

    /**
     * Translates a package managers signature scheme version into the corresponding init app scan
     * metric signature scheme version.
     *
     * @param signatureSchemeVersion A package manager SigningDetails.SignatureScheme.* enum value.
     * @return The corresponding init app scan metric signature scheme enum value.
     */
    private static int translateToSignatureSchemeVersion(int signatureSchemeVersion) {
        switch (signatureSchemeVersion) {
            case SignatureSchemeVersion.UNKNOWN:
                return FrameworkStatsLog.INIT_APP_SCAN_REPORTED__SIGNATURE_SCHEME_VERSION__UNKNOWN;
            case SignatureSchemeVersion.JAR:
                return FrameworkStatsLog
                        .INIT_APP_SCAN_REPORTED__SIGNATURE_SCHEME_VERSION__JAR;
            case SignatureSchemeVersion.SIGNING_BLOCK_V2:
                return FrameworkStatsLog
                        .INIT_APP_SCAN_REPORTED__SIGNATURE_SCHEME_VERSION__V2;
            case SignatureSchemeVersion.SIGNING_BLOCK_V3:
                return FrameworkStatsLog
                        .INIT_APP_SCAN_REPORTED__SIGNATURE_SCHEME_VERSION__V3;
            case SignatureSchemeVersion.SIGNING_BLOCK_V4:
                return FrameworkStatsLog
                        .INIT_APP_SCAN_REPORTED__SIGNATURE_SCHEME_VERSION__V4;

            default:
                return FrameworkStatsLog
                        .INIT_APP_SCAN_REPORTED__SIGNATURE_SCHEME_VERSION__UNKNOWN;
        }
    }

    /**
     * Translates a package manager installation error code into the corresponding init app scan
     * outcome for metrics logging.
     *
     * @param returnCode A PackageManager.INSTALL_* error code.
     * @return The corresponding app scan outcome enum value.
     */
    private static int translateToInitAppScanOutcome(int returnCode) {
        switch (returnCode) {
            case PackageManager.INSTALL_SUCCEEDED:
                return FrameworkStatsLog.INIT_APP_SCAN_REPORTED__INIT_APP_SCAN_OUTCOME__SUCCESS;
            case PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES:
                return FrameworkStatsLog
                        .INIT_APP_SCAN_REPORTED__INIT_APP_SCAN_OUTCOME__FAILURE_NO_CERTIFICATES;
            case PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE:
                return FrameworkStatsLog
                        .INIT_APP_SCAN_REPORTED__INIT_APP_SCAN_OUTCOME__FAILURE_VERIFICATION;
            case PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE:
                return FrameworkStatsLog
                        .INIT_APP_SCAN_REPORTED__INIT_APP_SCAN_OUTCOME__FAILURE_UPDATE_INCOMPATIBLE;
            case PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES:
                return FrameworkStatsLog
                        .INIT_APP_SCAN_REPORTED__INIT_APP_SCAN_OUTCOME__FAILURE_INCONSISTENT_CERTIFICATES;
            case PackageManager.INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING:
                return FrameworkStatsLog
                        .INIT_APP_SCAN_REPORTED__INIT_APP_SCAN_OUTCOME__FAILURE_CERTIFICATE_ENCODING;
            case PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE:
            case PackageManager.INSTALL_FAILED_INVALID_APK:
            case PackageManager.INSTALL_FAILED_PACKAGE_CHANGED:
            case PackageManager.INSTALL_FAILED_DUPLICATE_PERMISSION:
                return FrameworkStatsLog
                        .INIT_APP_SCAN_REPORTED__INIT_APP_SCAN_OUTCOME__FAILURE_SCAN_VALIDATION;

            default:
                return FrameworkStatsLog
                        .INIT_APP_SCAN_REPORTED__INIT_APP_SCAN_OUTCOME__FAILURE_OTHER;
        }
    }

    /**
     * Sets whether the scanned package is allow-listed for FSI check.
     *
     * @param isFsiEnabled True if the package has FSI enabled, false otherwise.
     * @return This {@link InitAppScanMetrics} instance for chaining.
     */
    public InitAppScanMetrics setIsFsiEnabled(boolean isFsiEnabled) {
        this.mIsFsiEnabled = isFsiEnabled;
        return this;
    }

    /**
     * Sets the number of APK splits for the scanned package.
     *
     * @param numApkSplits number of APK splits.
     * @return This {@link InitAppScanMetrics} instance for chaining.
     */
    public InitAppScanMetrics setNumApkSplits(int numApkSplits) {
        this.mNumApkSplits = numApkSplits;
        return this;
    }

    /**
     * Sets the signature scheme version used for package verification.
     *
     * @param signatureSchemeVersion The version of the signature scheme.
     * @return This {@link InitAppScanMetrics} instance for chaining.
     */
    public InitAppScanMetrics setSignatureSchemeVersion(int signatureSchemeVersion) {
        this.mSignatureSchemeVersion =
            translateToSignatureSchemeVersion(signatureSchemeVersion);
        return this;
    }

    /**
     * Sets the final outcome of the APK scan.
     *
     * @param returnCode A PackageManager.INSTALL_* error code.
     * @return This {@link InitAppScanMetrics} instance for chaining.
     */
    public InitAppScanMetrics setInitAppScanOutcome(int returnCode) {
        this.mInitAppScanOutcome = translateToInitAppScanOutcome(returnCode);
        return this;
    }

    /** Finalizes and logs the collected metrics to FrameworkStatsLog. */
    public void log() {
        this.mTotalScanDurationMillis = SystemClock.uptimeMillis() - mTotalScanStartTimeMillis;

        FrameworkStatsLog.write(
                FrameworkStatsLog.INIT_APP_SCAN_REPORTED,
                mIsFsiEnabled,
                mNumApkSplits,
                mSignatureSchemeVersion,
                mTotalScanDurationMillis,
                mInitAppScanOutcome);
    }
}
+252 −226
Original line number Diff line number Diff line
@@ -4337,10 +4337,13 @@ final class InstallPackageHelper {
            @ParsingPackageUtils.ParseFlags int parseFlags,
            @PackageManagerService.ScanFlags int scanFlags,
            @Nullable UserHandle user) throws PackageManagerException {
        final InitAppScanMetrics metrics = new InitAppScanMetrics();
        boolean shouldLogInitAppScanMetric = false;
        try {
            final boolean scanSystemPartition =
                (parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) != 0;
        final ScanRequest initialScanRequest = prepareInitialScanRequest(parsedPackage, parseFlags,
                scanFlags, user, null);
            final ScanRequest initialScanRequest = prepareInitialScanRequest(parsedPackage,
                    parseFlags, scanFlags, user, null);
            final PackageSetting installedPkgSetting = initialScanRequest.mPkgSetting;
            final PackageSetting originalPkgSetting = initialScanRequest.mOriginalPkgSetting;
            final PackageSetting pkgSetting =
@@ -4391,6 +4394,7 @@ final class InstallPackageHelper {
                }
            } // End of mLock

            shouldLogInitAppScanMetric = !scanSystemPartition && isSystemPkgUpdated;
            final boolean newPkgChangedPaths = pkgAlreadyExists
                    && !pkgSetting.getPathString().equals(parsedPackage.getPath());
            final boolean newPkgVersionGreater = pkgAlreadyExists
@@ -4436,8 +4440,8 @@ final class InstallPackageHelper {
                // For some updated system packages, during addForInit we want to ensure the
                // PackageSetting has the correct SigningDetails compares to the original version on
                // the system partition. For the check to happen later during the /data scan, update
            // the disabled package setting per the original APK on a system partition so that it
            // can be trusted during reconcile.
                // the disabled package setting per the original APK on a system partition so that
                // it can be trusted during reconcile.
                if (needSignatureMatchToSystem(parsedPackage.getPackageName())) {
                    final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
                    final ParseResult<SigningDetails> result =
@@ -4450,22 +4454,23 @@ final class InstallPackageHelper {
                    disabledPkgSetting.setSigningDetails(result.getResult());
                }

            // In the case of a skipped package, commitReconciledScanResultLocked is not called to
            // add the object to the "live" data structures, so this is the final mutation step
            // for the package. Which means it needs to be finalized here to cache derived fields.
            // This is relevant for cases where the disabled system package is used for flags or
            // other metadata.
                // In the case of a skipped package, commitReconciledScanResultLocked is not called
                // to add the object to the "live" data structures, so this is the final mutation
                // step for the package. Which means it needs to be finalized here to cache derived
                // fields. This is relevant for cases where the disabled system package is used for
                // flags or other metadata.
                parsedPackage.hideAsFinal();
                throw PackageManagerException.ofInternalError(
                        "Package " + parsedPackage.getPackageName()
                                + " at " + parsedPackage.getPath() + " ignored: updated version "
                    + (pkgAlreadyExists ? String.valueOf(pkgSetting.getVersionCode()) : "unknown")
                                + (pkgAlreadyExists
                                        ? String.valueOf(pkgSetting.getVersionCode()) : "unknown")
                                + " better than this " + parsedPackage.getLongVersionCode(),
                        PackageManagerException.INTERNAL_ERROR_UPDATED_VERSION_BETTER_THAN_SYSTEM);
            }

        // Verify certificates against what was last scanned. Force re-collecting certificate in two
        // special cases:
            // Verify certificates against what was last scanned. Force re-collecting certificate in
            // two special cases:
            // 1) when scanning system, force re-collect only if system is upgrading.
            // 2) when scanning /data, force re-collect only if the package name is allowlisted.
            final boolean forceCollect = scanSystemPartition ? isUpgrade
@@ -4474,13 +4479,21 @@ final class InstallPackageHelper {
                Slog.d(TAG, "Force collect certificate of " + parsedPackage.getPackageName());
            }

        // APK verification can be skipped during certificate collection, only if the file is in a
        // verified partition.
            // APK verification can be skipped during certificate collection, only if the file is in
            // a verified partition.
            final boolean skipVerify = scanSystemPartition;
            ScanPackageUtils.collectCertificatesLI(pkgSetting, parsedPackage,
                    mPm.getSettingsVersionForPackage(parsedPackage), forceCollect, skipVerify,
                    mPm.isPreNMR1Upgrade());

            // Populate the InitAppScanMetrics object since all the variables are defined now.
            metrics.setIsFsiEnabled(forceCollect)
                    .setNumApkSplits(parsedPackage.getSplitCodePaths() == null
                            ? 0
                            : parsedPackage.getSplitCodePaths().length)
                    .setSignatureSchemeVersion(
                            parsedPackage.getSigningDetails().getSignatureSchemeVersion());

            // Reset profile if the application version is changed
            maybeClearProfilesForUpgradesLI(pkgSetting, parsedPackage);

@@ -4571,7 +4584,20 @@ final class InstallPackageHelper {
            final long firstInstallTime = System.currentTimeMillis();
            final ScanResult scanResult = scanPackageNew(parsedPackage, parseFlags,
                    scanFlags | SCAN_UPDATE_SIGNATURE, firstInstallTime, user, null);
            // Set scan outcome as successful for InitAppScanMetrics.
            metrics.setInitAppScanOutcome(PackageManager.INSTALL_SUCCEEDED);
            return new Pair<>(scanResult, shouldHideSystemApp);
        } catch (PackageManagerException e) {
            // Set scan outcome failure type for InitAppScanMetrics.
            metrics.setInitAppScanOutcome(e.error);
            throw e;
        } finally {
            // Finalizes the total scan duration and logs the InitAppScanMetrics metric. The metric
            // is only logged for updated system apps.
            if (shouldLogInitAppScanMetric) {
                metrics.log();
            }
        }
    }

    private static boolean hasLauncherEntry(ParsedPackage parsedPackage) {