Add walrus provider to manta

Bug: b:370476808
Test: autoninja -C out_volteer/Release chrome
Change-Id: Iadc5c92a1724ead33c1f6716ec017c59c0061262
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5898137
Reviewed-by: Chris Mullins <[email protected]>
Reviewed-by: Tony Yeoman <[email protected]>
Commit-Queue: MD Nayeem Jahan Rafi <[email protected]>
Reviewed-by: Xinglong Luan <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1364724}
diff --git a/components/manta/BUILD.gn b/components/manta/BUILD.gn
index 1d09cc5f..131c008 100644
--- a/components/manta/BUILD.gn
+++ b/components/manta/BUILD.gn
@@ -56,6 +56,8 @@
       "sparky/sparky_util.h",
       "sparky/system_info_delegate.cc",
       "sparky/system_info_delegate.h",
+      "walrus_provider.cc",
+      "walrus_provider.h",
     ]
     deps += [ "//chromeos/constants:constants" ]
   }
@@ -75,6 +77,7 @@
       "snapper_provider_unittest.cc",
       "sparky/sparky_provider_unittest.cc",
       "sparky/sparky_util_unittest.cc",
+      "walrus_provider_unittest.cc",
     ]
   }
 
diff --git a/components/manta/features.cc b/components/manta/features.cc
index a2e2b92f..c54bcdf 100644
--- a/components/manta/features.cc
+++ b/components/manta/features.cc
@@ -25,6 +25,11 @@
              "MahiUseProdServer",
              base::FEATURE_ENABLED_BY_DEFAULT);
 
+// Enables Walrus Prod Server
+BASE_FEATURE(kWalrusUseProdServer,
+             "WalrusUseProdServer",
+             base::FEATURE_ENABLED_BY_DEFAULT);
+
 bool IsMantaServiceEnabled() {
   return base::FeatureList::IsEnabled(kMantaService);
 }
@@ -41,4 +46,8 @@
   return base::FeatureList::IsEnabled(kMahiUseProdServer);
 }
 
+bool IsWalrusUseProdServerEnabled() {
+  return base::FeatureList::IsEnabled(kWalrusUseProdServer);
+}
+
 }  // namespace manta::features
diff --git a/components/manta/features.h b/components/manta/features.h
index d104af8..701db835 100644
--- a/components/manta/features.h
+++ b/components/manta/features.h
@@ -18,6 +18,8 @@
 
 COMPONENT_EXPORT(MANTA) BASE_DECLARE_FEATURE(kSeaPenUseProdServer);
 
+COMPONENT_EXPORT(MANTA) BASE_DECLARE_FEATURE(kWalrusUseProdServer);
+
 COMPONENT_EXPORT(MANTA) bool IsMantaServiceEnabled();
 
 COMPONENT_EXPORT(MANTA) bool IsOrcaUseProdServerEnabled();
@@ -26,6 +28,8 @@
 
 COMPONENT_EXPORT(MANTA) bool IsMahiUseProdServerEnabled();
 
+COMPONENT_EXPORT(MANTA) bool IsWalrusUseProdServerEnabled();
+
 }  // namespace manta::features
 
 #endif  // COMPONENTS_MANTA_FEATURES_H_
diff --git a/components/manta/manta_service.cc b/components/manta/manta_service.cc
index 189f382c..e309afeb 100644
--- a/components/manta/manta_service.cc
+++ b/components/manta/manta_service.cc
@@ -25,6 +25,7 @@
 #include "components/manta/orca_provider.h"
 #include "components/manta/snapper_provider.h"
 #include "components/manta/sparky/sparky_provider.h"
+#include "components/manta/walrus_provider.h"
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
 namespace manta {
@@ -161,6 +162,16 @@
       std::move(sparky_delegate), std::move(system_info_delegate));
 }
 
