[DFMs] Measure DFM install duration

We track two durations:
- Install duration for module requested first since the last Chrome
  start. Those installs are unlikely to come from cache and the install
  duration should therefore be most representative.
- Install duration for module requested before Chrome started last. May
  be very short due to caching. Included here for completeness.

Bug: 923539
Change-Id: I771cb841bd74f737ecfc6d2a9d2ebb35de138746
Reviewed-on: https://chromium-review.googlesource.com/c/1427930
Commit-Queue: Ilya Sherman <isherman@chromium.org>
Reviewed-by: Ilya Sherman <isherman@chromium.org>
Reviewed-by: Andrew Grieve <agrieve@chromium.org>
Auto-Submit: Tibor Goldschwendt <tiborg@chromium.org>
Cr-Original-Commit-Position: refs/heads/master@{#625906}(cherry picked from commit b5d410266ebd0da54c56643105867f8b965bb104)
Reviewed-on: https://chromium-review.googlesource.com/c/1452758
Reviewed-by: Tibor Goldschwendt <tiborg@chromium.org>
Cr-Commit-Position: refs/branch-heads/3683@{#180}
Cr-Branched-From: e51029943e0a38dd794b73caaf6373d5496ae783-refs/heads/master@{#625896}
diff --git a/components/module_installer/android/java/src-impl/org/chromium/components/module_installer/PlayCoreModuleInstallerBackend.java b/components/module_installer/android/java/src-impl/org/chromium/components/module_installer/PlayCoreModuleInstallerBackend.java
index 18ee5fd..4061ba5 100644
--- a/components/module_installer/android/java/src-impl/org/chromium/components/module_installer/PlayCoreModuleInstallerBackend.java
+++ b/components/module_installer/android/java/src-impl/org/chromium/components/module_installer/PlayCoreModuleInstallerBackend.java
@@ -4,6 +4,9 @@
 
 package org.chromium.components.module_installer;
 
+import android.content.SharedPreferences;
+import android.util.Pair;
+
 import com.google.android.play.core.splitinstall.SplitInstallManager;
 import com.google.android.play.core.splitinstall.SplitInstallManagerFactory;
 import com.google.android.play.core.splitinstall.SplitInstallRequest;
@@ -17,7 +20,12 @@
 import org.chromium.components.module_installer.ModuleInstallerBackend.OnFinishedListener;
 
 import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
 
 /**
  * Backend that uses the Play Core SDK to download a module from Play and install it subsequently.
@@ -25,6 +33,9 @@
 /* package */ class PlayCoreModuleInstallerBackend
         extends ModuleInstallerBackend implements SplitInstallStateUpdatedListener {
     private static final String TAG = "PlayCoreModInBackend";
+    private static final String KEY_MODULES_REQUESTED_PREVIOUSLY =
+            "key_modules_requested_previously";
+    private final Map<String, Pair<Long, Boolean>> mInstallStartTimeMap = new HashMap<>();
     private final SplitInstallManager mManager;
     private boolean mIsClosed;
 
@@ -47,6 +58,25 @@
     @Override
     public void install(String moduleName) {
         assert !mIsClosed;
+
+        // Record start time in order to later report the install duration via UMA. We want to make
+        // a difference between modules that have been requested first before and after the last
+        // Chrome start. Modules that have been requested before may install quicker as they may be
+        // installed form cache. To do this, we use shared prefs to track modules previously
+        // requested.
+        assert !mInstallStartTimeMap.containsKey(moduleName);
+        SharedPreferences prefs = ContextUtils.getAppSharedPreferences();
+        Set<String> modulesRequestedPreviously =
+                prefs.getStringSet(KEY_MODULES_REQUESTED_PREVIOUSLY, new HashSet<String>());
+        mInstallStartTimeMap.put(moduleName,
+                Pair.create(System.currentTimeMillis(),
+                        modulesRequestedPreviously.contains(moduleName)));
+        Set<String> newModulesRequestedPreviously = new HashSet<>(modulesRequestedPreviously);
+        newModulesRequestedPreviously.add(moduleName);
+        SharedPreferences.Editor editor = prefs.edit();
+        editor.putStringSet(KEY_MODULES_REQUESTED_PREVIOUSLY, newModulesRequestedPreviously);
+        editor.apply();
+
         SplitInstallRequest request =
                 SplitInstallRequest.newBuilder().addModule(moduleName).build();
         mManager.startInstall(request).addOnFailureListener(errorCode -> {
@@ -93,6 +123,17 @@
         for (String name : moduleNames) {
             RecordHistogram.recordEnumeratedHistogram(
                     "Android.FeatureModules.InstallStatus." + name, eventId, INSTALL_STATUS_COUNT);
+
+            assert mInstallStartTimeMap.containsKey(name);
+            if (success) {
+                Pair<Long, Boolean> moduleInfo = mInstallStartTimeMap.get(name);
+                long installDurationMs = System.currentTimeMillis() - moduleInfo.first;
+                String histogramSubname =
+                        moduleInfo.second ? "CachedInstallDuration" : "UncachedInstallDuration";
+                RecordHistogram.recordLongTimesHistogram(
+                        "Android.FeatureModules." + histogramSubname + "." + name,
+                        installDurationMs, TimeUnit.MILLISECONDS);
+            }
         }
         onFinished(success, moduleNames);
     }
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 4db4711..79ebb60 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -1974,8 +1974,22 @@
   </summary>
 </histogram>
 
+<histogram base="true" name="Android.FeatureModules.CachedInstallDuration"
+    units="ms" expires_after="2020-01-01">
+<!-- Name completed by histogram_suffixes
+       name="AndroidFeatureModuleName" -->
+
+  <owner>tiborg@chromium.org</owner>
+  <owner>agrieve@chromium.org</owner>
+  <summary>
+    Duration of successful installs for each dynamic feature module. Only
+    contains install durations for modules first requested *before* Chrome
+    started last and may therefore be installed from cache.
+  </summary>
+</histogram>
+
 <histogram base="true" name="Android.FeatureModules.InstallStatus"
-    enum="FeatureModuleInstallStatus">
+    enum="FeatureModuleInstallStatus" expires_after="2020-01-01">
 <!-- Name completed by histogram_suffixes
        name="AndroidFeatureModuleName" -->
 
@@ -1987,6 +2001,20 @@
   </summary>
 </histogram>
 
+<histogram base="true" name="Android.FeatureModules.UncachedInstallDuration"
+    units="ms" expires_after="2020-01-01">
+<!-- Name completed by histogram_suffixes
+       name="AndroidFeatureModuleName" -->
+
+  <owner>tiborg@chromium.org</owner>
+  <owner>agrieve@chromium.org</owner>
+  <summary>
+    Duration of successful installs for each dynamic feature module. Only
+    contains install durations of modules first requested *after* Chrome started
+    last and are therefore unlikely to be installed from cache.
+  </summary>
+</histogram>
+
 <histogram name="Android.HistoryPage.OpenSelected">
   <owner>twellington@chromium.org</owner>
   <summary>
@@ -130674,7 +130702,9 @@
 <histogram_suffixes name="AndroidFeatureModuleName" separator=".">
   <suffix name="ar" label="Augmented Reality Module"/>
   <suffix name="vr" label="Virtual Reality Module"/>
+  <affected-histogram name="Android.FeatureModules.CachedInstallDuration"/>
   <affected-histogram name="Android.FeatureModules.InstallStatus"/>
+  <affected-histogram name="Android.FeatureModules.UncachedInstallDuration"/>
 </histogram_suffixes>
 
 <histogram_suffixes name="AndroidGATTEvents" separator=".">