chromium/chrome/services/sharing/nearby/platform.cc

// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "third_party/nearby/src/internal/platform/implementation/platform.h"

#include "base/strings/string_number_conversions.h"
#include "base/task/thread_pool.h"
#include "chrome/services/sharing/nearby/nearby_connections.h"
#include "chrome/services/sharing/nearby/nearby_shared_remotes.h"
#include "chrome/services/sharing/nearby/platform/atomic_boolean.h"
#include "chrome/services/sharing/nearby/platform/atomic_uint32.h"
#include "chrome/services/sharing/nearby/platform/ble_medium.h"
#include "chrome/services/sharing/nearby/platform/ble_v2_medium.h"
#include "chrome/services/sharing/nearby/platform/bluetooth_adapter.h"
#include "chrome/services/sharing/nearby/platform/bluetooth_classic_medium.h"
#include "chrome/services/sharing/nearby/platform/condition_variable.h"
#include "chrome/services/sharing/nearby/platform/count_down_latch.h"
#include "chrome/services/sharing/nearby/platform/credential_storage.h"
#include "chrome/services/sharing/nearby/platform/input_file.h"
#include "chrome/services/sharing/nearby/platform/log_message.h"
#include "chrome/services/sharing/nearby/platform/mutex.h"
#include "chrome/services/sharing/nearby/platform/output_file.h"
#include "chrome/services/sharing/nearby/platform/recursive_mutex.h"
#include "chrome/services/sharing/nearby/platform/scheduled_executor.h"
#include "chrome/services/sharing/nearby/platform/submittable_executor.h"
#include "chrome/services/sharing/nearby/platform/webrtc.h"
#include "chrome/services/sharing/nearby/platform/wifi_direct_medium.h"
#include "chrome/services/sharing/nearby/platform/wifi_lan_medium.h"
#include "chromeos/ash/services/nearby/public/mojom/firewall_hole.mojom.h"
#include "chromeos/ash/services/nearby/public/mojom/mdns.mojom.h"
#include "chromeos/ash/services/nearby/public/mojom/tcp_socket_factory.mojom.h"
#include "chromeos/services/network_config/public/mojom/cros_network_config.mojom.h"
#include "components/cross_device/nearby/nearby_features.h"
#include "device/bluetooth/public/mojom/adapter.mojom.h"
#include "mojo/public/cpp/bindings/shared_remote.h"
#include "third_party/nearby/src/internal/platform/implementation/atomic_boolean.h"
#include "third_party/nearby/src/internal/platform/implementation/atomic_reference.h"
#include "third_party/nearby/src/internal/platform/implementation/ble.h"
#include "third_party/nearby/src/internal/platform/implementation/ble_v2.h"
#include "third_party/nearby/src/internal/platform/implementation/bluetooth_adapter.h"
#include "third_party/nearby/src/internal/platform/implementation/bluetooth_classic.h"
#include "third_party/nearby/src/internal/platform/implementation/condition_variable.h"
#include "third_party/nearby/src/internal/platform/implementation/count_down_latch.h"
#include "third_party/nearby/src/internal/platform/implementation/credential_storage.h"
#include "third_party/nearby/src/internal/platform/implementation/log_message.h"
#include "third_party/nearby/src/internal/platform/implementation/mutex.h"
#include "third_party/nearby/src/internal/platform/implementation/scheduled_executor.h"
#include "third_party/nearby/src/internal/platform/implementation/server_sync.h"
#include "third_party/nearby/src/internal/platform/implementation/shared/file.h"
#include "third_party/nearby/src/internal/platform/implementation/submittable_executor.h"
#include "third_party/nearby/src/internal/platform/implementation/webrtc.h"
#include "third_party/nearby/src/internal/platform/implementation/wifi.h"
#include "third_party/nearby/src/internal/platform/implementation/wifi_direct.h"
#include "third_party/nearby/src/internal/platform/implementation/wifi_hotspot.h"

