Add new getHistoricalSessions API to PMInternal
Sessions were written to strings to be stored as a historical session to
avoid keeping references on Bitmaps and other heavy-weight objects.
Since referenes to these heavy-weight object are all in SessionParams,
we just write the SessionParams to a string into a new
PackageInstallerHistoricalSession object.
Bug: 292091934
Test: manual install and check the dump
Change-Id: I839d67dff735a8e45ff206a0c9b08a1e72353042
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 557e4ac..838aae8 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -1409,4 +1409,10 @@
*/
public abstract boolean isPackageQuarantined(@NonNull String packageName,
@UserIdInt int userId);
+
+ /**
+ * Return a list of all historical install sessions for the given user.
+ */
+ public abstract ParceledListSlice<PackageInstaller.SessionInfo> getHistoricalSessions(
+ int userId);
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerHistoricalSession.java b/services/core/java/com/android/server/pm/PackageInstallerHistoricalSession.java
new file mode 100644
index 0000000..d40a715
--- /dev/null
+++ b/services/core/java/com/android/server/pm/PackageInstallerHistoricalSession.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2023 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.PackageInstaller.PreapprovalDetails;
+import android.content.pm.PackageInstaller.SessionInfo;
+import android.content.pm.PackageInstaller.SessionParams;
+
+import com.android.internal.util.IndentingPrintWriter;
+
+import java.io.CharArrayWriter;
+import java.io.File;
+
+/**
+ * A historical session object that stores minimal session info.
+ */
+public final class PackageInstallerHistoricalSession {
+ public final int sessionId;
+ public final int userId;
+ private final String mParams;
+ private final long mCreatedMillis;
+
+ private final File mStageDir;
+ private final String mStageCid;
+
+ private final long mUpdatedMillis;
+
+ private final long mCommittedMillis;
+
+ private final int mOriginalInstallerUid;
+
+ private final String mOriginalInstallerPackageName;
+
+ private final int mInstallerUid;
+
+ private final InstallSource mInstallSource;
+
+ private final float mClientProgress;
+
+ private final float mProgress;
+ private final boolean mSealed;
+
+ private final boolean mPreapprovalRequested;
+ private final boolean mCommitted;
+
+ private final boolean mStageDirInUse;
+
+ private final boolean mPermissionsManuallyAccepted;
+
+ private final int mFinalStatus;
+ private final String mFinalMessage;
+
+ private final int mFds;
+ private final int mBridges;
+
+ private final String mPreapprovalDetails;
+ private final int mParentSessionId;
+ private final boolean mDestroyed;
+ private final int[] mChildSessionIds;
+ private final boolean mSessionApplied;
+ private final boolean mSessionReady;
+ private final boolean mSessionFailed;
+ private final int mSessionErrorCode;
+ private final String mSessionErrorMessage;
+
+ PackageInstallerHistoricalSession(int sessionId, int userId, int originalInstallerUid,
+ String originalInstallerPackageName, InstallSource installSource, int installerUid,
+ long createdMillis, long updatedMillis, long committedMillis, File stageDir,
+ String stageCid, float clientProgress, float progress, boolean committed,
+ boolean preapprovalRequested, boolean sealed, boolean permissionsManuallyAccepted,
+ boolean stageDirInUse, boolean destroyed, int fds, int bridges, int finalStatus,
+ String finalMessage, SessionParams params, int parentSessionId,
+ int[] childSessionIds, boolean sessionApplied, boolean sessionFailed,
+ boolean sessionReady, int sessionErrorCode, String sessionErrorMessage,
+ PreapprovalDetails preapprovalDetails) {
+ this.sessionId = sessionId;
+ this.userId = userId;
+ this.mOriginalInstallerUid = originalInstallerUid;
+ this.mOriginalInstallerPackageName = originalInstallerPackageName;
+ this.mInstallSource = installSource;
+ this.mInstallerUid = installerUid;
+ this.mCreatedMillis = createdMillis;
+ this.mUpdatedMillis = updatedMillis;
+ this.mCommittedMillis = committedMillis;
+ this.mStageDir = stageDir;
+ this.mStageCid = stageCid;
+ this.mClientProgress = clientProgress;
+ this.mProgress = progress;
+ this.mCommitted = committed;
+ this.mPreapprovalRequested = preapprovalRequested;
+ this.mSealed = sealed;
+ this.mPermissionsManuallyAccepted = permissionsManuallyAccepted;
+ this.mStageDirInUse = stageDirInUse;
+ this.mDestroyed = destroyed;
+ this.mFds = fds;
+ this.mBridges = bridges;
+ this.mFinalStatus = finalStatus;
+ this.mFinalMessage = finalMessage;
+
+ CharArrayWriter writer = new CharArrayWriter();
+ IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
+ params.dump(pw);
+ this.mParams = writer.toString();
+
+ this.mParentSessionId = parentSessionId;
+ this.mChildSessionIds = childSessionIds;
+ this.mSessionApplied = sessionApplied;
+ this.mSessionFailed = sessionFailed;
+ this.mSessionReady = sessionReady;
+ this.mSessionErrorCode = sessionErrorCode;
+ this.mSessionErrorMessage = sessionErrorMessage;
+ if (preapprovalDetails != null) {
+ this.mPreapprovalDetails = preapprovalDetails.toString();
+ } else {
+ this.mPreapprovalDetails = null;
+ }
+ }
+
+ void dump(IndentingPrintWriter pw) {
+ pw.println("Session " + sessionId + ":");
+ pw.increaseIndent();
+
+ pw.printPair("userId", userId);
+ pw.printPair("mOriginalInstallerUid", mOriginalInstallerUid);
+ pw.printPair("mOriginalInstallerPackageName", mOriginalInstallerPackageName);
+ pw.printPair("installerPackageName", mInstallSource.mInstallerPackageName);
+ pw.printPair("installInitiatingPackageName", mInstallSource.mInitiatingPackageName);
+ pw.printPair("installOriginatingPackageName", mInstallSource.mOriginatingPackageName);
+ pw.printPair("mInstallerUid", mInstallerUid);
+ pw.printPair("createdMillis", mCreatedMillis);
+ pw.printPair("updatedMillis", mUpdatedMillis);
+ pw.printPair("committedMillis", mCommittedMillis);
+ pw.printPair("stageDir", mStageDir);
+ pw.printPair("stageCid", mStageCid);
+ pw.println();
+
+ pw.print(mParams);
+
+ pw.printPair("mClientProgress", mClientProgress);
+ pw.printPair("mProgress", mProgress);
+ pw.printPair("mCommitted", mCommitted);
+ pw.printPair("mPreapprovalRequested", mPreapprovalRequested);
+ pw.printPair("mSealed", mSealed);
+ pw.printPair("mPermissionsManuallyAccepted", mPermissionsManuallyAccepted);
+ pw.printPair("mStageDirInUse", mStageDirInUse);
+ pw.printPair("mDestroyed", mDestroyed);
+ pw.printPair("mFds", mFds);
+ pw.printPair("mBridges", mBridges);
+ pw.printPair("mFinalStatus", mFinalStatus);
+ pw.printPair("mFinalMessage", mFinalMessage);
+ pw.printPair("mParentSessionId", mParentSessionId);
+ pw.printPair("mChildSessionIds", mChildSessionIds);
+ pw.printPair("mSessionApplied", mSessionApplied);
+ pw.printPair("mSessionFailed", mSessionFailed);
+ pw.printPair("mSessionReady", mSessionReady);
+ pw.printPair("mSessionErrorCode", mSessionErrorCode);
+ pw.printPair("mSessionErrorMessage", mSessionErrorMessage);
+ pw.printPair("mPreapprovalDetails", mPreapprovalDetails);
+ pw.println();
+
+ pw.decreaseIndent();
+ }
+
+ /**
+ * Generates a {@link SessionInfo} object.
+ */
+ public SessionInfo generateInfo() {
+ final SessionInfo info = new SessionInfo();
+ info.sessionId = sessionId;
+ info.userId = userId;
+ info.installerPackageName = mInstallSource.mInstallerPackageName;
+ info.installerAttributionTag = mInstallSource.mInstallerAttributionTag;
+ info.progress = mProgress;
+ info.sealed = mSealed;
+ info.isCommitted = mCommitted;
+ info.isPreapprovalRequested = mPreapprovalRequested;
+
+ info.parentSessionId = mParentSessionId;
+ info.childSessionIds = mChildSessionIds;
+ info.isSessionApplied = mSessionApplied;
+ info.isSessionReady = mSessionReady;
+ info.isSessionFailed = mSessionFailed;
+ info.setSessionErrorCode(mSessionErrorCode, mSessionErrorMessage);
+ info.createdMillis = mCreatedMillis;
+ info.updatedMillis = mUpdatedMillis;
+ info.installerUid = mInstallerUid;
+ return info;
+ }
+}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 10cd51a..e360256 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -111,7 +111,6 @@
import org.xmlpull.v1.XmlPullParserException;
-import java.io.CharArrayWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
@@ -230,7 +229,7 @@
/** Historical sessions kept around for debugging purposes */
@GuardedBy("mSessions")
- private final List<String> mHistoricalSessions = new ArrayList<>();
+ private final List<PackageInstallerHistoricalSession> mHistoricalSessions = new ArrayList<>();
@GuardedBy("mSessions")
private final SparseIntArray mHistoricalSessionsByInstaller = new SparseIntArray();
@@ -570,14 +569,11 @@
@GuardedBy("mSessions")
private void addHistoricalSessionLocked(PackageInstallerSession session) {
- CharArrayWriter writer = new CharArrayWriter();
- IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
- session.dump(pw);
if (mHistoricalSessions.size() > HISTORICAL_SESSIONS_THRESHOLD) {
Slog.d(TAG, "Historical sessions size reaches threshold, clear the oldest");
mHistoricalSessions.subList(0, HISTORICAL_CLEAR_SIZE).clear();
}
- mHistoricalSessions.add(writer.toString());
+ mHistoricalSessions.add(session.createHistoricalSession());
int installerUid = session.getInstallerUid();
// Increment the number of sessions by this installerUid.
@@ -1223,6 +1219,24 @@
return new ParceledListSlice<>(result);
}
+ ParceledListSlice<SessionInfo> getHistoricalSessions(int userId) {
+ final int callingUid = Binder.getCallingUid();
+ final Computer snapshot = mPm.snapshotComputer();
+ snapshot.enforceCrossUserPermission(callingUid, userId, true, false, "getAllSessions");
+
+ final List<SessionInfo> result = new ArrayList<>();
+ synchronized (mSessions) {
+ for (int i = 0; i < mHistoricalSessions.size(); i++) {
+ final PackageInstallerHistoricalSession session = mHistoricalSessions.get(i);
+ if (userId == UserHandle.USER_ALL || session.userId == userId) {
+ result.add(session.generateInfo());
+ }
+ }
+ }
+ result.removeIf(info -> shouldFilterSession(snapshot, callingUid, info));
+ return new ParceledListSlice<>(result);
+ }
+
@Override
public void uninstall(VersionedPackage versionedPackage, String callerPackageName, int flags,
IntentSender statusReceiver, int userId) {
@@ -1837,7 +1851,7 @@
pw.increaseIndent();
N = mHistoricalSessions.size();
for (int i = 0; i < N; i++) {
- pw.print(mHistoricalSessions.get(i));
+ mHistoricalSessions.get(i).dump(pw);
pw.println();
}
pw.println();
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 6270655..b6079a6 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -1148,6 +1148,25 @@
}
}
+ PackageInstallerHistoricalSession createHistoricalSession() {
+ final float progress;
+ final float clientProgress;
+ synchronized (mProgressLock) {
+ progress = mProgress;
+ clientProgress = mClientProgress;
+ }
+ synchronized (mLock) {
+ return new PackageInstallerHistoricalSession(sessionId, userId, mOriginalInstallerUid,
+ mOriginalInstallerPackageName, mInstallSource, mInstallerUid, createdMillis,
+ updatedMillis, committedMillis, stageDir, stageCid, clientProgress, progress,
+ isCommitted(), isPreapprovalRequested(), mSealed, mPermissionsManuallyAccepted,
+ mStageDirInUse, mDestroyed, mFds.size(), mBridges.size(), mFinalStatus,
+ mFinalMessage, params, mParentSessionId, getChildSessionIdsLocked(),
+ mSessionApplied, mSessionFailed, mSessionReady, mSessionErrorCode,
+ mSessionErrorMessage, mPreapprovalDetails);
+ }
+ }
+
/**
* Returns {@code true} if the {@link SessionInfo} object should be produced with potentially
* sensitive data scrubbed from its fields.
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index c63d8be..914f6d0 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -6933,6 +6933,11 @@
return mDistractingPackageHelper.getDistractingPackageRestrictionsAsUser(snapshot,
packageNames, userId, callingUid);
}
+
+ @Override
+ public ParceledListSlice<PackageInstaller.SessionInfo> getHistoricalSessions(int userId) {
+ return mInstallerService.getHistoricalSessions(userId);
+ }
}
private void setEnabledOverlayPackages(@UserIdInt int userId,