+std::unique_ptr<WalrusProvider> MantaService::CreateWalrusProvider() {
+  if (!identity_manager_) {
+    return nullptr;
+  }
+  const ProviderParams provider_params = {/*use_api_key=*/is_demo_mode_,
+                                          chrome_version_, chrome_channel_};
+  return std::make_unique<WalrusProvider>(shared_url_loader_factory_,
+                                          identity_manager_, provider_params);
+}
+
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
 void MantaService::Shutdown() {
diff --git a/components/manta/manta_service.h b/components/manta/manta_service.h
index 1887c89..413e0ac1 100644
--- a/components/manta/manta_service.h
+++ b/components/manta/manta_service.h
@@ -37,6 +37,7 @@
 class OrcaProvider;
 class SnapperProvider;
 class SparkyProvider;
+class WalrusProvider;
 
 // The MantaService class is a KeyedService for the Chrome/ChromeOS Manta
 // project. It serves two main functions:
@@ -68,6 +69,7 @@
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   // Virtual for testing.
   virtual std::unique_ptr<MahiProvider> CreateMahiProvider();
+  virtual std::unique_ptr<WalrusProvider> CreateWalrusProvider();
 
   std::unique_ptr<OrcaProvider> CreateOrcaProvider();
   virtual std::unique_ptr<SnapperProvider> CreateSnapperProvider();
diff --git a/components/manta/manta_service_callbacks.cc b/components/manta/manta_service_callbacks.cc
index 7b2ac71..be7cf64 100644
--- a/components/manta/manta_service_callbacks.cc
+++ b/components/manta/manta_service_callbacks.cc
@@ -88,6 +88,10 @@
       base::UmaHistogramTimes("Ash.MantaService.AnchovyProvider.TimeCost",
                               time_cost);
       break;
+    case MantaMetricType::kWalrus:
+      base::UmaHistogramTimes("Ash.MantaService.WalrusProvider.TimeCost",
+                              time_cost);
+      break;
   }
 }
 
@@ -118,6 +122,10 @@
       base::UmaHistogramEnumeration(
           "Ash.MantaService.AnchovyProvider.StatusCode", status_code);
       break;
+    case MantaMetricType::kWalrus:
+      base::UmaHistogramEnumeration(
+          "Ash.MantaService.WalrusProvider.StatusCode", status_code);
+      break;
   }
 }
 }  // namespace
diff --git a/components/manta/manta_service_callbacks.h b/components/manta/manta_service_callbacks.h
index 4fb1419..64f2c68b3 100644
--- a/components/manta/manta_service_callbacks.h
+++ b/components/manta/manta_service_callbacks.h
@@ -28,6 +28,7 @@
   kMahiSummary,
   kMahiQA,
   kSparky,
+  kWalrus,
 };
 
 // Manta service uses this callback to return a Response proto parsed
diff --git a/components/manta/proto/manta.proto b/components/manta/proto/manta.proto
index 067302e..ad59963 100644
--- a/components/manta/proto/manta.proto
+++ b/components/manta/proto/manta.proto
@@ -22,6 +22,7 @@
   CHROMEOS_SPARKY = 314;
   CHROMEOS_LOBSTER = 315;
   CHROMEOS_SCANNER = 316;
+  CHROMEOS_WALRUS = 318;
   reserved 304 to 311;
 
   ACCESSIBILITY_IMAGE_DESCRIPTION = 700;