namespace nearby::chrome {

// Minimal `WifiMedium` implementation required to allow WiFi Direct.
class WifiMedium : public api::WifiMedium {
 public:
  WifiMedium() {
    // The official WifiDirectMedium implementation queries the platform layer
    // to determine if the device supports WiFi Direct, so this capabilities
    // value is not actually used to determine support.
    capabilities_.support_wifi_direct = true;
  }

  // api::WifiMedium
  bool IsInterfaceValid() const override {
    // This call is required for the WifiDirectMedium to trigger properly.
    return true;
  }

  api::WifiCapability& GetCapability() override {
    NOTIMPLEMENTED();
    return capabilities_;
  }

  api::WifiInformation& GetInformation() override {
    NOTIMPLEMENTED();
    return information_;
  }

  bool Scan(const ScanResultCallback& scan_result_callback) override {
    NOTIMPLEMENTED();
    return false;
  }

  api::WifiConnectionStatus ConnectToNetwork(
      absl::string_view ssid,
      absl::string_view password,
      api::WifiAuthType auth_type) override {
    NOTIMPLEMENTED();
    return api::WifiConnectionStatus::kUnknown;
  }

  bool VerifyInternetConnectivity() override {
    NOTIMPLEMENTED();
    return false;
  }

  std::string GetIpAddress() override {
    NOTIMPLEMENTED();
    return std::string();
  }

 private:
  api::WifiCapability capabilities_;
  api::WifiInformation information_;
};

}  // namespace nearby::chrome

