Add UMA to record whether the extension is from cache or not

We want to investigate why the force installed extensions fail to
install with error CRX_HEADER_INVALID. This CL adds UMA statistics
to record whether the extension is downloaded from cache or not when
extension fails with CRX_HEADER_INVALID error to get more details
about it.

New histogram added:
"Extensions.ForceInstalledFailureWithCrxHeaderInvalidIsFromCache"

(cherry picked from commit 1e01124fa9f126d3d4e8b61397ec931db0ff8863)

Bug: b/170087934
Change-Id: I843e4a1c2cce87b3975e2ad426f9b8177a744878
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2460734
Reviewed-by: Saurabh Nijhara <[email protected]>
Reviewed-by: Brian White <[email protected]>
Reviewed-by: Oleg Davydov <[email protected]>
Commit-Queue: Swapnil Gupta <[email protected]>
Cr-Original-Commit-Position: refs/heads/master@{#816786}
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2471057
Reviewed-by: Swapnil Gupta <[email protected]>
Cr-Commit-Position: refs/branch-heads/4280@{#367}
Cr-Branched-From: ea420fb963f9658c9969b6513c56b8f47efa1a2a-refs/heads/master@{#812852}
diff --git a/chrome/browser/extensions/forced_extensions/force_installed_metrics.cc b/chrome/browser/extensions/forced_extensions/force_installed_metrics.cc
index 00ed95e..fc7a5e3 100644
--- a/chrome/browser/extensions/forced_extensions/force_installed_metrics.cc
+++ b/chrome/browser/extensions/forced_extensions/force_installed_metrics.cc
@@ -78,6 +78,15 @@
 }
 #endif  // defined(OS_CHROMEOS)
 
+// Returns true if the extension is fetched from the cache.
+bool IsExtensionFetchedFromCache(
+    const base::Optional<ExtensionDownloaderDelegate::CacheStatus> status) {
+  DCHECK(status);
+  return status.value() == ExtensionDownloaderDelegate::CacheStatus::
+                               CACHE_HIT_ON_MANIFEST_FETCH_FAILURE ||
+         status.value() == ExtensionDownloaderDelegate::CacheStatus::CACHE_HIT;
+}
+
 // Reports time taken for force installed extension during different
 // installation stages.
 void ReportInstallationStageTimes(
@@ -240,6 +249,9 @@
     base::UmaHistogramBoolean(
         "Extensions.ForceInstalledFailureWithCrxHeaderInvalidIsCWS",
         is_from_store);
+    base::UmaHistogramBoolean(
+        "Extensions.ForceInstalledFailureWithCrxHeaderInvalidIsFromCache",
+        IsExtensionFetchedFromCache(installation.downloading_cache_status));
   }
 }
 
diff --git a/chrome/browser/extensions/forced_extensions/force_installed_metrics_unittest.cc b/chrome/browser/extensions/forced_extensions/force_installed_metrics_unittest.cc
index 60b57ef..90f9ddb 100644
--- a/chrome/browser/extensions/forced_extensions/force_installed_metrics_unittest.cc
+++ b/chrome/browser/extensions/forced_extensions/force_installed_metrics_unittest.cc
@@ -113,6 +113,8 @@
     "Extensions.ForceInstalledTime.FinalizingStartTo.CRXInstallComplete";
 constexpr char kCrxHeaderInvalidFailureIsCWS[] =
     "Extensions.ForceInstalledFailureWithCrxHeaderInvalidIsCWS";
+constexpr char kCrxHeaderInvalidFailureFromCache[] =
+    "Extensions.ForceInstalledFailureWithCrxHeaderInvalidIsFromCache";
 
 }  // namespace
 
@@ -533,10 +535,12 @@
       1);
 }
 
