Loading services/core/java/com/android/server/pm/PackageInstallerService.java +9 −3 Original line number Diff line number Diff line Loading @@ -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); } } } Loading @@ -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(); } } } Loading Loading @@ -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); } } Loading services/core/java/com/android/server/pm/PackageInstallerSession.java +45 −0 Original line number Diff line number Diff line Loading @@ -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(); Loading Loading @@ -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() { Loading Loading @@ -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; Loading Loading @@ -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 Loading Loading @@ -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) { Loading @@ -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(); } } }); } Loading Loading @@ -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; Loading Loading @@ -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(); Loading Loading @@ -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); Loading Loading @@ -5247,6 +5280,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { mPreapprovalDetails = details; setPreapprovalRemoteStatusReceiver(statusReceiver); } synchronized (mMetrics) { mMetrics.onPreapprovalSet(); } } private void dispatchPreapprovalRequest() { Loading Loading @@ -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. * Loading services/core/java/com/android/server/pm/SessionMetrics.java 0 → 100644 +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; }; } } Loading
services/core/java/com/android/server/pm/PackageInstallerService.java +9 −3 Original line number Diff line number Diff line Loading @@ -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); } } } Loading @@ -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(); } } } Loading Loading @@ -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); } } Loading
services/core/java/com/android/server/pm/PackageInstallerSession.java +45 −0 Original line number Diff line number Diff line Loading @@ -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(); Loading Loading @@ -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() { Loading Loading @@ -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; Loading Loading @@ -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 Loading Loading @@ -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) { Loading @@ -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(); } } }); } Loading Loading @@ -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; Loading Loading @@ -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(); Loading Loading @@ -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); Loading Loading @@ -5247,6 +5280,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { mPreapprovalDetails = details; setPreapprovalRemoteStatusReceiver(statusReceiver); } synchronized (mMetrics) { mMetrics.onPreapprovalSet(); } } private void dispatchPreapprovalRequest() { Loading Loading @@ -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. * Loading
services/core/java/com/android/server/pm/SessionMetrics.java 0 → 100644 +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; }; } }