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

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

Merge "[pm] add package installer session metrics" into main

parents 4796ca01 9fac2379
Loading
Loading
Loading
Loading
+9 −3
Original line number Diff line number Diff line
@@ -579,7 +579,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
            if (!valid) {
                Slog.w(TAG, "Remove old session: " + session.sessionId);
                // Remove expired sessions as well as child sessions if any
                removeActiveSession(session);
                removeActiveSession(session, /* isSessionExpired= */ true);
            }
        }
    }
@@ -589,12 +589,18 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
     * This should only be called on a root session.
     */
    @GuardedBy("mSessions")
    private void removeActiveSession(PackageInstallerSession session) {
    private void removeActiveSession(PackageInstallerSession session, boolean isSessionExpired) {
        mSessions.remove(session.sessionId);
        addHistoricalSessionLocked(session);
        if (isSessionExpired) {
            session.onSessionExpired();
        }
        for (PackageInstallerSession child : session.getChildSessions()) {
            mSessions.remove(child.sessionId);
            addHistoricalSessionLocked(child);
            if (isSessionExpired) {
                child.onSessionExpired();
            }
        }
    }

@@ -2320,7 +2326,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
                            boolean shouldRemove = !session.isStaged() || session.isDestroyed()
                                    || !session.isCommitted();
                            if (shouldRemove) {
                                removeActiveSession(session);
                                removeActiveSession(session, /* isSessionExpired= */ false);
                            }
                        }

+45 −0
Original line number Diff line number Diff line
@@ -545,6 +545,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
    @GuardedBy("mLock")
    private DomainSet mPreVerifiedDomains;

    @GuardedBy("mMetrics")
    @NonNull private final SessionMetrics mMetrics;

    private AtomicBoolean mDependencyInstallerEnabled = new AtomicBoolean();
    private AtomicInteger mMissingSharedLibraryCount = new AtomicInteger();

