[pm/metrics][2/n] pass snapshot to metrics when finish

Snapshot is needed to collect information about installer UID and
installed package version code.

+ minor improvements

BUG: 249294752
Test: atest com.android.cts.packagemanager.stats.host.PackageInstallationSessionReportedStatsTests
Change-Id: Ibaf94132f70bd7a8a74351a297dc92fe515258f3
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 84b8264..2c08248 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -150,7 +150,6 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.content.F2fsUtils;
-import com.android.internal.content.InstallLocationUtils;
 import com.android.internal.security.VerityUtils;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.CollectionUtils;
@@ -875,7 +874,7 @@
                 }
             }
 
-            Map<String, ReconciledPackage> reconciledPackages;
+            List<ReconciledPackage> reconciledPackages;
             synchronized (mPm.mLock) {
                 try {
                     Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "reconcilePackages");
@@ -1881,11 +1880,11 @@
     }
 
     @GuardedBy("mPm.mLock")
-    private void commitPackagesLocked(Map<String, ReconciledPackage> reconciledPackages,
+    private void commitPackagesLocked(List<ReconciledPackage> reconciledPackages,
             @NonNull int[] allUsers) {
         // TODO: remove any expected failures from this method; this should only be able to fail due
         //       to unavoidable errors (I/O, etc.)
-        for (ReconciledPackage reconciledPkg : reconciledPackages.values()) {
+        for (ReconciledPackage reconciledPkg : reconciledPackages) {
             final InstallRequest installRequest = reconciledPkg.mInstallRequest;
             final ParsedPackage parsedPackage = installRequest.getParsedPackage();
             final String packageName = parsedPackage.getPackageName();
@@ -2203,9 +2202,9 @@
      * locks on {@link com.android.server.pm.PackageManagerService.mLock}.
      */
     @GuardedBy("mPm.mInstallLock")
-    private void executePostCommitStepsLIF(Map<String, ReconciledPackage> reconciledPackages) {
+    private void executePostCommitStepsLIF(List<ReconciledPackage> reconciledPackages) {
         final ArraySet<IncrementalStorage> incrementalStorages = new ArraySet<>();
-        for (ReconciledPackage reconciledPkg : reconciledPackages.values()) {
+        for (ReconciledPackage reconciledPkg : reconciledPackages) {
             final InstallRequest installRequest = reconciledPkg.mInstallRequest;
             final boolean instantApp = ((installRequest.getScanFlags() & SCAN_AS_INSTANT_APP) != 0);
             final boolean isApex = ((installRequest.getScanFlags() & SCAN_AS_APEX) != 0);
@@ -2336,45 +2335,6 @@
                 incrementalStorages);
     }
 
-    public int installLocationPolicy(PackageInfoLite pkgLite, int installFlags) {
-        String packageName = pkgLite.packageName;
-        int installLocation = pkgLite.installLocation;
-        // reader
-        synchronized (mPm.mLock) {
-            // Currently installed package which the new package is attempting to replace or
-            // null if no such package is installed.
-            AndroidPackage installedPkg = mPm.mPackages.get(packageName);
-
-            if (installedPkg != null) {
-                if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {
-                    // Check for updated system application.
-                    if (installedPkg.isSystem()) {
-                        return InstallLocationUtils.RECOMMEND_INSTALL_INTERNAL;
-                    } else {
-                        // If current upgrade specifies particular preference
-                        if (installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) {
-                            // Application explicitly specified internal.
-                            return InstallLocationUtils.RECOMMEND_INSTALL_INTERNAL;
-                        } else if (
-                                installLocation == PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL) {
-                            // App explicitly prefers external. Let policy decide
-                        } else {
-                            // Prefer previous location
-                            if (installedPkg.isExternalStorage()) {
-                                return InstallLocationUtils.RECOMMEND_INSTALL_EXTERNAL;
-                            }
-                            return InstallLocationUtils.RECOMMEND_INSTALL_INTERNAL;
-                        }
-                    }
-                } else {
-                    // Invalid install. Return error code
-                    return InstallLocationUtils.RECOMMEND_FAILED_ALREADY_EXISTS;
-                }
-            }
-        }
-        return pkgLite.recommendedInstallLocation;
-    }
-
     Pair<Integer, String> verifyReplacingVersionCode(PackageInfoLite pkgLite,
             long requiredInstalledVersionCode, int installFlags) {
         if ((installFlags & PackageManager.INSTALL_APEX) != 0) {
@@ -3643,7 +3603,7 @@
             boolean appIdCreated = false;
             try {
                 final String pkgName = scanResult.mPkgSetting.getPackageName();
-                final Map<String, ReconciledPackage> reconcileResult =
+                final List<ReconciledPackage> reconcileResult =
                         ReconcilePackageUtils.reconcilePackages(
                                 Collections.singletonList(installRequest),
                                 mPm.mPackages, Collections.singletonMap(pkgName,
@@ -3655,7 +3615,7 @@
                 } else {
                     installRequest.setScannedPackageSettingAppId(Process.INVALID_UID);
                 }
-                commitReconciledScanResultLocked(reconcileResult.get(pkgName),
+                commitReconciledScanResultLocked(reconcileResult.get(0),
                         mPm.mUserManager.getUserIds());
             } catch (PackageManagerException e) {
                 if (appIdCreated) {
diff --git a/services/core/java/com/android/server/pm/InstallRequest.java b/services/core/java/com/android/server/pm/InstallRequest.java
index 4443710..01a8bd0 100644
--- a/services/core/java/com/android/server/pm/InstallRequest.java
+++ b/services/core/java/com/android/server/pm/InstallRequest.java
@@ -59,8 +59,10 @@
     @Nullable
     private PackageRemovedInfo mRemovedInfo;
 
-    private @PackageManagerService.ScanFlags int mScanFlags;
-    private @ParsingPackageUtils.ParseFlags int mParseFlags;
+    @PackageManagerService.ScanFlags
+    private int mScanFlags;
+    @ParsingPackageUtils.ParseFlags
+    private int mParseFlags;
     private boolean mReplace;
 
     @Nullable /* The original Package if it is being replaced, otherwise {@code null} */
@@ -155,7 +157,7 @@
         mParseFlags = parseFlags;
         mScanFlags = scanFlags;
         mScanResult = scanResult;
-        mPackageMetrics = null; // No real logging from this code path
+        mPackageMetrics = null; // No logging from this code path
     }
 
     @Nullable
@@ -393,11 +395,13 @@
         return mParsedPackage;
     }
 
-    public @ParsingPackageUtils.ParseFlags int getParseFlags() {
+    @ParsingPackageUtils.ParseFlags
+    public int getParseFlags() {
         return mParseFlags;
     }
 
-    public @PackageManagerService.ScanFlags int getScanFlags() {
+    @PackageManagerService.ScanFlags
+    public int getScanFlags() {
         return mScanFlags;
     }
 
@@ -435,6 +439,11 @@
         return mIsInstallForUsers;
     }
 
+    public boolean isInstallFromAdb() {
+        return mInstallArgs != null
+                && (mInstallArgs.mInstallFlags & PackageManager.INSTALL_FROM_ADB) != 0;
+    }
+
     @Nullable
     public PackageSetting getOriginalPackageSetting() {
         return mOriginalPs;
@@ -731,10 +740,10 @@
         }
     }
 
-    public void onInstallCompleted() {
+    public void onInstallCompleted(Computer snapshot) {
         if (getReturnCode() == INSTALL_SUCCEEDED) {
             if (mPackageMetrics != null) {
-                mPackageMetrics.onInstallSucceed();
+                mPackageMetrics.onInstallSucceed(snapshot);
             }
         }
     }
diff --git a/services/core/java/com/android/server/pm/InstallingSession.java b/services/core/java/com/android/server/pm/InstallingSession.java
index d13822a..e4a0a3a 100644
--- a/services/core/java/com/android/server/pm/InstallingSession.java
+++ b/services/core/java/com/android/server/pm/InstallingSession.java
@@ -510,7 +510,7 @@
             mInstallPackageHelper.installPackagesTraced(installRequests);
 
             for (InstallRequest request : installRequests) {
-                request.onInstallCompleted();
+                request.onInstallCompleted(mPm.snapshotComputer());
                 doPostInstall(request);
             }
         }
diff --git a/services/core/java/com/android/server/pm/PackageMetrics.java b/services/core/java/com/android/server/pm/PackageMetrics.java
index b725325..0391163 100644
--- a/services/core/java/com/android/server/pm/PackageMetrics.java
+++ b/services/core/java/com/android/server/pm/PackageMetrics.java
@@ -16,16 +16,26 @@
 
 package com.android.server.pm;
 
+import static android.os.Process.INVALID_UID;
+
 import android.annotation.IntDef;
+import android.content.pm.parsing.ApkLiteParseUtils;
 import android.os.UserManager;
 import android.util.Pair;
 import android.util.SparseArray;
 
 import com.android.internal.util.FrameworkStatsLog;
+import com.android.server.pm.pkg.PackageStateInternal;
 
+import java.io.File;
+import java.io.IOException;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.nio.file.Files;
+import java.nio.file.Path;
 import java.util.ArrayList;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.stream.Stream;
 
 /**
  * Metrics class for reporting stats to logging infrastructures like Westworld
@@ -43,7 +53,8 @@
             STEP_COMMIT,
     })
     @Retention(RetentionPolicy.SOURCE)
-    public @interface StepInt {}
+    public @interface StepInt {
+    }
 
     private final long mInstallStartTimestampMillis;
     private final SparseArray<InstallStep> mInstallSteps = new SparseArray<>();
@@ -56,16 +67,30 @@
         mInstallRequest = installRequest;
     }
 
-    public void onInstallSucceed() {
+    public void onInstallSucceed(Computer snapshot) {
+        // TODO(b/239722919): report to SecurityLog if on work profile or managed device
+        reportInstallationStats(snapshot, true /* success */);
+    }
+
+    private void reportInstallationStats(Computer snapshot, boolean success) {
+        // TODO(b/249294752): do not log if adb
         final long installDurationMillis =
                 System.currentTimeMillis() - mInstallStartTimestampMillis;
         // write to stats
         final Pair<int[], long[]> stepDurations = getInstallStepDurations();
         final int[] newUsers = mInstallRequest.getNewUsers();
         final int[] originalUsers = mInstallRequest.getOriginUsers();
+        final String packageName = mInstallRequest.getName();
+        final String installerPackageName = mInstallRequest.getInstallerPackageName();
+        final int installerUid = installerPackageName == null ? INVALID_UID
+                : snapshot.getPackageUid(installerPackageName, 0, 0);
+        final PackageStateInternal ps = snapshot.getPackageStateInternal(packageName);
+        final long versionCode = success ? 0 : ps.getVersionCode();
+        final long apksSize = getApksSize(ps.getPath());
+
         FrameworkStatsLog.write(FrameworkStatsLog.PACKAGE_INSTALLATION_SESSION_REPORTED,
                 0 /* session_id */,
-                null /* package_name */,
+                success ? null : packageName /* not report package_name on success */,
                 mInstallRequest.getUid() /* uid */,
                 newUsers /* user_ids */,
                 getUserTypes(newUsers) /* user_types */,
@@ -73,13 +98,13 @@
                 getUserTypes(originalUsers) /* original_user_types */,
                 mInstallRequest.getReturnCode() /* public_return_code */,
                 0 /* internal_error_code */,
-                0 /* apks_size_bytes */,
-                0 /* version_code */,
+                apksSize /* apks_size_bytes */,
+                versionCode /* version_code */,
                 stepDurations.first /* install_steps */,
                 stepDurations.second /* step_duration_millis */,
                 installDurationMillis /* total_duration_millis */,
                 mInstallRequest.getInstallFlags() /* install_flags */,
-                -1 /* installer_package_uid */,
+                installerUid /* installer_package_uid */,
                 -1 /* original_installer_package_uid */,
                 mInstallRequest.getDataLoaderType() /* data_loader_type */,
                 0 /* user_action_required_type */,
@@ -93,6 +118,19 @@
         );
     }
 
+    private long getApksSize(File apkDir) {
+        // TODO(b/249294752): also count apk sizes for failed installs
+        final AtomicLong apksSize = new AtomicLong();
+        try (Stream<Path> walkStream = Files.walk(apkDir.toPath())) {
+            walkStream.filter(p -> p.toFile().isFile()
+                    && ApkLiteParseUtils.isApkFile(p.toFile())).forEach(
+                            f -> apksSize.addAndGet(f.toFile().length()));
+        } catch (IOException e) {
+            // ignore
+        }
+        return apksSize.get();
+    }
+
     public void onStepStarted(@StepInt int step) {
         mInstallSteps.put(step, new InstallStep());
     }
@@ -140,12 +178,15 @@
     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 01d17f6..99bcbc9 100644
--- a/services/core/java/com/android/server/pm/ReconcilePackageUtils.java
+++ b/services/core/java/com/android/server/pm/ReconcilePackageUtils.java
@@ -35,6 +35,7 @@
 import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
 import com.android.server.utils.WatchedLongSparseArray;
 
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 
@@ -48,14 +49,14 @@
  * as install) led to the request.
  */
 final class ReconcilePackageUtils {
-    public static Map<String, ReconciledPackage> reconcilePackages(
+    public static List<ReconciledPackage> reconcilePackages(
             List<InstallRequest> installRequests,
             Map<String, AndroidPackage> allPackages,
             Map<String, Settings.VersionInfo> versionInfos,
             SharedLibrariesImpl sharedLibraries,
             KeySetManagerService ksms, Settings settings)
             throws ReconcileFailure {
-        final Map<String, ReconciledPackage> result = new ArrayMap<>(installRequests.size());
+        final List<ReconciledPackage> result = new ArrayList<>(installRequests.size());
 
         // make a copy of the existing set of packages so we can combine them with incoming packages
         final ArrayMap<String, AndroidPackage> combinedPackages =
@@ -88,7 +89,6 @@
             }
 
 
-
             final DeletePackageAction deletePackageAction;
             // we only want to try to delete for non system apps
             if (installRequest.isInstallReplace() && !installRequest.isInstallSystem()) {
@@ -257,13 +257,11 @@
                 }
             }
 
-            result.put(installPackageName,
+            final ReconciledPackage reconciledPackage =
                     new ReconciledPackage(installRequests, allPackages, installRequest,
                             deletePackageAction, allowedSharedLibInfos, signingDetails,
-                            sharedUserSignaturesChanged, removeAppKeySetData));
-        }
+                            sharedUserSignaturesChanged, removeAppKeySetData);
 
-        for (InstallRequest installRequest : installRequests) {
             // Check all shared libraries and map to their actual file path.
             // We only do this here for apps not on a system dir, because those
             // are the only ones that can fail an install due to this.  We
@@ -271,24 +269,21 @@
             // library paths after the scan is done. Also during the initial
             // scan don't update any libs as we do this wholesale after all
             // apps are scanned to avoid dependency based scanning.
-            if ((installRequest.getScanFlags() & SCAN_BOOTING) != 0
-                    || (installRequest.getParseFlags() & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR)
-                    != 0) {
-                continue;
+            if ((installRequest.getScanFlags() & SCAN_BOOTING) == 0
+                    && (installRequest.getParseFlags() & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR)
+                    == 0) {
+                try {
+                    reconciledPackage.mCollectedSharedLibraryInfos =
+                            sharedLibraries.collectSharedLibraryInfos(
+                                    installRequest.getParsedPackage(), combinedPackages,
+                                    incomingSharedLibraries);
+                } catch (PackageManagerException e) {
+                    throw new ReconcileFailure(e.error, e.getMessage());
+                }
             }
-            final String installPackageName = installRequest.getParsedPackage().getPackageName();
-            try {
-                result.get(installPackageName).mCollectedSharedLibraryInfos =
-                        sharedLibraries.collectSharedLibraryInfos(
-                                installRequest.getParsedPackage(), combinedPackages,
-                                incomingSharedLibraries);
-            } catch (PackageManagerException e) {
-                throw new ReconcileFailure(e.error, e.getMessage());
-            }
-        }
 
-        for (InstallRequest installRequest : installRequests) {
             installRequest.onReconcileFinished();
+            result.add(reconciledPackage);
         }
 
         return result;