diff --git a/components/manta/walrus_provider.cc b/components/manta/walrus_provider.cc
new file mode 100644
index 0000000..1ea36c0
--- /dev/null
+++ b/components/manta/walrus_provider.cc
@@ -0,0 +1,106 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/manta/walrus_provider.h"
+
+#include "base/strings/stringprintf.h"
+#include "components/manta/features.h"
+
+namespace manta {
+
+namespace {
+
+constexpr char kOauthConsumerName[] = "manta_walrus";
+constexpr base::TimeDelta kTimeout = base::Seconds(30);
+
+void OnServerResponseOrErrorReceived(
+    MantaGenericCallback callback,
+    std::unique_ptr<proto::Response> manta_response,
+    MantaStatus manta_status) {
+  if (manta_response == nullptr || !manta_response->filtered_data_size()) {
+    // Return the status if the text/images are not blocked.
+    std::move(callback).Run(base::Value::Dict(), std::move(manta_status));
+    return;
+  }
+
+  CHECK(manta_response != nullptr);
+
+  // Add extra information for the invalid inputs.
+  auto output_data = base::Value::Dict();
+  for (const auto& filtered_data : manta_response->filtered_data()) {
+    auto filtered_reason = filtered_data.reason();
+    switch (filtered_reason) {
+      case manta::proto::FilteredReason::IMAGE_SAFETY:
+        output_data.Set("image_blocked", true);
+        break;
+      case manta::proto::FilteredReason::TEXT_SAFETY:
+        output_data.Set("text_blocked", true);
+        break;
+      default:
+        break;
+    }
+  }
+
+  std::move(callback).Run(std::move(output_data),
+                          {manta::MantaStatusCode::kBlockedOutputs});
+}
+
+}  // namespace
+
+WalrusProvider::WalrusProvider(
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+    signin::IdentityManager* identity_manager,
+    const ProviderParams& provider_params)
+    : BaseProvider(url_loader_factory, identity_manager, provider_params) {}
+
+WalrusProvider::WalrusProvider(
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+    signin::IdentityManager* identity_manager)
+    : BaseProvider(url_loader_factory, identity_manager) {}
+
+WalrusProvider::~WalrusProvider() = default;
+
+void WalrusProvider::Filter(std::string text_prompt,
+                            MantaGenericCallback done_callback) {
+  std::vector<std::vector<uint8_t>> empty_images;
+  Filter(text_prompt, empty_images, std::move(done_callback));
+}
+
+void WalrusProvider::Filter(const std::optional<std::string>& text_prompt,
+                            const std::vector<std::vector<uint8_t>>& images,
+                            MantaGenericCallback done_callback) {
+  proto::Request request;
+  request.set_feature_name(proto::FeatureName::CHROMEOS_WALRUS);
+
+  if (text_prompt.has_value() && !text_prompt->empty()) {
+    auto* input_data = request.add_input_data();
+    input_data->set_tag("input_text");
+    input_data->set_text(text_prompt.value());
+  }
+
+  for (auto& image : images) {
+    auto* input_data = request.add_input_data();
+    input_data->set_tag("input_image");
+    input_data->mutable_image()->set_serialized_bytes(
+        std::string(image.begin(), image.end()));
+  }
+
+  if (!request.input_data_size()) {
+    std::move(done_callback)
+        .Run(base::Value::Dict(), {MantaStatusCode::kInvalidInput});
+    return;
+  }
+
+  // TODO(b:370476808): MISSING_TRAFFIC_ANNOTATION should be resolved before
+  // launch.
+  RequestInternal(
+      GURL{GetProviderEndpoint(features::IsWalrusUseProdServerEnabled())},
+      kOauthConsumerName, MISSING_TRAFFIC_ANNOTATION, request,
+      MantaMetricType::kWalrus,
+      base::BindOnce(&OnServerResponseOrErrorReceived,
+                     std::move(done_callback)),
+      kTimeout);
+}
+
+}  // namespace manta
diff --git a/components/manta/walrus_provider.h b/components/manta/walrus_provider.h
new file mode 100644
index 0000000..aacf803
--- /dev/null
+++ b/components/manta/walrus_provider.h
@@ -0,0 +1,72 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MANTA_WALRUS_PROVIDER_H_
+#define COMPONENTS_MANTA_WALRUS_PROVIDER_H_
+
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/component_export.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/scoped_observation.h"
+#include "components/endpoint_fetcher/endpoint_fetcher.h"
+#include "components/manta/base_provider.h"
+#include "components/manta/manta_service_callbacks.h"
+#include "components/manta/provider_params.h"
+#include "components/signin/public/identity_manager/identity_manager.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "url/gurl.h"
+
+namespace manta {
+
+// The Walrus provider for the Manta project. Provides a method for clients to
+// call the relevant google API, handling OAuth and http fetching.
+// IMPORTANT: This class depends on `IdentityManager`.
+// `WalrusProvider::Filter` will return an empty response after
+// `IdentityManager` destruction.
+class COMPONENT_EXPORT(MANTA) WalrusProvider : virtual public BaseProvider {
+ public:
+  // Returns a `WalrusProvider` instance tied to the profile of the passed
+  // arguments.
+  WalrusProvider(
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+      signin::IdentityManager* identity_manager,
+      const ProviderParams& provider_params);
+
+  WalrusProvider(const WalrusProvider&) = delete;
+  WalrusProvider& operator=(const WalrusProvider&) = delete;
+
+  ~WalrusProvider() override;
+
+  // Filters the given `text_prompt` and `images` by calling the google service
+  // endpoint with the http POST request payload populated with the `input`. The
+  // fetched response is processed and returned to the caller via an
+  // `MantaGenericCallback` callback.
+  // Will give an empty response if `IdentityManager` is no longer valid.
+  virtual void Filter(const std::optional<std::string>& text_prompt,
+                      const std::vector<std::vector<uint8_t>>& images,
+                      MantaGenericCallback done_callback);
+
+  // Filters the given `text_prompt`.
+  virtual void Filter(const std::string text_prompt,
+                      MantaGenericCallback done_callback);
+
+ protected:
+  WalrusProvider(
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+      signin::IdentityManager* identity_manager);
+
+ private:
+  friend class FakeWalrusProvider;
+
+  base::WeakPtrFactory<WalrusProvider> weak_ptr_factory_{this};
+};
+
+}  // namespace manta
+
+#endif  // COMPONENTS_MANTA_WALRUS_PROVIDER_H_
diff --git a/components/manta/walrus_provider_unittest.cc b/components/manta/walrus_provider_unittest.cc
new file mode 100644
index 0000000..cc5e25b
--- /dev/null
+++ b/components/manta/walrus_provider_unittest.cc
@@ -0,0 +1,267 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/manta/walrus_provider.h"
+
+#include <memory>
+#include <string>
+
+#include "base/strings/stringprintf.h"
+#include "base/test/bind.h"
+#include "base/test/metrics/histogram_tester.h"
+#include "base/test/task_environment.h"
+#include "base/time/time.h"
+#include "components/manta/base_provider.h"
+#include "components/manta/base_provider_test_helper.h"
+#include "components/manta/manta_status.h"
+#include "components/manta/proto/manta.pb.h"
+#include "components/signin/public/base/consent_level.h"
+#include "components/signin/public/identity_manager/identity_manager.h"
+#include "components/signin/public/identity_manager/identity_test_environment.h"
+#include "net/base/net_errors.h"
+#include "net/http/http_status_code.h"
+#include "net/http/http_util.h"
+#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
+#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
+#include "services/network/test/test_url_loader_factory.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace manta {
+
+namespace {
+constexpr char kMockEndpoint[] = "https://my-endpoint.com";
+}
+
+class FakeWalrusProvider : public WalrusProvider, public FakeBaseProvider {
+ public:
+  FakeWalrusProvider(
+      scoped_refptr<network::SharedURLLoaderFactory> test_url_loader_factory,
+      signin::IdentityManager* identity_manager)
+      : BaseProvider(test_url_loader_factory, identity_manager),
+        WalrusProvider(test_url_loader_factory,
+                       identity_manager,
+                       ProviderParams()),
+        FakeBaseProvider(test_url_loader_factory, identity_manager) {}
+};
+
+class WalrusProviderTest : public BaseProviderTest {
+ public:
+  WalrusProviderTest() = default;
+
+  WalrusProviderTest(const WalrusProviderTest&) = delete;
+  WalrusProviderTest& operator=(const WalrusProviderTest&) = delete;
+
+  ~WalrusProviderTest() override = default;
+
+  std::unique_ptr<FakeWalrusProvider> CreateWalrusProvider() {
+    return std::make_unique<FakeWalrusProvider>(
+        base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+            &test_url_loader_factory_),
+        identity_test_env_->identity_manager());
+  }
+};
+
+// Test that responses with http_status_code != net::HTTP_OK are captured.
+TEST_F(WalrusProviderTest, CaptureUnexcpetedStatusCode) {
+  std::unique_ptr<FakeWalrusProvider> walrus_provider = CreateWalrusProvider();
+
+  SetEndpointMockResponse(GURL{kMockEndpoint}, /*response_data=*/"",
+                          net::HTTP_BAD_REQUEST, net::OK);
+  std::optional<std::string> text_prompt = "text pompt";
+  std::vector<std::vector<uint8_t>> images;
+
+  walrus_provider->Filter(
+      text_prompt, images,
+      base::BindLambdaForTesting(
+          [quit_closure = task_environment_.QuitClosure()](
+              base::Value::Dict response, MantaStatus manta_status) {
+            EXPECT_EQ(manta_status.status_code,
+                      MantaStatusCode::kBackendFailure);
+            quit_closure.Run();
+          }));
+  task_environment_.RunUntilQuit();
+}
+
+// Test that responses with network errors are captured.
+TEST_F(WalrusProviderTest, CaptureNetError) {
+  std::unique_ptr<FakeWalrusProvider> walrus_provider = CreateWalrusProvider();
+
+  SetEndpointMockResponse(GURL{kMockEndpoint}, /*response_data=*/"",
+                          net::HTTP_OK, net::ERR_FAILED);
+  std::optional<std::string> text_prompt = "text pompt";
+  std::vector<std::vector<uint8_t>> images;
+
+  walrus_provider->Filter(
+      text_prompt, images,
+      base::BindLambdaForTesting(
+          [quit_closure = task_environment_.QuitClosure()](
+              base::Value::Dict response, MantaStatus manta_status) {
+            EXPECT_EQ(manta_status.status_code,
+                      MantaStatusCode::kNoInternetConnection);
+            quit_closure.Run();
+          }));
+  task_environment_.RunUntilQuit();
+}
+
+// Test Manta Provider rejects invalid input data. Currently we require the
+// input must contain a valid text prompt or an image.
+TEST_F(WalrusProviderTest, InvalidInput) {
+  std::unique_ptr<FakeWalrusProvider> walrus_provider = CreateWalrusProvider();
+
+  SetEndpointMockResponse(GURL{kMockEndpoint}, /*response_data=*/"",
+                          net::HTTP_OK, net::OK);
+  std::optional<std::string> text_prompt;
+  std::vector<std::vector<uint8_t>> images;
+
+  walrus_provider->Filter(
+      text_prompt, images,
+      base::BindLambdaForTesting(
+          [quit_closure = task_environment_.QuitClosure()](
+              base::Value::Dict response, MantaStatus manta_status) {
+            EXPECT_EQ(manta_status.status_code, MantaStatusCode::kInvalidInput);
+            quit_closure.Run();
+          }));
+  task_environment_.RunUntilQuit();
+}
+
+// Test the response when the text prompt / image is safe.
+TEST_F(WalrusProviderTest, SuccessfulResponse) {
+  std::string image_bytes = "image_bytes";
+  base::HistogramTester histogram_tester;
+  manta::proto::Response response;
+  auto* output_data = response.add_output_data();
+  output_data->set_text("text pompt");
+  output_data = response.add_output_data();
+  output_data->mutable_image()->set_serialized_bytes(image_bytes);
+
+  std::string response_data;
+  response.SerializeToString(&response_data);
+
+  SetEndpointMockResponse(GURL{kMockEndpoint}, response_data, net::HTTP_OK,
+                          net::OK);
+  std::unique_ptr<FakeWalrusProvider> walrus_provider = CreateWalrusProvider();
+  auto quit_closure = task_environment_.QuitClosure();
+  std::optional<std::string> text_prompt = "text pompt";
+  std::vector<std::vector<uint8_t>> images = {
+      std::vector<uint8_t>(image_bytes.begin(), image_bytes.end())};
+
+  walrus_provider->Filter(
+      text_prompt, images,
+      base::BindLambdaForTesting([&quit_closure](base::Value::Dict response,
+                                                 MantaStatus manta_status) {
+        // Even though the response has text and image, walrus just
+        // returns the status code
+        ASSERT_EQ(MantaStatusCode::kOk, manta_status.status_code);
+        ASSERT_TRUE(response.empty());
+        quit_closure.Run();
+      }));
+  task_environment_.RunUntilQuit();
+
+  // Metric is logged when response is successfully parsed.
+  histogram_tester.ExpectTotalCount("Ash.MantaService.WalrusProvider.TimeCost",
+                                    1);
+}
+
+// Test the response when the text prompt is blocked.
+TEST_F(WalrusProviderTest, TextBlocked) {
+  std::string image_bytes = "image_bytes";
+  base::HistogramTester histogram_tester;
+  manta::proto::Response response;
+  manta::proto::FilteredData& filtered_data = *response.add_filtered_data();
+  filtered_data.set_reason(manta::proto::FilteredReason::TEXT_SAFETY);
+  std::string response_data;
+  response.SerializeToString(&response_data);
+
+  SetEndpointMockResponse(GURL{kMockEndpoint}, response_data, net::HTTP_OK,
+                          net::OK);
+  std::unique_ptr<FakeWalrusProvider> walrus_provider = CreateWalrusProvider();
+  auto quit_closure = task_environment_.QuitClosure();
+  std::optional<std::string> text_prompt = "text pompt";
+  std::vector<std::vector<uint8_t>> images = {
+      std::vector<uint8_t>(image_bytes.begin(), image_bytes.end())};
+
+  walrus_provider->Filter(
+      text_prompt, images,
+      base::BindLambdaForTesting([&quit_closure](base::Value::Dict response,
+                                                 MantaStatus manta_status) {
+        // Even though the response has text and image, walrus just
+        // returns the status code.
+        ASSERT_EQ(MantaStatusCode::kBlockedOutputs, manta_status.status_code);
+        ASSERT_EQ(response.size(), 1u);
+        ASSERT_TRUE(response.FindBool("text_blocked"));
+        quit_closure.Run();
+      }));
+  task_environment_.RunUntilQuit();
+
+  // Metric is logged when response is successfully parsed.
+  histogram_tester.ExpectTotalCount("Ash.MantaService.WalrusProvider.TimeCost",
+                                    1);
+}
+
+// Test the response when the text prompt and images is blocked.
+TEST_F(WalrusProviderTest, TextImageBothBlocked) {
+  std::string image_bytes = "image_bytes";
+  base::HistogramTester histogram_tester;
+  manta::proto::Response response;
+  auto* filtered_data = response.add_filtered_data();
+  filtered_data->set_reason(manta::proto::FilteredReason::TEXT_SAFETY);
+  filtered_data = response.add_filtered_data();
+  filtered_data->set_reason(manta::proto::FilteredReason::IMAGE_SAFETY);
+  filtered_data = response.add_filtered_data();
+  filtered_data->set_reason(manta::proto::FilteredReason::IMAGE_SAFETY);
+  std::string response_data;
+  response.SerializeToString(&response_data);
+
+  SetEndpointMockResponse(GURL{kMockEndpoint}, response_data, net::HTTP_OK,
+                          net::OK);
+  std::unique_ptr<FakeWalrusProvider> walrus_provider = CreateWalrusProvider();
+  auto quit_closure = task_environment_.QuitClosure();
+  std::optional<std::string> text_prompt = "text pompt";
+  std::vector<std::vector<uint8_t>> images = {
+      std::vector<uint8_t>(image_bytes.begin(), image_bytes.end()),
+      std::vector<uint8_t>(image_bytes.begin(), image_bytes.end())};
+
+  walrus_provider->Filter(
+      text_prompt, images,
+      base::BindLambdaForTesting([&quit_closure](base::Value::Dict response,
+                                                 MantaStatus manta_status) {
+        // Even though the response has text and image, walrus just
+        // returns the status code
+        ASSERT_EQ(MantaStatusCode::kBlockedOutputs, manta_status.status_code);
+        ASSERT_EQ(response.size(), 2u);
+        ASSERT_TRUE(response.FindBool("text_blocked"));
+        ASSERT_TRUE(response.FindBool("image_blocked"));
+        quit_closure.Run();
+      }));
+  task_environment_.RunUntilQuit();
+
+  // Metric is logged when response is successfully parsed.
+  histogram_tester.ExpectTotalCount("Ash.MantaService.WalrusProvider.TimeCost",
+                                    1);
+}
+
+TEST_F(WalrusProviderTest, EmptyResponseAfterIdentityManagerShutdown) {
+  base::HistogramTester histogram_tester;
+  std::unique_ptr<FakeWalrusProvider> wallrus_provider = CreateWalrusProvider();
+
+  identity_test_env_.reset();
+
+  std::string text_prompt = "text pompt";
+  wallrus_provider->Filter(
+      text_prompt, base::BindLambdaForTesting(
+                       [quit_closure = task_environment_.QuitClosure()](
+                           base::Value::Dict dict, MantaStatus manta_status) {
+                         ASSERT_TRUE(dict.empty());
+                         ASSERT_EQ(MantaStatusCode::kNoIdentityManager,
+                                   manta_status.status_code);
+                         quit_closure.Run();
+                       }));
+  task_environment_.RunUntilQuit();
+
+  // No metric logged.
+  histogram_tester.ExpectTotalCount("Ash.MantaService.WalrusProvider.TimeCost",
+                                    0);
+}
+
+}  // namespace manta