@@ -1290,6 +1293,10 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
                        "Archived installation can only use Streaming System DataLoader.");
            }
        }

        mMetrics = new SessionMetrics(mHandler, sessionId, userId, installerUid, params,
                createdMillis, committedMillis, committed, childSessionIds, parentSessionId,
                sessionErrorCode);
    }

    PackageInstallerHistoricalSession createHistoricalSession() {
@@ -2547,6 +2554,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
                }
                committedMillis = System.currentTimeMillis();
            }
            synchronized (mMetrics) {
                mMetrics.onSessionCommitted(committedMillis);
            }
            return true;
        } catch (PackageManagerException e) {
            throw e;
@@ -2876,6 +2886,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
            // Commit was keeping session marked as active until now; release
            // that extra refcount so session appears idle.
            deactivate();
            synchronized (mMetrics) {
                mMetrics.onUserActionIntentSent();
            }
            return;
        } else if (mUserActionRequired) {
            // If user action is required, control comes back here when the user allows
@@ -2934,6 +2947,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
    private void runExtractNativeLibraries() {
        IoThread.getHandler().post(() -> {
            try {
                synchronized (mMetrics) {
                    mMetrics.onNativeLibExtractionStarted();
                }
                List<PackageInstallerSession> children = getChildSessions();
                if (isMultiPackage()) {
                    for (PackageInstallerSession child : children) {
@@ -2954,6 +2970,10 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
                final String errorMsg = PackageManager.installStatusToString(e.error, completeMsg);
                setSessionFailed(e.error, errorMsg);
                onSessionVerificationFailure(e.error, errorMsg);
            } finally {
                synchronized (mMetrics) {
                    mMetrics.onNativeLibExtractionFinished();
                }
            }
        });
    }
@@ -3135,8 +3155,14 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
        synchronized (mLock) {
            markStageDirInUseLocked();
        }
        synchronized (mMetrics) {
            mMetrics.onSessionVerificationStarted();
        }
        mSessionProvider.getSessionVerifier().verify(this, (error, msg) -> {
            mHandler.post(() -> {
                synchronized (mMetrics) {
                    mMetrics.onSessionVerificationFinished();
                }
                if (dispatchPendingAbandonCallback()) {
                    // No need to continue if abandoned
                    return;
@@ -3166,6 +3192,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
     * @return a future that will be completed when the whole process is completed.
     */
    private CompletableFuture<Void> install() {
        synchronized (mMetrics) {
            mMetrics.onInternalInstallationStarted();
        }
        // `futures` either contains only one session (`this`) or contains one parent session
        // (`this`) and n-1 child sessions.
        List<CompletableFuture<InstallResult>> futures = installNonStaged();
@@ -5162,6 +5191,10 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
            mFinalStatus = returnCode;
            mFinalMessage = msg;
        }
        synchronized (mMetrics) {
            mMetrics.onInternalInstallationFinished();
            mMetrics.onSessionFinished(returnCode);
        }

        final boolean success = (returnCode == INSTALL_SUCCEEDED);

@@ -5247,6 +5280,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
            mPreapprovalDetails = details;
            setPreapprovalRemoteStatusReceiver(statusReceiver);
        }
        synchronized (mMetrics) {
            mMetrics.onPreapprovalSet();
        }
    }

    private void dispatchPreapprovalRequest() {
@@ -5909,6 +5945,15 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
                || (!isReady && !isApplied && isFailed);
    }

    /**
     * Called to log the metrics about a session being removed due to expiration.
     */
    public void onSessionExpired() {
        synchronized (mMetrics) {
            mMetrics.onSessionExpired();
        }
    }

    /**
     * Read new session from a {@link TypedXmlPullParser xml description} and create it.
     *
+275 −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 static android.content.pm.PackageManager.installStatusToPublicStatus;

import android.annotation.Nullable;
import android.content.pm.DataLoaderType;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.os.Handler;

import com.android.internal.util.FrameworkStatsLog;

import java.util.Arrays;

final class SessionMetrics {
    private static final String TAG = "SessionMetrics";
    private final Handler mHandler;
    private final int mSessionId;
    private final int mUserId;
    private final int mInstallerUid;
    @Nullable
    private final int[] mChildSessionIds;
    private final int mParentSessionId;

    private final long mCreatedMillis;
    private long mCommittedMillis;
    private long mNativeLibExtractionStartedMillis;
    private long mNativeLibExtractionFinishedMillis;
    private long mVerificationStartedMillis;
    private long mVerificationFinishedMillis;
    private long mInternalInstallationStarted;
    private long mInternalInstallationFinished;
    private long mFinishedMillis;
    private int mStatusCode;
    private boolean mIsExpired = false;

    private final int mMode;
    private final int mRequireUserAction;
    private final int mInstallFlags;
    private final int mInstallLocation;
    private final int mInstallReason;
    private final int mInstallScenario;
    private final boolean mIsStaged;
    private final long mRequiredInstalledVersionCode;
    private final int mDataLoaderType;
    private final int mRollbackDataPolicy;
    private final long mRollbackLifetimeMillis;
    private final int mRollbackImpactLevel;
    private final boolean mForceQueryableOverride;
    private final boolean mApplicationEnabledSettingPersistent;
    private final boolean mIsMultiPackage;
    private boolean mIsPreapproval;
    private final boolean mIsUnarchive;
    private final boolean mIsAutoInstallDependenciesEnabled;
    private long mApksSizeBytes;
    private boolean mWasUserActionIntentSent;

    SessionMetrics(Handler handler,
            int sessionId, int userId, int installerUid,
            PackageInstaller.SessionParams params, long createdMillis, long committedMillis,
            boolean committed, @Nullable int[] childSessionIds, int parentSessionId,
            int sessionStatusCode) {
        mHandler = handler;
        mSessionId = sessionId;
        mUserId = userId;
        mInstallerUid = installerUid;
        mChildSessionIds = childSessionIds == null
                ? null : Arrays.copyOf(childSessionIds, childSessionIds.length);
        mParentSessionId = parentSessionId;
        mCreatedMillis = createdMillis;
        mCommittedMillis = committed ? committedMillis : 0;
        mStatusCode = sessionStatusCode;

        mMode = params.mode;
        mRequireUserAction = params.requireUserAction;
        mInstallFlags = params.installFlags;
        mInstallLocation = params.installLocation;
        mInstallReason = params.installReason;
        mInstallScenario = params.installScenario;
        mIsStaged = params.isStaged;
        mRequiredInstalledVersionCode = params.requiredInstalledVersionCode;
        mDataLoaderType = params.dataLoaderParams == null
                ? DataLoaderType.NONE : params.dataLoaderParams.getType();
        mRollbackDataPolicy = params.rollbackDataPolicy;
        mRollbackLifetimeMillis = params.rollbackLifetimeMillis;
        mRollbackImpactLevel = params.rollbackImpactLevel;
        mForceQueryableOverride = params.forceQueryableOverride;
        mApplicationEnabledSettingPersistent = params.applicationEnabledSettingPersistent;
        mIsMultiPackage = params.isMultiPackage;
        mIsUnarchive = params.unarchiveId != PackageInstaller.SessionInfo.INVALID_ID;
        mIsAutoInstallDependenciesEnabled = params.isAutoInstallDependenciesEnabled;
    }

    public void onPreapprovalSet() {
        mIsPreapproval = true;
    }

    public void onUserActionIntentSent() {
        mWasUserActionIntentSent = true;
    }

    public void onSessionCommitted(long committedMillis) {
        mCommittedMillis = committedMillis;
    }

    public void onNativeLibExtractionStarted() {
        mNativeLibExtractionStartedMillis = System.currentTimeMillis();
    }

    public void onNativeLibExtractionFinished() {
        mNativeLibExtractionFinishedMillis = System.currentTimeMillis();
    }

    public void onSessionVerificationStarted() {
        mVerificationStartedMillis = System.currentTimeMillis();
    }

    public void onSessionVerificationFinished() {
        mVerificationFinishedMillis = System.currentTimeMillis();
    }

    public void onInternalInstallationStarted() {
        mInternalInstallationStarted = System.currentTimeMillis();
    }

    public void onInternalInstallationFinished() {
        mInternalInstallationFinished = System.currentTimeMillis();
    }

    public void onSessionFinished(int statusCode) {
        mStatusCode = statusCode;
        mFinishedMillis = System.currentTimeMillis();
        reportStats();
    }

    public void onSessionExpired() {
        mFinishedMillis = System.currentTimeMillis();
        mIsExpired = true;
        reportStats();
    }

    private void reportStats() {
        final long sessionIdleDurationMillis = mCommittedMillis - mCreatedMillis;
        final long sessionCommitDurationMillis = mFinishedMillis - mCommittedMillis;
        final long nativeLibExtractionDurationMillis =
                mNativeLibExtractionFinishedMillis - mNativeLibExtractionStartedMillis;
        final long packageVerificationDurationMillis =
                mVerificationFinishedMillis - mVerificationStartedMillis;
        final long internalInstallationDurationMillis =
                mInternalInstallationFinished - mInternalInstallationStarted;
        final long sessionLifetimeMillis = mFinishedMillis - mCreatedMillis;
        // Do this on a handler so that we don't block anything critical
        mHandler.post(() ->
                FrameworkStatsLog.write(
                        FrameworkStatsLog.PACKAGE_INSTALLER_SESSION_REPORTED,
                        mSessionId,
                        mUserId,
                        mInstallerUid,
                        mChildSessionIds,
                        mParentSessionId,
                        getTranslatedModeForStats(mMode),
                        mRequireUserAction,
                        mInstallFlags,
                        mInstallLocation,
                        mInstallReason,
                        mInstallScenario,
                        mIsStaged,
                        mRequiredInstalledVersionCode,
                        mDataLoaderType,
                        getTranslatedRollbackDataPolicyForStats(mRollbackDataPolicy),
                        mRollbackLifetimeMillis,
                        getTranslatedRollbackImpactLevelForStats(mRollbackImpactLevel),
                        mForceQueryableOverride,
                        mApplicationEnabledSettingPersistent,
                        mIsMultiPackage,
                        mIsPreapproval,
                        mIsUnarchive,
                        mIsAutoInstallDependenciesEnabled,
                        mApksSizeBytes, // TODO: compute apks size bytes
                        getTranslatedStatusCodeForStats(installStatusToPublicStatus(mStatusCode)),
                        mWasUserActionIntentSent,
                        mIsExpired,
                        sessionIdleDurationMillis,
                        sessionCommitDurationMillis,
                        nativeLibExtractionDurationMillis,
                        packageVerificationDurationMillis,
                        internalInstallationDurationMillis,
                        sessionLifetimeMillis
                )
        );
    }

    public int getTranslatedModeForStats(int mode) {
        return switch (mode) {
            case PackageInstaller.SessionParams.MODE_INVALID ->
                    FrameworkStatsLog.PACKAGE_INSTALLER_SESSION_REPORTED__MODE__MODE_INVALID;
            case PackageInstaller.SessionParams.MODE_FULL_INSTALL ->
                    FrameworkStatsLog.PACKAGE_INSTALLER_SESSION_REPORTED__MODE__MODE_FULL_INSTALL;
            case PackageInstaller.SessionParams.MODE_INHERIT_EXISTING ->
                    FrameworkStatsLog.PACKAGE_INSTALLER_SESSION_REPORTED__MODE__MODE_INHERIT_EXISTING;
            default -> FrameworkStatsLog.PACKAGE_INSTALLER_SESSION_REPORTED__MODE__MODE_UNSPECIFIED;
        };
    }

    public int getTranslatedRollbackDataPolicyForStats(int rollbackDataPolicy) {
        return switch (rollbackDataPolicy) {
            case PackageManager.ROLLBACK_DATA_POLICY_RESTORE ->
                    FrameworkStatsLog.PACKAGE_INSTALLER_SESSION_REPORTED__ROLLBACK_DATA_POLICY__ROLLBACK_DATA_POLICY_RESTORE;
            case PackageManager.ROLLBACK_DATA_POLICY_WIPE ->
                    FrameworkStatsLog.PACKAGE_INSTALLER_SESSION_REPORTED__ROLLBACK_DATA_POLICY__ROLLBACK_DATA_POLICY_WIPE;
            case PackageManager.ROLLBACK_DATA_POLICY_RETAIN ->
                    FrameworkStatsLog.PACKAGE_INSTALLER_SESSION_REPORTED__ROLLBACK_DATA_POLICY__ROLLBACK_DATA_POLICY_RETAIN;
            default ->
                    FrameworkStatsLog.PACKAGE_INSTALLER_SESSION_REPORTED__ROLLBACK_DATA_POLICY__ROLLBACK_DATA_POLICY_UNSPECIFIED;
        };
    }

    public int getTranslatedRollbackImpactLevelForStats(int rollbackImpactLevel) {
        return switch (rollbackImpactLevel) {
            case PackageManager.ROLLBACK_USER_IMPACT_LOW ->
                    FrameworkStatsLog.PACKAGE_INSTALLER_SESSION_REPORTED__ROLLBACK_IMPACT_LEVEL__ROLLBACK_USER_IMPACT_LOW;
            case PackageManager.ROLLBACK_USER_IMPACT_HIGH ->
                    FrameworkStatsLog.PACKAGE_INSTALLER_SESSION_REPORTED__ROLLBACK_IMPACT_LEVEL__ROLLBACK_USER_IMPACT_HIGH;
            case PackageManager.ROLLBACK_USER_IMPACT_ONLY_MANUAL ->
                    FrameworkStatsLog.PACKAGE_INSTALLER_SESSION_REPORTED__ROLLBACK_IMPACT_LEVEL__ROLLBACK_USER_IMPACT_ONLY_MANUAL;
            default ->
                    FrameworkStatsLog.PACKAGE_INSTALLER_SESSION_REPORTED__ROLLBACK_IMPACT_LEVEL__ROLLBACK_USER_IMPACT_UNSPECIFIED;
        };
    }

    private static int getTranslatedStatusCodeForStats(int statusCode) {
        return switch (statusCode) {
            case PackageInstaller.STATUS_PENDING_STREAMING ->
                    FrameworkStatsLog.PACKAGE_INSTALLER_SESSION_REPORTED__STATUS_CODE__STATUS_PENDING_STREAMING;
            case PackageInstaller.STATUS_PENDING_USER_ACTION ->
                    FrameworkStatsLog.PACKAGE_INSTALLER_SESSION_REPORTED__STATUS_CODE__STATUS_PENDING_USER_ACTION;
            case PackageInstaller.STATUS_SUCCESS ->
                    FrameworkStatsLog.PACKAGE_INSTALLER_SESSION_REPORTED__STATUS_CODE__STATUS_SUCCESS;
            case PackageInstaller.STATUS_FAILURE ->
                    FrameworkStatsLog.PACKAGE_INSTALLER_SESSION_REPORTED__STATUS_CODE__STATUS_FAILURE;
            case PackageInstaller.STATUS_FAILURE_BLOCKED ->
                    FrameworkStatsLog.PACKAGE_INSTALLER_SESSION_REPORTED__STATUS_CODE__STATUS_FAILURE_BLOCKED;
            case PackageInstaller.STATUS_FAILURE_ABORTED ->
                    FrameworkStatsLog.PACKAGE_INSTALLER_SESSION_REPORTED__STATUS_CODE__STATUS_FAILURE_ABORTED;
            case PackageInstaller.STATUS_FAILURE_INVALID ->
                    FrameworkStatsLog.PACKAGE_INSTALLER_SESSION_REPORTED__STATUS_CODE__STATUS_FAILURE_INVALID;
            case PackageInstaller.STATUS_FAILURE_CONFLICT ->
                    FrameworkStatsLog.PACKAGE_INSTALLER_SESSION_REPORTED__STATUS_CODE__STATUS_FAILURE_CONFLICT;
            case PackageInstaller.STATUS_FAILURE_STORAGE ->
                    FrameworkStatsLog.PACKAGE_INSTALLER_SESSION_REPORTED__STATUS_CODE__STATUS_FAILURE_STORAGE;
            case PackageInstaller.STATUS_FAILURE_INCOMPATIBLE ->
                    FrameworkStatsLog.PACKAGE_INSTALLER_SESSION_REPORTED__STATUS_CODE__STATUS_FAILURE_INCOMPATIBLE;
            case PackageInstaller.STATUS_FAILURE_TIMEOUT ->
                    FrameworkStatsLog.PACKAGE_INSTALLER_SESSION_REPORTED__STATUS_CODE__STATUS_FAILURE_TIMEOUT;
            default ->
                    FrameworkStatsLog.PACKAGE_INSTALLER_SESSION_REPORTED__STATUS_CODE__STATUS_UNSPECIFIED;
        };
    }
}