chromium/chromeos/ash/services/wifi_direct/wifi_direct_manager.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 "chromeos/ash/services/wifi_direct/wifi_direct_manager.h"

#include "chromeos/ash/components/wifi_p2p/wifi_p2p_group.h"
#include "chromeos/ash/components/wifi_p2p/wifi_p2p_metrics_logger.h"
#include "chromeos/ash/services/wifi_direct/wifi_direct_connection.h"
#include "components/device_event_log/device_event_log.h"

namespace ash::wifi_direct {

using wifi_direct::mojom::WifiCredentialsPtr;
using wifi_direct::mojom::WifiDirectOperationResult;

namespace {

WifiDirectOperationResult GetMojoOperationResult(
    WifiP2PController::OperationResult result) {
  switch (result) {
    case WifiP2PController::OperationResult::kSuccess:
      return WifiDirectOperationResult::kSuccess;
    case WifiP2PController::OperationResult::kNotAllowed:
      return WifiDirectOperationResult::kNotAllowed;
    case WifiP2PController::OperationResult::kNotSupported:
      return WifiDirectOperationResult::kNotSupported;
    case WifiP2PController::OperationResult::kNotConnected:
      return WifiDirectOperationResult::kNotConnected;
    case WifiP2PController::OperationResult::kConcurrencyNotSupported:
      return WifiDirectOperationResult::kConcurrencyNotSupported;
    case WifiP2PController::OperationResult::kFrequencyNotSupported:
      return WifiDirectOperationResult::kFrequencyNotSupported;
    case WifiP2PController::OperationResult::kAuthFailure:
      return WifiDirectOperationResult::kAuthFailure;
    case WifiP2PController::OperationResult::kGroupNotFound:
      return WifiDirectOperationResult::kGroupNotFound;
    case WifiP2PController::OperationResult::kAlreadyConnected:
      return WifiDirectOperationResult::kAlreadyConnected;
    case WifiP2PController::OperationResult::kOperationInProgress:
      return WifiDirectOperationResult::kOperationInProgress;
    case WifiP2PController::OperationResult::kInvalidArguments:
      return WifiDirectOperationResult::kInvalidArguments;
    case WifiP2PController::OperationResult::kTimeout:
      return WifiDirectOperationResult::kTimeout;
    case WifiP2PController::OperationResult::kInvalidResultCode:
      return WifiDirectOperationResult::kInvalidResultCode;
    case WifiP2PController::OperationResult::kInvalidGroupProperties:
      return WifiDirectOperationResult::kInvalidGroupProperties;
    case WifiP2PController::OperationResult::kOperationFailed:
    case WifiP2PController::OperationResult::kDBusError:
      return WifiDirectOperationResult::kUnknownFailure;
  }
}

}  // namespace

WifiDirectManager::WifiDirectManager() {
  DCHECK(WifiP2PController::IsInitialized());
  WifiP2PController::Get()->AddObserver(this);
}

WifiDirectManager::~WifiDirectManager() {
  WifiP2PController::Get()->RemoveObserver(this);
}

void WifiDirectManager::BindPendingReceiver(
    mojo::PendingReceiver<mojom::WifiDirectManager> pending_receiver) {
  receivers_.Add(this, std::move(pending_receiver));
}

void WifiDirectManager::CreateWifiDirectGroup(
    WifiCredentialsPtr credentials,
    CreateWifiDirectGroupCallback callback) {
  WifiP2PController::Get()->CreateWifiP2PGroup(
      credentials ? std::optional{credentials->ssid} : std::nullopt,
      credentials ? std::optional{credentials->passphrase} : std::nullopt,
      base::BindOnce(&WifiDirectManager::OnCreateOrConnectWifiDirectGroup,
                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}

void WifiDirectManager::ConnectToWifiDirectGroup(
    WifiCredentialsPtr credentials,
    std::optional<uint32_t> frequency,
    ConnectToWifiDirectGroupCallback callback) {
  CHECK(credentials);
  WifiP2PController::Get()->ConnectToWifiP2PGroup(
      credentials->ssid, credentials->passphrase, frequency,
      base::BindOnce(&WifiDirectManager::OnCreateOrConnectWifiDirectGroup,
                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}

void WifiDirectManager::GetWifiP2PCapabilities(
    WifiDirectManager::GetWifiP2PCapabilitiesCallback callback) {
  auto result = mojom::WifiP2PCapabilities::New();

  result->is_owner_ready =
      WifiP2PController::Get()->GetP2PCapabilities().is_owner_ready;
  result->is_client_ready =
      WifiP2PController::Get()->GetP2PCapabilities().is_client_ready;
  result->is_p2p_supported =
      WifiP2PController::Get()->GetP2PCapabilities().is_p2p_supported;

  std::move(callback).Run(std::move(result));
}

void WifiDirectManager::OnWifiDirectConnectionDisconnected(const int shill_id,
                                                           bool is_owner) {
  const auto it = shill_id_to_wifi_direct_connection_.find(shill_id);
  if (it == shill_id_to_wifi_direct_connection_.end()) {
    NET_LOG(ERROR) << "Couldn't find the Wifi direct connection with shill id: "
                   << shill_id << " in map";
    return;
  }
  CHECK(shill_id_to_wifi_direct_connection_[shill_id]->IsOwner() == is_owner);
  WifiP2PMetricsLogger::RecordWifiP2PDisconnectReason(
      WifiP2PMetricsLogger::DisconnectReason::kInternalError, is_owner);

  shill_id_to_wifi_direct_connection_[shill_id].reset();
  shill_id_to_wifi_direct_connection_.erase(it);
}

void WifiDirectManager::OnCreateOrConnectWifiDirectGroup(
    CreateWifiDirectGroupCallback callback,
    WifiP2PController::OperationResult result,
    std::optional<WifiP2PGroup> group_metadata) {
  WifiDirectOperationResult mojo_result = GetMojoOperationResult(result);
  if (mojo_result != WifiDirectOperationResult::kSuccess) {
    std::move(callback).Run(mojo_result, mojo::NullRemote());
    return;
  }

  CHECK(group_metadata);
  const int shill_id = group_metadata->shill_id();
  NET_LOG(EVENT) << "Creating Wifi direct connection with Shill id: "
                 << shill_id;

  auto wifi_direct_connection_pair = WifiDirectConnection::Create(
      *group_metadata,
      base::BindOnce(&WifiDirectManager::OnClientRequestedDisconnection,
                     weak_ptr_factory_.GetWeakPtr(), shill_id));
  if (base::Contains(shill_id_to_wifi_direct_connection_, shill_id)) {
    NET_LOG(ERROR) << "Found an existing Wifi direct connection with Shill id: "
                   << shill_id;
  }

  shill_id_to_wifi_direct_connection_.insert_or_assign(
      shill_id, std::move(wifi_direct_connection_pair.first));
  std::move(callback).Run(WifiDirectOperationResult::kSuccess,
                          std::move(wifi_direct_connection_pair.second));
}

void WifiDirectManager::OnDestroyOrDisconnectWifiDirectGroup(
    WifiP2PController::OperationResult result) {
  WifiDirectOperationResult mojo_result = GetMojoOperationResult(result);

  if (mojo_result != WifiDirectOperationResult::kSuccess) {
    NET_LOG(ERROR) << "Disconnect or Destroy operation failed with the code: "
                   << mojo_result;
    return;
  }

  NET_LOG(EVENT)
      << "Successfully disconnected or destroyed the wifi direct group";
}

void WifiDirectManager::OnClientRequestedDisconnection(int shill_id) {
  NET_LOG(EVENT)
      << "Request diconnection for the Wifi direct group with Shill id: "
      << shill_id;
  const auto it = shill_id_to_wifi_direct_connection_.find(shill_id);
  if (it == shill_id_to_wifi_direct_connection_.end()) {
    NET_LOG(ERROR) << "Couldn't find the Wifi direct connection with shill id: "
                   << shill_id << " in map";
    return;
  }
  const bool is_owner =
      shill_id_to_wifi_direct_connection_[shill_id]->IsOwner();
  if (is_owner) {
    WifiP2PController::Get()->DestroyWifiP2PGroup(
        shill_id,
        base::BindOnce(&WifiDirectManager::OnDestroyOrDisconnectWifiDirectGroup,
                       weak_ptr_factory_.GetWeakPtr()));
  } else {
    WifiP2PController::Get()->DisconnectFromWifiP2PGroup(
        shill_id,
        base::BindOnce(&WifiDirectManager::OnDestroyOrDisconnectWifiDirectGroup,
                       weak_ptr_factory_.GetWeakPtr()));
  }
  WifiP2PMetricsLogger::RecordWifiP2PDisconnectReason(
      WifiP2PMetricsLogger::DisconnectReason::kClientInitiated, is_owner);
  shill_id_to_wifi_direct_connection_.erase(it);
}

size_t WifiDirectManager::GetConnectionsCountForTesting() const {
  return shill_id_to_wifi_direct_connection_.size();
}

void WifiDirectManager::FlushForTesting() {
  receivers_.FlushForTesting();  // IN-TEST
  for (const auto& shill_id_connection_pair :
       shill_id_to_wifi_direct_connection_) {
    shill_id_connection_pair.second->FlushForTesting();  // IN-TEST
  }
}

}  // namespace ash::wifi_direct