namespace nearby::api {

int GetCurrentTid() {
  // SubmittableExecutor and ScheduledExecutor does not own a thread pool
  // directly nor manages threads, thus cannot support this debug feature.
  return 0;
}

std::string ImplementationPlatform::GetCustomSavePath(
    const std::string& parent_folder,
    const std::string& file_name) {
  // This should return the <saved_custom_path>/file_name. For now we will
  // just return an empty string, since chrome doesn't call this yet.
  // TODO(b/223710122): Eventually chrome should implement this method.
  NOTIMPLEMENTED();
  return std::string();
}

std::string ImplementationPlatform::GetDownloadPath(
    const std::string& parent_folder,
    const std::string& file_name) {
  // This should return the <download_path>/parent_folder/file_name. For now we
  // will just return an empty string, since chrome doesn't call this yet.
  // TODO(b/223710122): Eventually chrome should implement this method.
  NOTIMPLEMENTED();
  return std::string();
}

OSName ImplementationPlatform::GetCurrentOS() {
  return OSName::kChromeOS;
}

std::unique_ptr<SubmittableExecutor>
ImplementationPlatform::CreateSingleThreadExecutor() {
  return std::make_unique<chrome::SubmittableExecutor>(
      base::ThreadPool::CreateSingleThreadTaskRunner(
          {base::MayBlock()},
          base::SingleThreadTaskRunnerThreadMode::DEDICATED));
}

std::unique_ptr<SubmittableExecutor>
ImplementationPlatform::CreateMultiThreadExecutor(int max_concurrency) {
  // We ignore |max_concurrency| and submit tasks to the main process thread
  // pool. Just before the task starts executing we enter a WILL_BLOCK scope
  // which signals to the thread pool to allocate a new thread if needed. This
  // gives the executor an effective thread count of whatever they need up to
  // the max thread pool size of 255.
  return std::make_unique<chrome::SubmittableExecutor>(
      base::ThreadPool::CreateTaskRunner({base::MayBlock()}));
}

std::unique_ptr<ScheduledExecutor>
ImplementationPlatform::CreateScheduledExecutor() {
  // TODO(crbug.com/40134072): Figure out if task runner needs to run in main
  // thread.
  return std::make_unique<chrome::ScheduledExecutor>(
      base::ThreadPool::CreateSequencedTaskRunner({base::MayBlock()}));
}

std::unique_ptr<AtomicUint32> ImplementationPlatform::CreateAtomicUint32(
    std::uint32_t initial_value) {
  return std::make_unique<chrome::AtomicUint32>(initial_value);
}

std::unique_ptr<BluetoothAdapter>
ImplementationPlatform::CreateBluetoothAdapter() {
  nearby::NearbySharedRemotes* nearby_shared_remotes =
      nearby::NearbySharedRemotes::GetInstance();
  if (nearby_shared_remotes &&
      nearby_shared_remotes->bluetooth_adapter.is_bound()) {
    return std::make_unique<chrome::BluetoothAdapter>(
        nearby_shared_remotes->bluetooth_adapter);
  }
  return nullptr;
}

std::unique_ptr<CountDownLatch> ImplementationPlatform::CreateCountDownLatch(
    std::int32_t count) {
  return std::make_unique<chrome::CountDownLatch>(count);
}

std::unique_ptr<AtomicBoolean> ImplementationPlatform::CreateAtomicBoolean(
    bool initial_value) {
  return std::make_unique<chrome::AtomicBoolean>(initial_value);
}

ABSL_DEPRECATED("This interface will be deleted in the near future.")
std::unique_ptr<InputFile> ImplementationPlatform::CreateInputFile(
    std::int64_t payload_id,
    std::int64_t total_size) {
  auto& connections = connections::NearbyConnections::GetInstance();
  return std::make_unique<chrome::InputFile>(
      connections.ExtractInputFile(payload_id));
}

std::unique_ptr<InputFile> ImplementationPlatform::CreateInputFile(
    const std::string& file_path,
    size_t size) {
  // This constructor is not called by Chrome. Returning nullptr, just in case.
  // TODO(b/223710122): Eventually chrome should implement and use this
  // constructor exclusively.
  NOTIMPLEMENTED();
  return nullptr;
}

ABSL_DEPRECATED("This interface will be deleted in the near future.")
std::unique_ptr<OutputFile> ImplementationPlatform::CreateOutputFile(
    std::int64_t payload_id) {
  auto& connections = connections::NearbyConnections::GetInstance();
  return std::make_unique<chrome::OutputFile>(
      connections.ExtractOutputFile(payload_id));
}

std::unique_ptr<OutputFile> ImplementationPlatform::CreateOutputFile(
    const std::string& file_path) {
  // This constructor is not called by Chrome. Returning nullptr, just in case.
  // TODO(b/223710122): Eventually chrome should implement and use this
  // constructor exclusively.
  NOTIMPLEMENTED();
  return nullptr;
}

std::unique_ptr<LogMessage> ImplementationPlatform::CreateLogMessage(
    const char* file,
    int line,
    LogMessage::Severity severity) {
  return std::make_unique<chrome::LogMessage>(file, line, severity);
}

std::unique_ptr<BluetoothClassicMedium>
ImplementationPlatform::CreateBluetoothClassicMedium(
    api::BluetoothAdapter& adapter) {
  nearby::NearbySharedRemotes* nearby_shared_remotes =
      nearby::NearbySharedRemotes::GetInstance();
  // Ignore the provided |adapter| argument; it is a reference to the object
  // created by ImplementationPlatform::CreateBluetoothAdapter(). Instead,
  // directly use the cached bluetooth::mojom::Adapter.
  if (nearby_shared_remotes &&
      nearby_shared_remotes->bluetooth_adapter.is_bound()) {
    return std::make_unique<chrome::BluetoothClassicMedium>(
        nearby_shared_remotes->bluetooth_adapter);
  }
  return nullptr;
}

std::unique_ptr<BleMedium> ImplementationPlatform::CreateBleMedium(
    api::BluetoothAdapter& adapter) {
  nearby::NearbySharedRemotes* nearby_shared_remotes =
      nearby::NearbySharedRemotes::GetInstance();
  // Ignore the provided |adapter| argument; it is a reference to the object
  // created by ImplementationPlatform::CreateBluetoothAdapter(). Instead,
  // directly use the cached bluetooth::mojom::Adapter.
  if (nearby_shared_remotes &&
      nearby_shared_remotes->bluetooth_adapter.is_bound()) {
    return std::make_unique<chrome::BleMedium>(
        nearby_shared_remotes->bluetooth_adapter);
  }
  return nullptr;
}

std::unique_ptr<ble_v2::BleMedium> ImplementationPlatform::CreateBleV2Medium(
    api::BluetoothAdapter& adapter) {
  nearby::NearbySharedRemotes* nearby_shared_remotes =
      nearby::NearbySharedRemotes::GetInstance();
  // Ignore the provided |adapter| argument; it is a reference to the object
  // created by ImplementationPlatform::CreateBluetoothAdapter(). Instead,
  // directly use the cached bluetooth::mojom::Adapter.
  if (nearby_shared_remotes &&
      nearby_shared_remotes->bluetooth_adapter.is_bound() &&
      features::IsNearbyBleV2Enabled()) {
    return std::make_unique<chrome::BleV2Medium>(
        nearby_shared_remotes->bluetooth_adapter);
  }
  return nullptr;
}

std::unique_ptr<api::CredentialStorage>
ImplementationPlatform::CreateCredentialStorage() {
  nearby::NearbySharedRemotes* nearby_shared_remotes =
      nearby::NearbySharedRemotes::GetInstance();
  if (nearby_shared_remotes &&
      nearby_shared_remotes->nearby_presence_credential_storage.is_bound()) {
    return std::make_unique<nearby::chrome::CredentialStorage>(
        nearby_shared_remotes->nearby_presence_credential_storage);
  }
  return nullptr;
}

std::unique_ptr<ServerSyncMedium>
ImplementationPlatform::CreateServerSyncMedium() {
  return nullptr;
}

std::unique_ptr<WifiMedium> ImplementationPlatform::CreateWifiMedium() {
  return std::make_unique<chrome::WifiMedium>();
}

std::unique_ptr<WifiDirectMedium>
ImplementationPlatform::CreateWifiDirectMedium() {
  nearby::NearbySharedRemotes* nearby_shared_remotes =
      nearby::NearbySharedRemotes::GetInstance();
  if (!nearby_shared_remotes) {
    return nullptr;
  }

  // TODO(b/340273442): This should always be bound when the WifiDirect feature
  // flag is enabled. Update logging to ERROR after launch.
  if (!nearby_shared_remotes->wifi_direct_manager.is_bound()) {
    VLOG(1) << "WifiDirectManager not bound. Returning null WifiDirect medium";
    return nullptr;
  }

  if (!nearby_shared_remotes->wifi_direct_firewall_hole_factory.is_bound()) {
    VLOG(1)
        << "FirewallHoleFactory not bound. Returning null WifiDirect medium";
    return nullptr;
  }

  return std::make_unique<chrome::WifiDirectMedium>(
      std::move(nearby_shared_remotes->wifi_direct_manager),
      std::move(nearby_shared_remotes->wifi_direct_firewall_hole_factory));
}

std::unique_ptr<WifiHotspotMedium>
ImplementationPlatform::CreateWifiHotspotMedium() {
  return nullptr;
}

std::unique_ptr<WifiLanMedium> ImplementationPlatform::CreateWifiLanMedium() {
  nearby::NearbySharedRemotes* nearby_shared_remotes =
      nearby::NearbySharedRemotes::GetInstance();
  if (!nearby_shared_remotes) {
    return nullptr;
  }

  // TODO(https://crbug.com/1261238): This should always be bound when the
  // WifiLan feature flag is enabled. Update logging to ERROR after launch.
  const mojo::SharedRemote<chromeos::network_config::mojom::CrosNetworkConfig>&
      cros_network_config = nearby_shared_remotes->cros_network_config;
  if (!cros_network_config.is_bound()) {
    VLOG(1) << "CrosNetworkConfig not bound. Returning null WifiLan medium";
    return nullptr;
  }

  // TODO(https://crbug.com/1261238): This should always be bound when the
  // WifiLan feature flag is enabled. Update logging to ERROR after launch.
  const mojo::SharedRemote<::sharing::mojom::FirewallHoleFactory>&
      firewall_hole_factory = nearby_shared_remotes->firewall_hole_factory;
  if (!firewall_hole_factory.is_bound()) {
    VLOG(1) << "FirewallHoleFactory not bound. Returning null WifiLan medium";
    return nullptr;
  }

  // TODO(https://crbug.com/1261238): This should always be bound when the
  // WifiLan feature flag is enabled. Update logging to ERROR after launch.
  const mojo::SharedRemote<::sharing::mojom::TcpSocketFactory>&
      tcp_socket_factory = nearby_shared_remotes->tcp_socket_factory;
  if (!tcp_socket_factory.is_bound()) {
    VLOG(1) << "TcpSocketFactory not bound. Returning null WifiLan medium";
    return nullptr;
  }

  const mojo::SharedRemote<::sharing::mojom::MdnsManager>& mdns_manager =
      nearby_shared_remotes->mdns_manager;
  if (features::IsNearbyMdnsEnabled() && !mdns_manager.is_bound()) {
    LOG(ERROR) << "MdnsManager not bound. Returning null WifiLan medium";
    return nullptr;
  }

  return std::make_unique<chrome::WifiLanMedium>(
      tcp_socket_factory, cros_network_config, firewall_hole_factory,
      mdns_manager);
}

std::unique_ptr<WebRtcMedium> ImplementationPlatform::CreateWebRtcMedium() {
  nearby::NearbySharedRemotes* nearby_shared_remotes =
      nearby::NearbySharedRemotes::GetInstance();

  if (!nearby_shared_remotes) {
    LOG(ERROR) << "No NearbySharedRemotes instance. Returning null medium.";
    return nullptr;
  }

  const mojo::SharedRemote<network::mojom::P2PSocketManager>& socket_manager =
      nearby_shared_remotes->socket_manager;
  const mojo::SharedRemote<::sharing::mojom::MdnsResponderFactory>&
      mdns_responder_factory = nearby_shared_remotes->mdns_responder_factory;
  const mojo::SharedRemote<::sharing::mojom::IceConfigFetcher>&
      ice_config_fetcher = nearby_shared_remotes->ice_config_fetcher;
  const mojo::SharedRemote<::sharing::mojom::WebRtcSignalingMessenger>&
      messenger = nearby_shared_remotes->webrtc_signaling_messenger;

  auto log_error = [](std::string dependency_name) {
    LOG(ERROR) << "Webrtc dependency [" << dependency_name
               << "] is not bound. Returning null medium.";
  };

  if (!socket_manager.is_bound()) {
    log_error("socket_manager");
    return nullptr;
  }

  if (!mdns_responder_factory.is_bound()) {
    log_error("mdns_responder_factory");
    return nullptr;
  }

  if (!ice_config_fetcher.is_bound()) {
    log_error("ice_config_fetcher");
    return nullptr;
  }

  if (!messenger.is_bound()) {
    log_error("messenger");
    return nullptr;
  }

  auto& connections = connections::NearbyConnections::GetInstance();

  return std::make_unique<chrome::WebRtcMedium>(
      socket_manager, mdns_responder_factory, ice_config_fetcher, messenger,
      connections.GetThreadTaskRunner());
}

std::unique_ptr<Mutex> ImplementationPlatform::CreateMutex(Mutex::Mode mode) {
  // Chrome does not support unchecked Mutex in debug mode, therefore
  // chrome::Mutex is used for both kRegular and kRegularNoCheck.
  if (mode == Mutex::Mode::kRecursive) {
    return std::make_unique<chrome::RecursiveMutex>();
  } else {
    return std::make_unique<chrome::Mutex>();
  }
}

std::unique_ptr<ConditionVariable>
ImplementationPlatform::CreateConditionVariable(Mutex* mutex) {
  return std::make_unique<chrome::ConditionVariable>(
      static_cast<chrome::Mutex*>(mutex));
}

}  // namespace nearby::api