Add feature flag for descriptive failed offline page status text

Check flag when determining status text for a failed download attempt.
When the flag is enabled, descriptive status texts should be used for
downloads that failed due to an actionable error. Descriptive status
texts still are TBD. When the flag is disabled, use generic "Download
failed."

Bug: 824931
Change-Id: I2d67a7c3213a3a57a39b72eeae7433a6b847283e
Reviewed-on: https://chromium-review.googlesource.com/982520
Commit-Queue: Candice Sy <[email protected]>
Reviewed-by: Cathy Li <[email protected]>
Reviewed-by: David Trainor <[email protected]>
Reviewed-by: Joy Ming <[email protected]>
Cr-Commit-Position: refs/heads/master@{#547324}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
index b20d4886..f4b13d1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
@@ -230,6 +230,8 @@
     public static final String NTP_SHORTCUTS = "NTPShortcuts";
     public static final String NTP_SHOW_GOOGLE_G_IN_OMNIBOX = "NTPShowGoogleGInOmnibox";
     public static final String NTP_SNIPPETS_INCREASED_VISIBILITY = "NTPSnippetsIncreasedVisibility";
+    public static final String OFFLINE_PAGES_DESCRIPTIVE_FAIL_STATUS =
+            "OfflinePagesDescriptiveFailStatus";
     public static final String OFFLINE_PAGES_DESCRIPTIVE_PENDING_STATUS =
             "OfflinePagesDescriptivePendingStatus";
     public static final String OMNIBOX_HIDE_SCHEME_DOMAIN_IN_STEADY_STATE =
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationFactory.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationFactory.java
index 3245b5a..ccfc013c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationFactory.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationFactory.java
@@ -238,10 +238,7 @@
 
             case FAILED:
                 iconId = android.R.drawable.stat_sys_download_done;
-                // TODO(cmsy): Use downloadUpdate.getFailState() to determine which descriptive
-                // status text to use.
-                contentText =
-                        context.getResources().getString(R.string.download_notification_failed);
+                contentText = DownloadUtils.getFailStatusString(downloadUpdate.getFailState());
                 break;
 
             case SUMMARY:
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadUtils.java
index 493766e..82af7f71 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadUtils.java
@@ -49,6 +49,7 @@
 import org.chromium.components.download.DownloadState;
 import org.chromium.components.feature_engagement.EventConstants;
 import org.chromium.components.feature_engagement.Tracker;
+import org.chromium.components.offline_items_collection.FailState;
 import org.chromium.components.offline_items_collection.OfflineItem;
 import org.chromium.components.offline_items_collection.OfflineItem.Progress;
 import org.chromium.components.offline_items_collection.OfflineItemProgressUnit;
@@ -672,6 +673,30 @@
     }
 
     /**
+     * Determine the status string for a failed download.
+     *
+     * @param failState Reason download failed.
+     * @return String representing the current download status.
+     */
+    public static String getFailStatusString(@FailState int failState) {
+        Context context = ContextUtils.getApplicationContext();
+        if (BrowserStartupController.get(LibraryProcessType.PROCESS_BROWSER)
+                        .isStartupSuccessfullyCompleted()
+                && ChromeFeatureList.isEnabled(
+                           ChromeFeatureList.OFFLINE_PAGES_DESCRIPTIVE_FAIL_STATUS)) {
+            switch (failState) {
+                // TODO(cmsy): Return correct status for failure reasons once strings are finalized.
+                case FailState.CANNOT_DOWNLOAD:
+                case FailState.NETWORK_INSTABILITY:
+                default:
+                    return context.getString(R.string.download_notification_failed);
+            }
+        } else {
+            return context.getString(R.string.download_notification_failed);
+        }
+    }
+
+    /**
      * Determine the status string for a pending download.
      *
      * @param pendingState Reason download is pending.
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadNotificationService2Test.java b/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadNotificationService2Test.java
index 1b5bf09..474a452 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadNotificationService2Test.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadNotificationService2Test.java
@@ -54,8 +54,10 @@
 
     @ClassParameter
     private static List<ParameterSet> sClassParams = Arrays.asList(
-            new ParameterSet().value(false).name("DisableOfflinePagesDescriptivePendingStatus"),
-            new ParameterSet().value(true).name("EnableOfflinePagesDescriptivePendingStatus"));
+            new ParameterSet().value(false, false).name("GenericStatus"),
+            new ParameterSet().value(true, false).name("EnableDescriptivePendingStatusOnly"),
+            new ParameterSet().value(false, true).name("EnableDescriptiveFailStatusOnly"),
+            new ParameterSet().value(true, true).name("EnableDescriptivePendingAndFailStatus"));
 
     @Rule
     public TestRule mFeaturesProcessor = new Features.JUnitProcessor();
@@ -69,9 +71,12 @@
     private DownloadSharedPreferenceHelper mDownloadSharedPreferenceHelper;
 
     private final boolean mEnableOfflinePagesDescriptivePendingStatus;
+    private final boolean mEnableOfflinePagesDescriptiveFailStatus;
 
-    public DownloadNotificationService2Test(boolean enableOfflinePagesDescriptivePendingStatus) {
+    public DownloadNotificationService2Test(boolean enableOfflinePagesDescriptivePendingStatus,
+            boolean enableOfflinePagesDescriptiveFailStatus) {
         mEnableOfflinePagesDescriptivePendingStatus = enableOfflinePagesDescriptivePendingStatus;
+        mEnableOfflinePagesDescriptiveFailStatus = enableOfflinePagesDescriptiveFailStatus;
     }
 
     private static DownloadSharedPreferenceEntry buildEntryStringWithGuid(ContentId contentId,
@@ -89,6 +94,11 @@
             Features.getInstance().disable(
                     ChromeFeatureList.OFFLINE_PAGES_DESCRIPTIVE_PENDING_STATUS);
         }
+        if (mEnableOfflinePagesDescriptiveFailStatus) {
+            Features.getInstance().enable(ChromeFeatureList.OFFLINE_PAGES_DESCRIPTIVE_FAIL_STATUS);
+        } else {
+            Features.getInstance().disable(ChromeFeatureList.OFFLINE_PAGES_DESCRIPTIVE_FAIL_STATUS);
+        }
         DownloadNotificationService2.clearResumptionAttemptLeft();
         mDownloadNotificationService = new MockDownloadNotificationService2();
         mDownloadForegroundServiceManager =
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 9785caa..ccd7e2d 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -2324,6 +2324,12 @@
      flag_descriptions::kBackgroundLoaderForDownloadsName,
      flag_descriptions::kBackgroundLoaderForDownloadsDescription, kOsAndroid,
      FEATURE_VALUE_TYPE(offline_pages::kBackgroundLoaderForDownloadsFeature)},
+    {"offline-pages-failed-download",
+     flag_descriptions::kOfflinePagesDescriptiveFailStatusName,
+     flag_descriptions::kOfflinePagesDescriptiveFailStatusDescription,
+     kOsAndroid,
+     FEATURE_VALUE_TYPE(
+         offline_pages::kOfflinePagesDescriptiveFailStatusFeature)},
     {"offline-pages-pending-download",
      flag_descriptions::kOfflinePagesDescriptivePendingStatusName,
      flag_descriptions::kOfflinePagesDescriptivePendingStatusDescription,
diff --git a/chrome/browser/android/chrome_feature_list.cc b/chrome/browser/android/chrome_feature_list.cc
index aa79e2c2..4a8134a0 100644
--- a/chrome/browser/android/chrome_feature_list.cc
+++ b/chrome/browser/android/chrome_feature_list.cc
@@ -145,6 +145,7 @@
     &offline_pages::kBackgroundLoaderForDownloadsFeature,
     &offline_pages::kOfflinePagesCTFeature,    // See crbug.com/620421.
     &offline_pages::kOfflinePagesCTV2Feature,  // See crbug.com/734753.
+    &offline_pages::kOfflinePagesDescriptiveFailStatusFeature,
     &offline_pages::kOfflinePagesDescriptivePendingStatusFeature,
     &offline_pages::kOfflinePagesSharingFeature,
     &omnibox::kUIExperimentHideSteadyStateUrlSchemeAndSubdomains,
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index c63a5a8..5a8bf82 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -2148,6 +2148,24 @@
     "V2 features include attributing pages to the app that initiated the "
     "custom tabs, and being able to query for pages by page attribution.";
 
+const char kOfflinePagesDescriptiveFailStatusName[] =
+    "Enables descriptive failed download status text.";
+const char kOfflinePagesDescriptiveFailStatusDescription[] =
+    "Enables failed download status text in notifications and Downloads Home "
+    "to state the reason the request failed if the failure is actionable.";
+
+const char kOfflinePagesDescriptivePendingStatusName[] =
+    "Enables descriptive pending download status text.";
+const char kOfflinePagesDescriptivePendingStatusDescription[] =
+    "Enables pending download status text in notifications and Downloads Home "
+    "to state the reason the request is pending.";
+
+const char kOfflinePagesInDownloadHomeOpenInCctName[] =
+    "Enables offline pages in the downloads home to be opened in CCT.";
+const char kOfflinePagesInDownloadHomeOpenInCctDescription[] =
+    "When enabled offline pages launched from the Downloads Home will be "
+    "opened in Chrome Custom Tabs (CCT) instead of regular tabs.";
+
 const char kOfflinePagesLimitlessPrefetchingName[] =
     "Removes resource usage limits for the prefetching of offline pages.";
 const char kOfflinePagesLimitlessPrefetchingDescription[] =
@@ -2162,18 +2180,6 @@
     "page.  This data is collected in the snapshotted offline page to allow "
     "data analysis to improve deciding when to make the offline snapshot.";
 
-const char kOfflinePagesDescriptivePendingStatusName[] =
-    "Enables descriptive pending download status text.";
-const char kOfflinePagesDescriptivePendingStatusDescription[] =
-    "Enables pending download status text in notifications and Downloads Home "
-    "to state the reason the request is pending.";
-
-const char kOfflinePagesInDownloadHomeOpenInCctName[] =
-    "Enables offline pages in the downloads home to be opened in CCT.";
-const char kOfflinePagesInDownloadHomeOpenInCctDescription[] =
-    "When enabled offline pages launched from the Downloads Home will be "
-    "opened in Chrome Custom Tabs (CCT) instead of regular tabs.";
-
 const char kOfflinePagesPrefetchingName[] =
     "Enables suggested offline pages to be prefetched.";
 const char kOfflinePagesPrefetchingDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 621c289a..61db0f5 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -1311,11 +1311,8 @@
 extern const char kOfflinePagesCtV2Name[];
 extern const char kOfflinePagesCtV2Description[];
 
-extern const char kOfflinePagesLimitlessPrefetchingName[];
-extern const char kOfflinePagesLimitlessPrefetchingDescription[];
-
-extern const char kOfflinePagesLoadSignalCollectingName[];
-extern const char kOfflinePagesLoadSignalCollectingDescription[];
+extern const char kOfflinePagesDescriptiveFailStatusName[];
+extern const char kOfflinePagesDescriptiveFailStatusDescription[];
 
 extern const char kOfflinePagesDescriptivePendingStatusName[];
 extern const char kOfflinePagesDescriptivePendingStatusDescription[];
@@ -1323,6 +1320,12 @@
 extern const char kOfflinePagesInDownloadHomeOpenInCctName[];
 extern const char kOfflinePagesInDownloadHomeOpenInCctDescription[];
 
+extern const char kOfflinePagesLimitlessPrefetchingName[];
+extern const char kOfflinePagesLimitlessPrefetchingDescription[];
+
+extern const char kOfflinePagesLoadSignalCollectingName[];
+extern const char kOfflinePagesLoadSignalCollectingDescription[];
+
 extern const char kOfflinePagesPrefetchingName[];
 extern const char kOfflinePagesPrefetchingDescription[];
 
diff --git a/components/offline_pages/core/offline_page_feature.cc b/components/offline_pages/core/offline_page_feature.cc
index 7bff4c6..c711c05 100644
--- a/components/offline_pages/core/offline_page_feature.cc
+++ b/components/offline_pages/core/offline_page_feature.cc
@@ -60,6 +60,9 @@
 const base::Feature kOfflinePagesLimitlessPrefetchingFeature{
     "OfflinePagesLimitlessPrefetching", base::FEATURE_DISABLED_BY_DEFAULT};
 
+const base::Feature kOfflinePagesDescriptiveFailStatusFeature{
+    "OfflinePagesDescriptiveFailStatus", base::FEATURE_DISABLED_BY_DEFAULT};
+
 const base::Feature kOfflinePagesDescriptivePendingStatusFeature{
     "OfflinePagesDescriptivePendingStatus", base::FEATURE_DISABLED_BY_DEFAULT};
 
@@ -129,6 +132,11 @@
   return base::FeatureList::IsEnabled(kOfflinePagesCTV2Feature);
 }
 
+bool IsOfflinePagesDescriptiveFailStatusEnabled() {
+  return base::FeatureList::IsEnabled(
+      kOfflinePagesDescriptiveFailStatusFeature);
+}
+
 bool IsOfflinePagesDescriptivePendingStatusEnabled() {
   return base::FeatureList::IsEnabled(
       kOfflinePagesDescriptivePendingStatusFeature);
diff --git a/components/offline_pages/core/offline_page_feature.h b/components/offline_pages/core/offline_page_feature.h
index 8c981e71..0373787c 100644
--- a/components/offline_pages/core/offline_page_feature.h
+++ b/components/offline_pages/core/offline_page_feature.h
@@ -25,6 +25,7 @@
 extern const base::Feature kOfflinePagesLimitlessPrefetchingFeature;
 extern const base::Feature kOfflinePagesDescriptivePendingStatusFeature;
 extern const base::Feature kOfflinePagesInDownloadHomeOpenInCctFeature;
+extern const base::Feature kOfflinePagesDescriptiveFailStatusFeature;
 
 // The parameter name used to find the experiment tag for prefetching offline
 // pages.
@@ -78,6 +79,10 @@
 // Returns true if we should record request origin as part of custom tabs V2.
 bool IsOfflinePagesCTV2Enabled();
 
+// Returns true if descriptive failed download status texts should be used in
+// notifications and Downloads Home.
+bool IsOfflinePagesDescriptiveFailStatusEnabled();
+
 // Returns true if descriptive pending download status texts should be used in
 // notifications and Downloads Home.
 bool IsOfflinePagesDescriptivePendingStatusEnabled();
diff --git a/components/offline_pages/core/offline_page_feature_unittest.cc b/components/offline_pages/core/offline_page_feature_unittest.cc
index 7ac2e7bc..be6c2af 100644
--- a/components/offline_pages/core/offline_page_feature_unittest.cc
+++ b/components/offline_pages/core/offline_page_feature_unittest.cc
@@ -124,6 +124,17 @@
   EXPECT_TRUE(offline_pages::ShouldOfflinePagesInDownloadHomeOpenInCct());
 }
 
+TEST(OfflinePageFeatureTest, OfflinePagesDescriptiveFailStatus) {
+  // Disabled by default.
+  EXPECT_FALSE(offline_pages::IsOfflinePagesDescriptiveFailStatusEnabled());
+
+  // Check if helper method works correctly when the features is enabled.
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(
+      kOfflinePagesDescriptiveFailStatusFeature);
+  EXPECT_TRUE(offline_pages::IsOfflinePagesDescriptiveFailStatusEnabled());
+}
+
 TEST(OfflinePageFeatureTest, OfflinePagesDescriptivePendingStatus) {
   // Disabled by default.
   EXPECT_FALSE(offline_pages::IsOfflinePagesDescriptivePendingStatusEnabled());
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index e82f17fc..6e759d3 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -26987,6 +26987,7 @@
   <int value="909439558" label="disable-device-discovery"/>
   <int value="916316159" label="disable-new-app-list-mixer"/>
   <int value="918046854" label="NtlmV2Enabled:disabled"/>
+  <int value="921536672" label="OfflinePagesDescriptiveFailStatus:enabled"/>
   <int value="926852901" label="DataReductionProxyMainMenu:disabled"/>
   <int value="929462705" label="disable-link-disambiguation-popup"/>
   <int value="935655516" label="password-import-export:disabled"/>
@@ -27014,6 +27015,7 @@
   <int value="988981463" label="ImageCaptureAPI:enabled"/>
   <int value="989062160" label="ModuleScriptsImportMetaUrl:enabled"/>
   <int value="996797157" label="V8ContextSnapshot:enabled"/>
+  <int value="1000587036" label="OfflinePagesDescriptiveFailStatus:disabled"/>
   <int value="1000706989" label="AutomaticTabDiscarding:disabled"/>
   <int value="1002585107" label="emphasize-titles-in-omnibox-dropdown"/>
   <int value="1003002105" label="MaterialDesignBookmarks:disabled"/>