chromium/ash/components/arc/net/arc_wifi_host_impl.cc

// 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 "ash/components/arc/net/arc_wifi_host_impl.h"

#include "ash/components/arc/arc_browser_context_keyed_service_factory_base.h"
#include "ash/components/arc/net/arc_net_utils.h"
#include "ash/components/arc/session/arc_bridge_service.h"
#include "base/memory/singleton.h"
#include "chromeos/ash/components/network/network_event_log.h"
#include "chromeos/ash/components/network/network_handler.h"
#include "chromeos/ash/components/network/network_state_handler.h"
#include "chromeos/ash/components/network/network_type_pattern.h"
#include "chromeos/ash/components/network/onc/network_onc_utils.h"
#include "chromeos/ash/components/network/technology_state_controller.h"

namespace {
constexpr int kGetScanResultsListLimit = 100;

ash::NetworkStateHandler* GetStateHandler() {
  return ash::NetworkHandler::Get()->network_state_handler();
}

ash::TechnologyStateController* GetTechnologyStateController() {
  return ash::NetworkHandler::Get()->technology_state_controller();
}

}  // namespace

namespace arc {
namespace {

// Singleton factory for ArcWifiHostImpl.
class ArcWifiHostImplFactory
    : public internal::ArcBrowserContextKeyedServiceFactoryBase<
          ArcWifiHostImpl,
          ArcWifiHostImplFactory> {
 public:
  // Factory name used by ArcBrowserContextKeyedServiceFactoryBase.
  static constexpr const char* kName = "ArcWifiHostImplFactory";

  static ArcWifiHostImplFactory* GetInstance() {
    return base::Singleton<ArcWifiHostImplFactory>::get();
  }

 private:
  friend base::DefaultSingletonTraits<ArcWifiHostImplFactory>;
  ArcWifiHostImplFactory() = default;
  ~ArcWifiHostImplFactory() override = default;
};

}  // namespace

// static
ArcWifiHostImpl* ArcWifiHostImpl::GetForBrowserContext(
    content::BrowserContext* context) {
  return ArcWifiHostImplFactory::GetForBrowserContext(context);
}

// static
ArcWifiHostImpl* ArcWifiHostImpl::GetForBrowserContextForTesting(
    content::BrowserContext* context) {
  return ArcWifiHostImplFactory::GetForBrowserContextForTesting(context);
}

ArcWifiHostImpl::ArcWifiHostImpl(content::BrowserContext* context,
                                 ArcBridgeService* bridge_service)
    : arc_bridge_service_(bridge_service) {
  arc_bridge_service_->arc_wifi()->SetHost(this);
  arc_bridge_service_->arc_wifi()->AddObserver(this);
}

ArcWifiHostImpl::~ArcWifiHostImpl() {
  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  arc_bridge_service_->arc_wifi()->RemoveObserver(this);
  arc_bridge_service_->arc_wifi()->SetHost(nullptr);
}

// static
void ArcWifiHostImpl::EnsureFactoryBuilt() {
  ArcWifiHostImplFactory::GetInstance();
}

void ArcWifiHostImpl::GetWifiEnabledState(
    GetWifiEnabledStateCallback callback) {
  bool is_enabled =
      GetStateHandler()->IsTechnologyEnabled(ash::NetworkTypePattern::WiFi());
  std::move(callback).Run(is_enabled);
}

void ArcWifiHostImpl::SetWifiEnabledState(
    bool is_enabled,
    SetWifiEnabledStateCallback callback) {
  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  auto state =
      GetStateHandler()->GetTechnologyState(ash::NetworkTypePattern::WiFi());
  // WiFi can't be enabled or disabled in these states.
  switch (state) {
    case ash::NetworkStateHandler::TECHNOLOGY_PROHIBITED:
    case ash::NetworkStateHandler::TECHNOLOGY_UNINITIALIZED:
    case ash::NetworkStateHandler::TECHNOLOGY_UNAVAILABLE:
      // If WiFi is in above state, it is already disabled. This is a noop.
      if (!is_enabled) {
        std::move(callback).Run(true);
        return;
      }
      NET_LOG(ERROR) << __func__ << ": failed due to WiFi state: " << state;
      std::move(callback).Run(false);
      return;
    default:
      break;
  }

  NET_LOG(USER) << __func__ << ": " << is_enabled;
  GetTechnologyStateController()->SetTechnologiesEnabled(
      ash::NetworkTypePattern::WiFi(), is_enabled,
      ash::network_handler::ErrorCallback());
  std::move(callback).Run(true);
}

void ArcWifiHostImpl::StartScan() {
  GetStateHandler()->RequestScan(ash::NetworkTypePattern::WiFi());
}

void ArcWifiHostImpl::ScanCompleted(const ash::DeviceState* /*unused*/) {
  auto* arc_wifi_instance = ARC_GET_INSTANCE_FOR_METHOD(
      arc_bridge_service_->arc_wifi(), ScanCompleted);
  if (!arc_wifi_instance) {
    return;
  }

  arc_wifi_instance->ScanCompleted();
}

void ArcWifiHostImpl::GetScanResults(GetScanResultsCallback callback) {
  ash::NetworkTypePattern network_pattern =
      ash::onc::NetworkTypePatternFromOncType(onc::network_type::kWiFi);

  ash::NetworkStateHandler::NetworkStateList network_states;
  GetStateHandler()->GetNetworkListByType(
      network_pattern, /*configured_only=*/false, /*visible_only=*/true,
      kGetScanResultsListLimit, &network_states);

  std::move(callback).Run(net_utils::TranslateScanResults(network_states));
}

void ArcWifiHostImpl::OnConnectionReady() {
  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);

  if (ash::NetworkHandler::IsInitialized()) {
    GetStateHandler()->AddObserver(this, FROM_HERE);
  }
}

void ArcWifiHostImpl::OnConnectionClosed() {
  GetStateHandler()->RemoveObserver(this, FROM_HERE);
}

void ArcWifiHostImpl::OnShuttingDown() {
  GetStateHandler()->RemoveObserver(this, FROM_HERE);
}

void ArcWifiHostImpl::DeviceListChanged() {
  auto* arc_wifi_instance = ARC_GET_INSTANCE_FOR_METHOD(
      arc_bridge_service_->arc_wifi(), WifiEnabledStateChanged);
  if (!arc_wifi_instance) {
    return;
  }

  bool is_enabled =
      GetStateHandler()->IsTechnologyEnabled(ash::NetworkTypePattern::WiFi());
  arc_wifi_instance->WifiEnabledStateChanged(is_enabled);
}

}  // namespace arc