-// Reporting whether the extension is from CWS or not when the force installed
-// extension fails to install with error CRX_HEADER_INVALID.
-TEST_F(ForceInstalledMetricsTest, ExtensionsCrxHeaderInvalid) {
+// Reporting when the extension is downloaded from cache and it fails to install
+// with error CRX_HEADER_INVALID.
+TEST_F(ForceInstalledMetricsTest, ExtensionsCrxHeaderInvalidFromCache) {
   SetupForceList();
+  install_stage_tracker()->ReportDownloadingCacheStatus(
+      kExtensionId1, ExtensionDownloaderDelegate::CacheStatus::CACHE_HIT);
   install_stage_tracker()->ReportSandboxedUnpackerFailureReason(
       kExtensionId1, SandboxedUnpackerFailureReason::CRX_HEADER_INVALID);
   auto ext2 = ExtensionBuilder(kExtensionName2).SetID(kExtensionId2).Build();
@@ -550,6 +554,31 @@
       kSandboxUnpackFailureReason,
       SandboxedUnpackerFailureReason::CRX_HEADER_INVALID, 1);
   histogram_tester_.ExpectBucketCount(kCrxHeaderInvalidFailureIsCWS, true, 1);
+  histogram_tester_.ExpectBucketCount(kCrxHeaderInvalidFailureFromCache, true,
+                                      1);
+}
+
+// Reporting when the extension is not downloaded from cache and it fails to
+// install with error CRX_HEADER_INVALID.
+TEST_F(ForceInstalledMetricsTest, ExtensionsCrxHeaderInvalidNotFromCache) {
+  SetupForceList();
+  install_stage_tracker()->ReportDownloadingCacheStatus(
+      kExtensionId1, ExtensionDownloaderDelegate::CacheStatus::CACHE_MISS);
+  install_stage_tracker()->ReportSandboxedUnpackerFailureReason(
+      kExtensionId1, SandboxedUnpackerFailureReason::CRX_HEADER_INVALID);
+  auto ext2 = ExtensionBuilder(kExtensionName2).SetID(kExtensionId2).Build();
+  registry()->AddEnabled(ext2.get());
+  force_installed_tracker()->OnExtensionLoaded(profile(), ext2.get());
+  // ForceInstalledMetrics shuts down timer because all extension are either
+  // loaded or failed.
+  EXPECT_FALSE(fake_timer_->IsRunning());
+  histogram_tester_.ExpectTotalCount(kSandboxUnpackFailureReason, 1);
+  histogram_tester_.ExpectBucketCount(
+      kSandboxUnpackFailureReason,
+      SandboxedUnpackerFailureReason::CRX_HEADER_INVALID, 1);
+  histogram_tester_.ExpectBucketCount(kCrxHeaderInvalidFailureIsCWS, true, 1);
+  histogram_tester_.ExpectBucketCount(kCrxHeaderInvalidFailureFromCache, false,
+                                      1);
 }
 
 // Reporting info when the force installed extension fails to install with error
diff --git a/tools/metrics/histograms/histograms_xml/extensions/histograms.xml b/tools/metrics/histograms/histograms_xml/extensions/histograms.xml
index 8da06b6..20cb385 100644
--- a/tools/metrics/histograms/histograms_xml/extensions/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/extensions/histograms.xml
@@ -1483,6 +1483,21 @@
   </summary>
 </histogram>
 
+<histogram
+    name="Extensions.ForceInstalledFailureWithCrxHeaderInvalidIsFromCache"
+    enum="BooleanCacheHit" expires_after="2021-01-24">
+  <owner>[email protected]</owner>
+  <owner>[email protected]</owner>
+  <owner>[email protected]</owner>
+  <owner>[email protected]</owner>
+  <summary>
+    Records whether the extension is downloaded from cache or not. Recorded for
+    each forced extension that failed to install after 5 minutes with
+    Extensions.ForceInstalledFailureSandboxUnpackFailureReason equal to
+    CRX_HEADER_INVALID.
+  </summary>
+</histogram>
+
 <histogram name="Extensions.ForceInstalledFetchTries" units="retries"
     expires_after="2021-03-28">
   <owner>[email protected]</owner>