chromium/android_webview/browser/ip_protection/aw_ip_protection_config_provider.h

// 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 ANDROID_WEBVIEW_BROWSER_IP_PROTECTION_AW_IP_PROTECTION_CONFIG_PROVIDER_H_
#define ANDROID_WEBVIEW_BROWSER_IP_PROTECTION_AW_IP_PROTECTION_CONFIG_PROVIDER_H_

#include <memory>
#include <optional>
#include <string>
#include <vector>

#include "android_webview/browser/aw_browser_context.h"
#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/task/sequenced_task_runner.h"
#include "base/threading/sequence_bound.h"
#include "base/time/time.h"
#include "components/ip_protection/android/ip_protection_token_ipc_fetcher.h"
#include "components/ip_protection/common/ip_protection_config_provider_helper.h"
#include "components/ip_protection/common/ip_protection_data_types.h"
#include "components/ip_protection/common/ip_protection_proxy_config_fetcher.h"
#include "components/ip_protection/common/ip_protection_proxy_config_retriever.h"
#include "components/ip_protection/common/ip_protection_telemetry.h"
#include "components/keyed_service/core/keyed_service.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/receiver_set.h"
#include "mojo/public/cpp/bindings/remote_set.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "services/network/public/mojom/network_context.mojom.h"
#include "third_party/abseil-cpp/absl/status/status.h"

namespace quiche {
class BlindSignAuthInterface;
enum class ProxyLayer;
struct BlindSignToken;
}  // namespace quiche

namespace android_webview {

// Fetches IP protection tokens and proxy list on demand for the network
// service.

// TODO(b/346997109): Refactor AwIpProtectionConfigProvider to reduce code
// duplication once a common implementation of IpProtectionConfigGetter is
// added.
class AwIpProtectionConfigProvider
    : public KeyedService,
      public network::mojom::IpProtectionConfigGetter {
 public:
  explicit AwIpProtectionConfigProvider(AwBrowserContext* aw_browser_context);

  ~AwIpProtectionConfigProvider() override;

  AwIpProtectionConfigProvider(const AwIpProtectionConfigProvider&) = delete;
  AwIpProtectionConfigProvider& operator=(const AwIpProtectionConfigProvider&) =
      delete;

  // IpProtectionConfigGetter:
  // Get a batch of blind-signed auth tokens.
  void TryGetAuthTokens(uint32_t batch_size,
                        network::mojom::IpProtectionProxyLayer proxy_layer,
                        TryGetAuthTokensCallback callback) override;
  // Get the list of IP Protection proxies.
  void GetProxyList(GetProxyListCallback callback) override;

  // KeyedService:

  // We do not currently support destroying WebView's browser context. No
  // shutdown code will be executed on termination of the browser process so
  // this is not actually being tested yet. However, we would like to support
  // destroying browser context in the future so this method contains an idea of
  // how this could be done. Note that Shutdown should not be called more than
  // once.
  void Shutdown() override;

  static AwIpProtectionConfigProvider* Get(
      AwBrowserContext* aw_browser_context);

  static bool CanIpProtectionBeEnabled();

  // Checks if IP Protection is disabled.
  bool IsIpProtectionEnabled();

  // Binds Mojo interfaces to be passed to a new network service.
  void AddNetworkService(
      mojo::PendingReceiver<network::mojom::IpProtectionConfigGetter>
          pending_receiver,
      mojo::PendingRemote<network::mojom::IpProtectionProxyDelegate>
          pending_remote);

  // Like `SetUp()`, but providing values for each of the member variables. Note
  // `bsa` is moved onto a separate sequence when initializing
  // `ip_protection_token_ipc_fetcher_`.
  void SetUpForTesting(
      std::unique_ptr<ip_protection::IpProtectionProxyConfigRetriever>
          ip_protection_proxy_config_retriever,
      std::unique_ptr<quiche::BlindSignAuthInterface> bsa);

 private:
  // Set up `ip_protection_token_ipc_fetcher_`
  // and`ip_protection_proxy_config_retriever_`, if not already initialized.
  void SetUp();

  // `FetchBlindSignedToken()` uses the `ip_protection_token_ipc_fetcher_`
  // to make an async call on the bound sequence into the
  // `quiche::BlindSignAuth` library to request a blind-signed auth token for
  // use at the IP Protection proxies.
  void FetchBlindSignedToken(int batch_size,
                             quiche::ProxyLayer quiche_proxy_layer,
                             TryGetAuthTokensCallback callback);
  void OnFetchBlindSignedTokenCompleted(
      base::TimeTicks bsa_get_tokens_start_time,
      TryGetAuthTokensCallback callback,
      absl::StatusOr<std::vector<quiche::BlindSignToken>> tokens);

  // Finish a call to `TryGetAuthTokens()` by recording the result and invoking
  // its callback.
  void TryGetAuthTokensComplete(
      std::optional<std::vector<ip_protection::BlindSignedAuthToken>>
          bsa_tokens,
      TryGetAuthTokensCallback callback,
      ip_protection::TryGetAuthTokensAndroidResult result,
      std::optional<base::TimeDelta> duration = std::nullopt);

  // Calculates the backoff time for the given result, based on
  // `last_try_get_auth_tokens_..` fields, and updates those fields.
  std::optional<base::TimeDelta> CalculateBackoff(
      ip_protection::TryGetAuthTokensAndroidResult result);

  // Injected browser context.
  raw_ptr<AwBrowserContext> aw_browser_context_;

  std::unique_ptr<ip_protection::IpProtectionProxyConfigFetcher>
      ip_protection_proxy_config_fetcher_;

  // The thread pool task runner on which async calls are made to
  // `ip_protection_token_ipc_fetcher` to fetch blind signed tokens. This
  // is needed to move some of the expensive token generation work off the UI
  // thread.
  scoped_refptr<base::SequencedTaskRunner> token_fetcher_task_runner_;

  // An IpProtectionTokenIpcFetcher instance that is bound to the given
  // sequenced `token_fetcher_task_runner_` on which all calls to the
  // `quiche::BlindSignAuth` library will happen on.
  base::SequenceBound<ip_protection::IpProtectionTokenIpcFetcher>
      ip_protection_token_ipc_fetcher_;

  // Whether `Shutdown()` has been called.
  bool is_shutting_down_ = false;

  // The result of the last call to `TryGetAuthTokens()`, and the
  // backoff applied to `try_again_after`. `last_try_get_auth_tokens_backoff_`
  // will be set to `base::TimeDelta::Max()` if no further attempts to get
  // tokens should be made. These will be updated by calls from any receiver.
  ip_protection::TryGetAuthTokensAndroidResult
      last_try_get_auth_tokens_result_ =
          ip_protection::TryGetAuthTokensAndroidResult::kSuccess;
  std::optional<base::TimeDelta> last_try_get_auth_tokens_backoff_;

  // The `mojo::Receiver` objects allowing the network service to call methods
  // on `this`.
  mojo::ReceiverSet<network::mojom::IpProtectionConfigGetter> receivers_;

  // Similar to `receivers_`, but containing remotes for all existing
  // IpProtectionProxyDelegates.
  mojo::RemoteSet<network::mojom::IpProtectionProxyDelegate> remotes_;

  // This must be the last member in this class.
  base::WeakPtrFactory<AwIpProtectionConfigProvider> weak_ptr_factory_{this};
};

}  // namespace android_webview

#endif  // ANDROID_WEBVIEW_BROWSER_IP_PROTECTION_AW_IP_PROTECTION_CONFIG_PROVIDER_H_